<?php

class WhiskyExaminationDB {
    /*** プロパティ ***/
    private $aPHPDataObject;
    
    /*** コンストラクタ ***/
    function __construct () {
      $aDatabaseServerName = 'mysql:dbname=whiskyexamination;host=localhost';
      $userName = 'mysql';
      $passWord = 'mysql';
      
      try{
          $aPHPDataObject = new PDO($aDatabaseServerName, $userName, $passWord);
      }catch (PDOException $anException){
          print('Error:' . $anException -> getMessage());
          die();
      }

      $this -> setAPHPDataObject($aPHPDataObject);
    }

    /*** getter/setter ***/
    private function getAPHPDataObject(){
        return $this -> aPHPDataObject;
    }
    private function setAPHPDataObject($aPHPDataObject){
        $this -> aPHPDataObject = $aPHPDataObject;
    }

    /*** アクセッサメソッド群 ***/
    # 現在の総問題数を応答する。
    public function getNumberOfQuestion() {
        $anIdStringArray = $this -> getIdArray();
        $anInteger = count($anIdStringArray);
        return $anInteger;
    }

    # 問題を1つランダムに取り出し、応答する。
    public function getQuestion() {
        $aShuffledIdArray = $this -> getShuffledIdArray();
        $anIdString = array_shift($aShuffledIdArray);
        $anIntegerOfId = (integer)$anIdString;

        $aQuestion = $this -> getQuestionById($anIntegerOfId);
        
        return $aQuestion;
    }

    # 指定した識別子の問題を取り出し、応答する。
    public function getQuestionById($anIntegerOfId) {
        $anIdString = (String)$anIntegerOfId;
        $aSQLString = 'select * from Question where id = ' . $anIdString . ';';
        $aPDOStatement = $this -> executeQuery($aSQLString);
        $aQuestion = $this -> asAQuestion($aPDOStatement);

        return $aQuestion;
    }

    # 指定した個数の問題をランダムに取り出し、配列形式で応答する。
    # ※問題は重複しないものとする。
    # 　ただし、引数に指定された問題数が総問題数を上回る場合は問題の重複を許し、
    # 　指定数分の問題を応答する。
    public function getQuestions($aNumberOfQuestions) {
        $aString = gettype ($aNumberOfQuestions);
        // 引数がInteger型でない場合、Integerにキャスト
        if ($aString != 'integer') {
            $aNumber = (integer) $aNumberOfQuestions;
        } else {
            $aNumber = $aNumberOfQuestions;
        }

        $returnQuestionArray = array();
        $aNumberOfExistsQuestions = $this -> getNumberOfQuestion();

        $shuffledIDArray = array();

        if ($aNumber > $aNumberOfExistsQuestions) {
            // 引数に指定された問題数が総問題数を上回る場合
            // 引数／総問題数で商と余りを求める
            $aQuotientFloat = $aNumber / $aNumberOfExistsQuestions;
            $aQuotient = (integer)$aQuotientFloat;
            $aSurplus = $aNumber % $aNumberOfExistsQuestions;
            // 商の数だけループし、ランダム配列を結合
            for ($index = 0 ; $index < $aQuotient ; $index++) {
                $tmpShuffledIDArray = $this -> getShuffledIDArray();
                foreach ($tmpShuffledIDArray as $anElement) {
                    array_push($shuffledIDArray, $anElement);
                }
            }
            // 再度ランダム配列を取出し、
            $tmpShuffledIDArray = $this -> getShuffledIDArray();
            $tmpArray = array();
            // 余りの数だけループし、先頭から配列にプッシュ
            for ($index = 0 ; $index < $aSurplus ; $index++) {
                array_push($shuffledIDArray, $tmpShuffledIDArray[$index]);
            }
        } else {
            $shuffledIDArray = $this -> getShuffledIDArray();
        }
        var_dump($shuffledIDArray);

        for ($index = 0 ; $index < $aNumber ; $index++) {
            $anIntegerOfId = (integer)$shuffledIDArray[$index];
            $aQuestion = $this -> getQuestionById($anIntegerOfId);
            array_push($returnQuestionArray, $aQuestion);
        }

        return $returnQuestionArray;
    }

    # 指定した問題を登録する。
    # ※idが既存の場合は、当該idの問題に対して問題文/正解群/不正解群を上書きし、当該idを応答する。
    #　　idが既存でない場合は、当該idを無視して新規登録を行ない、新規登録時のidを応答する。
    public function setQuestion($aQuestion) {
        $anIdNumber = $aQuestion -> getId();
        $anIdNumberString = (string)$anIdNumber;
        $aQuestionString = $aQuestion -> getQuestion();
        $aCorrectAnswersArray = $aQuestion -> getCorrectAnswers();
        $anIncorrectAnswersArray = $aQuestion -> getIncorrectAnswers();
        if (mb_strlen($aQuestionString) == 0) {
            return null;
        }
        if (count($aCorrectAnswersArray) == 0) {
            return null;
        }
        if (count($anIncorrectAnswersArray) == 0) {
            return null;
        }
        if (mb_strlen($anIdNumberString) == 0) {
            $anIdNumber = $this -> insertIntoQuestion($aQuestion);
        } else {
            if ($this -> isExistingId($anIdNumberString)) {
                $anIdNumber = $this -> updateQuestion($aQuestion);
            } else {
                $anIdNumber = $this -> insertIntoQuestion($aQuestion);
            }            
        }

        return $anIdNumber;
    }

    /*** サービスメソッド群 ***/
    private function executeQuery($aSQLString) {
        $aPHPDataObject = $this -> aPHPDataObject;
        
        try{
            $aPDOStatement = $aPHPDataObject -> query($aSQLString);
        }catch (PDOException $anException){
            print('Error:' . $anException -> getMessage());
            die();
        }

        return $aPDOStatement;
    }

    // QuestionテーブルからIDのみselectし配列に格納して応答する。
    private function getIdArray() {
        $aSQLString = 'select id from Question;';
        $aPDOStatement = $this -> executeQuery($aSQLString);
        $returnArray = array();
        foreach ($aPDOStatement as $aRow) {
            array_push($returnArray, $aRow['id']);
        }
        return $returnArray;
    }

    // 抽出したIDの配列をシャッフルして応答する。
    private function getShuffledIdArray() {
        $anArray = $this -> getIdArray();
        shuffle($anArray);
        return $anArray;
    }

    // SQL実行結果オブジェクト（PDOStatementのインスタンス）を引き、
    // それぞれのカラムに値が有ればvar_dump()で出力する。
    private function showAtVarDump($aPDOStatement) {
        foreach ($aPDOStatement as $aRow) {
            if(array_key_exists('id', $aRow)){
                print($aRow['id'].',');
            }
            if(array_key_exists('question', $aRow)){
                print($aRow['question'].',');
            }
            if(array_key_exists('correct', $aRow)){
                print($aRow['correct'].',');
            }
            if(array_key_exists('incorrect', $aRow)){
                print($aRow['incorrect']);
            }
            print('<br />');
        }
    }

    // SQL実行結果オブジェクト（PDOStatementのインスタンス）を引き、
    // それぞれのカラムに値が有ればQuestionクラスのインスタンスを作り応答する。
    // 一つでもない要素が有ればnullを応答する。
    private function asAQuestion($aPDOStatement) {
        foreach ($aPDOStatement as $aRow) {
            if(array_key_exists('id', $aRow)){
                $anId = $aRow['id'];
            } else {
                return null;
            }
            if(array_key_exists('question', $aRow)){
                $aQuestion = $aRow['question'];
            } else {
                return null;
            }
            if(array_key_exists('correct', $aRow)){
                $aCorrectAnswers = $aRow['correct'];
            } else {
                return null;
            }
            if(array_key_exists('incorrect', $aRow)){
                $aIncorrectAnswers = $aRow['incorrect'];
            } else {
                return null;
            }
        }

        $aQuestion = new Question($anId, $aQuestion, $aCorrectAnswers, $aIncorrectAnswers);
        return $aQuestion;
    }

    // 新規登録用メソッド
    // setQuestionから呼び出される
    private function insertIntoQuestion($aQuestion) {
        $aQuestionString = $aQuestion -> getQuestion();
        $aQuestionString = $this -> surroundSingleQuote($aQuestionString);
        $aCorrectAnswersInJSON = $aQuestion -> getCorrectAnswersInJSON();
        $aCorrectAnswersInJSON = $this -> surroundSingleQuote($aCorrectAnswersInJSON);
        $anIncorrectAnswersInJSON = $aQuestion -> getIncorrectAnswersInJSON();
        $anIncorrectAnswersInJSON = $this -> surroundSingleQuote($anIncorrectAnswersInJSON);
        $aSQLString = 'insert into question (question,correct,incorrect) values(' . $aQuestionString . ',' . $aCorrectAnswersInJSON . ',' . $anIncorrectAnswersInJSON . ');';

        $aPDOStatement = $this -> executeQuery($aSQLString);
        $anIdNumberString = $this -> aPHPDataObject -> lastInsertId();
        $anIdNumber = (integer)$anIdNumberString;

        return $anIdNumber;
    }

    // 更新用メソッド
    // setQuestionから呼び出される
    private function updateQuestion($aQuestion) {
        $anIdNumber = $aQuestion -> getId();
        $anIdString = (string)$anIdNumber;
        $anIdString = $this -> surroundSingleQuote($anIdString);
        $aQuestionString = $aQuestion -> getQuestion();
        $aQuestionString = $this -> surroundSingleQuote($aQuestionString);
        $aCorrectAnswersInJSON = $aQuestion -> getCorrectAnswersInJSON();
        $aCorrectAnswersInJSON = $this -> surroundSingleQuote($aCorrectAnswersInJSON);
        $anIncorrectAnswersInJSON = $aQuestion -> getIncorrectAnswersInJSON();
        $anIncorrectAnswersInJSON = $this -> surroundSingleQuote($anIncorrectAnswersInJSON);
        $aSQLString = 'update question set question = ' . $aQuestionString . ' , correct = ' . $aCorrectAnswersInJSON . ' , incorrect = ' . $anIncorrectAnswersInJSON . ' where id = ' . $anIdString . ';';

        $aPDOStatement = $this -> executeQuery($aSQLString);

        return $anIdNumber;
    }

    // 引数の文字列を'で囲んで応答する。
    private function surroundSingleQuote($aString) {
        $aSingleQuoteString = '\'';
        $returnString = $aSingleQuoteString . $aString . $aSingleQuoteString;
        return $returnString;
    }

    private function isExistingId($anIdNumberString) {
        $returnBoolean = false;
        $anIdStringArray = $this -> getIdArray();
        foreach ($anIdStringArray as $existingIdString) {
            if ($existingIdString == $anIdNumberString) {
                $returnBoolean = true;    
            }
        }
        return $returnBoolean;
    }

    public function example() {
        //$anInteger = $this -> getNumberOfQuestion();
        //var_dump($anInteger);

        //$aQuestion = $this -> getQuestion();
        //var_dump($aQuestion);

        //$anArray = $this -> getShuffledIDArray();
        //var_dump($anArray);

        $returnQuestionArray = $this -> getQuestions(55);
        var_dump(count($returnQuestionArray));

        //$aFloat = floatval(1.1);
        //var_dump($aFloat);
        //$anInt = (int)$aFloat;
        //var_dump($anInt);

        //$anId = '999';
        //$aQuestion = 'fromPHP';
        //$aCorrectAnswers = '["fromPHP"]';
        //$aIncorrectAnswers = '["fromPHP","fromPHP","fromPHP"]';
        //$aQuestion = new Question($anId, $aQuestion, $aCorrectAnswers, $aIncorrectAnswers);
        //$aNumber = $this -> setQuestion($aQuestion);
        //var_dump($aNumber);

        //$anId = '28';
        //$aQuestion = 'updateTest';
        //$aCorrectAnswers = '["fromPHP","fromPHP","fromPHP"]';
        //$aIncorrectAnswers = '["fromPHP"]';
        //$aQuestion = new Question($anId, $aQuestion, $aCorrectAnswers, $aIncorrectAnswers);
        //$aNumber = $this -> updateQuestion($aQuestion);
        //var_dump($aNumber);
    }
}

?>