<?php

declare(strict_types=1);

namespace Anon\Quiz;

class QuestionException extends \Exception {}

abstract class Question
{
    protected $text;
    protected $correctAnswer;
    
    public function __construct(string $text, $correctAnswer)
    {
        
        $this->text = $text;
        $this->correctAnswer = $correctAnswer;
        
    }
    
    public function getQuestionText(): string
    {
        
        return $this->text;        
        
    }
    
    public function getCorrectAnswer()
    {
        
        return $this->correctAnswer;
        
    }
    
    abstract public function getQuestionTips(): string;
    
    abstract public function checkAnswer($userAnswer): bool;
    
}


class NumericQuestion extends Question
{
    
    private $answerDeviation;    
    
    public function __construct(string $text, float $correctAnswer, float $answerDeviation)
    {
        
        parent::__construct($text, $correctAnswer);
        $this->answerDeviation = $answerDeviation;
        
    }
    
    public function getQuestionTips(): string
    {
        
        $output = "Введите вариант ответа. Допустимая погрешность - {$this->answerDeviation}.";
        
        return $output;
        
    }
    
    public function checkAnswer($userAnswer): bool
    {
        
        $leftLimit  = $this->correctAnswer - $this->answerDeviation;
        $rightLimit = $this->correctAnswer + $this->answerDeviation;
        
        return ($userAnswer >= $leftLimit && $userAnswer <= $rightLimit);        
        
    }    
    
}


class ChoiceQuestion extends Question
{
    
    private $choices = array();
    
    public function __construct(string $text, string $correctAnswer, array $choices)
    {
        
        if (empty($choices)) {
            
            throw new QuestionException('Массив с вариантами ответов пуст.');
            
        }
        
        parent::__construct($text, $correctAnswer);
        $this->choices = $choices;       
        
    }
    
    public function getQuestionTips(): string
    {
        
        $output = "Варианты ответа:\n";
        
        foreach ($this->choices as $choiceIndex => $choiceText) {

            $output .= "    {$choiceIndex}) {$choiceText}\n";

        }

        return rtrim($output);
    
    }
    
    public function checkAnswer($userAnswer): bool
    {
        
        return $userAnswer == $this->correctAnswer;
        
    }
    
    public function getAvailableChoices(): array
    {
        
        return $this->choices;
        
    }
    
}


class QuestionWriter
{
        
    private $questions = array();
    
    public function addQuestion(Question $question): void
    {
        
        $this->questions[] = $question;        
        
    }
    
    public function addQuestions(array $questions): void
    {
        
        if (empty($questions)) {
            
            throw new QuestionException('Переданный массив с вопросами пуст.');
            
        }
        
        foreach ($questions as $question) {
        
            $this->addQuestion($question);

        }            
        
    }
    
    public function writeQuestions(): void
    {
        
        $output = '';
        $questionNumber = 1;
        
        foreach ($this->questions as $question) {
            
            $output .= "$questionNumber. {$question->getQuestionText()}\n\n";
            
            $output .= "{$question->getQuestionTips()}\n\n\n";
            
            $questionNumber++;
            
        }
        
        echo rtrim($output);
        
    } 
    
    public function writeAnswers(array $userAnswers): void
    {
        
        $output = '';
        $questionNumber = 1;
        
        foreach ($this->questions as $question) {
        	
        	$arrayIndex = $questionNumber - 1;
            
            if (!isset($userAnswers[$arrayIndex])) {
                
                throw new QuestionException("Ответ на вопрос не найден (индекс $arrayIndex)");
                
            }
            
            $userAnswer = $userAnswers[$arrayIndex];
            $result = $question->checkAnswer($userAnswer);
            
            $output .= "$questionNumber. Ваш ответ - $userAnswer. ";
            
            $output .= $result ? "Верно!" : "Неверно! Правильный ответ - {$question->getCorrectAnswer()}.";
            
            $output .= "\n";
            
            $questionNumber++;
            
        }
        
        echo rtrim($output);
        
    } 
    
}


function getQuestions(): array
{
    
    $questions = array();

    $questions[] = new ChoiceQuestion('В каком фильме Георгия Данелия низшим сословиям предписывалось носить цак?', 'b',
        array('a' => 'Новая планета',
              'b' => 'Кин-дза-дза!',
              'c' => 'Убить Билла',
              'd' => 'Не грози Южному Централу, попивая сок у себя в квартале'
    ));

    $questions[] = new ChoiceQuestion('Первый и последний президент Советского Союза?', 'd',
        array('a' => 'Сталин',
              'b' => 'Черненко',
              'c' => 'Ельцин',
              'd' => 'Горбачев'
    ));

    $questions[] = new NumericQuestion('Чему равняется число Пи?', 3.14, 0.002);
    $questions[] = new NumericQuestion('Сколько существует чудес света?', 7, 0);
    
    return $questions;    
    
}

$questions = getQuestions();

$questionWriter = new QuestionWriter();
$questionWriter->addQuestions($questions);
$questionWriter->writeQuestions();

$answers = array('b', 'c', 3.141592, 8);

echo "\n\n\n";

$questionWriter->writeAnswers($answers);