<?php
/**
 * 手作りカレンダークラス
 */
class calendar{
    // 本日の情報
    public $today = array();
    // 表示するカレンダーの情報
    public $disp = array();
    // 日曜始まりデフォルト
    public $startWeek = 0;
    /**
     * インスタンス化されたときに実行される
     */
    public function __constract(){
    }
    /**
     * メンバ変数を初期化する
     */
    public function init(){
        self::setStartWeek();
        self::setToday();
        self::setDisp();
    }
    /**
     * HTMLに出力する処理
     */
    public function echoAll(){
        $echos = array();
        $echos[] = self::getTable();
        $echos[] = self::getTitleTr();
        $echos[] = self::getBeforeTrTd();
        for($day=1;$day<32;$day++){
            $d = str_pad($day, 2, '0', STR_PAD_LEFT);
            if(checkdate($this->disp['m'], $d, $this->disp['Y']) === false){
                break;
            }
            $ts = strtotime($this->disp['Y'].$this->disp['m'].$d);
            $week = self::getWeek($ts);
            $echos[] = self::getTr($week);
            $echos[] = self::getTd($week);
            $echos[] = $day;
            $echos[] = self::getTdClose();
            $echos[] = self::getTrClose($week);
        }
        $echos[] = self::getAfterTrTd($ts);
        $echos[] = self::getTableClose();
        return implode('',$echos);
    }
    public function setStartWeek(){
        if (isset($_REQUEST['sw']) && in_array($_REQUEST['sw'],array('1','0'))) {
            // 0 : 日曜始まり, 1 : 月曜始まり
            $this->startWeek = (int) $_REQUEST['sw'];
        }
    }
    /**
     * HTMLに出力する年、月を決定している
     */
    public function setDisp(){
        if (isset($_REQUEST['ym']) && strlen($_REQUEST['ym']) === 6 && is_numeric($_REQUEST['ym'])) {
            $this->disp['Y'] = substr($_REQUEST['ym'], 0,4);
            $this->disp['m'] = substr($_REQUEST['ym'], 4,2);
            $this->disp['n'] = $this->disp['m']+0;
        } else if (isset($this->today['Y'],$this->today['m'])) {
            $this->disp['Y'] = $this->today['Y'];
            $this->disp['m'] = $this->today['m'];
            $this->disp['n'] = $this->disp['m']+0;
        } else {
            $this->disp['Y'] = date('Y');
            $this->disp['m'] = date('m');
            $this->disp['n'] = date('n');
        }
        $this->disp['ts'] = strtotime($this->disp['Y'].$this->disp['m'].$this->today['d']);
    }
    public function setToday(){
        $this->today['Y'] = date('Y');
        $this->today['m'] = date('m');
        $this->today['d'] = date('d');
        $this->today['n'] = date('n');
        $this->today['j'] = date('j');
        $this->today['ts'] = strtotime($this->today['Y'].$this->today['m'].$this->today['d']);
    }
    function getWeek($ts){
        // 0 (日曜)から 6 (土曜)
        return (int) date('w',$ts);
    }
    function getTable(){
        return '<table class="table table-sm table-bordered table-calendar">';
    }
    function getTableClose(){
        return '</table>';
    }
    function getTitleTr(){
        $weeks = array('月', '火', '水', '木', '金', '土', '日');
        if ($this->startWeek === 0) {
            $weeks = array('日', '月', '火', '水', '木', '金', '土');
        }
        $return = '<tr class="calendar-title">';
        foreach ($weeks as $i => $week){
            $return .= self::getTd(($i+$this->startWeek === 7 ? 0 : $i+$this->startWeek));
            $return .= $week;
            $return .= self::getTdClose();
        }
        $return .= '</tr>';
        return $return;
    }
    function getBeforeTrTd(){
        $firstWeek = self::getWeek(strtotime($this->disp['Y'].$this->disp['m'].'01'));
        if ($firstWeek === $this->startWeek) {
            // 1日が左端からピッタリ埋まっていれば何もしない
            return '';
        }
        if ($this->startWeek === 0) {
            // 日曜始まり
            // あと何個 td が必要か
            $endI = $firstWeek;
        } else {
            // 月曜始まり
            // あと何個 td が必要か
            $endI = $firstWeek - 1;
            if ($firstWeek === 0) {
                $endI = 6;
            }
        }
        $return = '<tr>';
        // その月の1日がくるまでtdを埋める
        for($i=0;$i<$endI;$i++){
            $return .= self::getTd($i+$this->startWeek);
            $return .= ' ';
            $return .= self::getTdClose();
        }
        return $return;
    }
    function getAfterTrTd($lastTs){
        $lastWeek = self::getWeek($lastTs);
        if ($this->startWeek === 0) {
            // 日曜始まり
            if ($lastWeek === 6) {
                // 末日が右端までピッタリ埋まっていれば何もしない
                return '';
            }
            // あと何個 td が必要か
            $endI = 6 - $lastWeek;
        } else {
            // 月曜始まり
            if ($lastWeek === 0) {
                // 末日が右端までピッタリ埋まっていれば何もしない
                return '';
            }
            // あと何個 td が必要か
            $endI = 7 - $lastWeek;
        }
        $return = '';
        // その月の末日がくるまでtdを埋める
        for($i=1;$i<=$endI && $i<7;$i++){
            $return .= self::getTd($i+$lastWeek === 7 ? 0 : $i+$lastWeek);
            $return .= ' ';
            $return .= self::getTdClose();
        }
        $return .= '</tr>';
        return $return;
    }
    function getTd($week){
        if ($week === 6) {
            return '<td class="sat">';
        }
        if ($week === 0) {
            return '<td class="sun">';
        }
        return '<td>';
    }
    function getTdClose(){
        return '</td>';
    }
    function getTr($week){
        if ($week === $this->startWeek) {
            return '<tr>';
        }
        return '';
    }
    function getTrClose($week){
        if ($this->startWeek === 0) {
            // 日曜始まり
            if ($week === 6) {
                return '</tr>';
            }
        } else {
            // 月曜始まり
            if ($week === 0) {
                return '</tr>';
            }
        }
        return '';
    }
    /**
     * リンクを作成する。
     */
    function getNextLink(){
        // 次のページで現在表示されている年月の翌月を表示するためのリンク先のURLパラメータ
        return 'ym='.date('Ym',strtotime($this->disp['Y'].$this->disp['m'].'01 + 1month'));
    }
    /**
     * リンクを作成する
     */
    function getPreLink(){
        // 次のページで現在表示されている年月の前月を表示するためのリンク先のURLパラメータ
        return 'ym='.date('Ym',strtotime($this->disp['Y'].$this->disp['m'].'01 - 1month'));
    }
}
$c = new calendar();
$c->init();
?>
<div class="row text-center mb-3">
    <div class="col-4">
        <a href="?sw=<?php echo $c->startWeek;?>&<?php echo $c->getPreLink();?>" class="btn btn-sm btn-outline-primary">前の月</a>
    </div>
    <div class="col-4 year-month">
        <?php echo $c->disp['Y'];?>年<?php echo $c->disp['n'];?>月
    </div>
    <div class="col-4">
        <a href="?sw=<?php echo $c->startWeek;?>&<?php echo $c->getNextLink();?>" class="btn btn-sm btn-outline-primary">次の月</a>
    </div>
</div>
<div><?php echo $c->echoAll();?></div>
<div class="row">
    <div class="col-12 text-right">
        <a href="?sw=<?php echo $c->startWeek === 1 ? '0' : '1';?>&<?php echo $c->getNextLink();?>" class="btn btn-sm btn-outline-secondary"><?php echo $c->startWeek === 1 ? '日曜始まり表示' : '月曜始まり表示切り替え' ?></a>
    </div>
</div>