<?php
use Random\Engine;
class UserModel
{

    private EnigmeGateway $enigme_gateway;
    private PartieGateway $partie_gateway;
    private UtilisateurGateway $utilisateur_gateway;
    private ResoudreGateway $resoudre_gateway;
    private Nettoyage $nettoyage;
    private Validation $validation;

    function __construct()
    {
        try {
            global $error, $view, $rep;
            $this->enigme_gateway = new EnigmeGateway();
            $this->partie_gateway = new PartieGateway();
            $this->utilisateur_gateway = new UtilisateurGateway();
            $this->resoudre_gateway = new ResoudreGateway();
            $this->nettoyage = new Nettoyage();
            $this->validation = new Validation();
        } catch (Exception $e) {
            $error = $e->getMessage();
            require($rep . $view['erreur']);
        }
    }
    public function addToQueue($mailUtilisateur) : int{
        global $nbEnigmePerGame;
        $idPartie=$this->partie_gateway->findPartieInQueue();
        if ($idPartie == 0){
            $lesEnigmes = array();
            $enigme= $this->enigme_gateway->getRandomEnigme();
            $lesEnigmes [] = $enigme;
            // check nb enigme;
            for ($i = 1; $i < $nbEnigmePerGame; $i++){
                $enigme = $this->enigme_gateway->getRandomEnigme();
                while (in_array($enigme, $lesEnigmes)){
                    $enigme = $this->enigme_gateway->getRandomEnigme();
                }
                $lesEnigmes [] = $enigme;
            }
            $this->partie_gateway->createPartieMulti($lesEnigmes, $mailUtilisateur);
            $idPartie=$this->partie_gateway->findPartieInQueue();
            $etat=$this->partie_gateway->getEtat($idPartie);
        }
        else{
            $this->partie_gateway->addToPartie($mailUtilisateur, $idPartie);
            $etat=$this->partie_gateway->getEtat($idPartie);
        }
        $tpsMaxPartie = $this->calculTpsMaXPartie($idPartie);
        $_SESSION['tpsMaxPartie'] = $tpsMaxPartie;
        return $idPartie;
    }

    public function calculTpsMaXPartie( int $idPartie) : int {
        $tpsMaxPartie = 0;
        $lesIdEnigmes = $this->partie_gateway->findAllEnigmeIdInPartie($idPartie);
        foreach ($lesIdEnigmes as $idEnigme){
            $enigme = $this->enigme_gateway->findById($idEnigme)[0];
            $tpsMaxPartie += $enigme->getTempsDeResolution();
        }
        return $tpsMaxPartie;
    }

    public function findUserGroup() : int {
        return $this->partie_gateway->findPartieInQueue();
    }

    public function launchGame($idPartie)
    {
            $this->partie_gateway->launchGame($idPartie);
    }

    public function logout()
    {
        session_unset();
        session_destroy();
        $_SESSION = array();
        $_SESSION['role'] = 'visitor';
        header('Location: index.php');
    }

    public function findUsersInQueue() : array
    {
        return $this->utilisateur_gateway->findUsersInQueue();
    }

    public function getEnigmeByOrdre(int $num) : Enigme
    {
        $tabEnigme = $this->enigme_gateway->findByOrdre($num);
        if ($tabEnigme == null) {
            $last = $this->enigme_gateway->findLastEnigmaByOrdre();
            if ($last[0]->getOrdre() == ($num-1)){
                return $this->enigme_gateway->findByOrdre($num-1)[0];
            }
            throw new Exception("Enigme non trouvée");
        }
        return $tabEnigme[0];
    }

    public function resoudreEnigmeSolo(Utilisateur $utilisateur, int $enigmeId){
        if($this->resoudre_gateway->checkPartieIsAlreadyCreate($enigmeId)){
            $partieId = $this->partie_gateway->findPartieByEnigmeId($enigmeId)->getIdPartie();
        } else {
            $enigme=$this->enigme_gateway->findById($enigmeId)[0];
            $this->partie_gateway->createPartie($enigme, $utilisateur);
            $partieId = $this->partie_gateway->findLastPartie()->getIdPartie();
        }
        $this->resoudre_gateway->resoudreEnigmeSolo($utilisateur, $enigmeId, $partieId);
    }
    public function resoudreEnigmeMulti(Utilisateur $utilisateur, int $enigmeId, int $idPartie, int $index){
        $this->resoudre_gateway->resoudreEnigmeMulti($utilisateur, $enigmeId, $idPartie, $index);
    }
    public function checkEnigmeIsEnded(string $mailUtilisateur, int $enigmeId) : bool {
        return $this->resoudre_gateway->checkEnigmeIsEnded($mailUtilisateur,$enigmeId);
    }
    public function enigmeEnded(string $mailUtilisateur, int $enigmeId){
        $this->resoudre_gateway->enigmeEnded($mailUtilisateur,$enigmeId);
    }
    public function enigmeMultiEnded(string $mailUtilisateur, int $enigmeId){
        global $playerNumberPerGame, $test;
        $idPartie = $_SESSION['idPartie'];
        $dateDebut = $this->partie_gateway->getDateDebut($idPartie);
        $now = new DateTime();
        $interval = $now->diff($dateDebut);
        $tempDeResolution = $interval->days * 24 * 60 * 60 + $interval->h * 60 * 60 + $interval->i * 60 + $interval->s;
        $leClassement = $this->resoudre_gateway->getClassement($enigmeId, $idPartie);
        usort($leClassement, function($leClassement, $b) {
            if ($leClassement === $b) {
                return 0;
            }
            if ($leClassement === null) {
                return 1;
            }
            if ($b === null) {
                return -1;
            }
            return ($leClassement < $b) ? -1 : 1;
        });
        for ($i = 0; $i < $playerNumberPerGame; $i++){
            if ($leClassement[$i] == null || $leClassement[$i] != $i + 1){
                $classement = $i + 1;
                break;
            }
        }
        $this->resoudre_gateway->enigmeMultiEnded($mailUtilisateur,$enigmeId, $idPartie ,$tempDeResolution, $classement);
    }

    public function saveCode(string $mailUtilisateur, int $enigmeId,string $code ){
        $this->resoudre_gateway->saveCode($mailUtilisateur, $enigmeId, $code);
    }
    public function saveCodeMulti(string $mailUtilisateur, int $enigmeId, int $idPartie, int $index,string $code ){
        $this->resoudre_gateway->saveCodeMulti($mailUtilisateur, $enigmeId, $idPartie , $index, $code);
    }

    public function getCode(string $mailUtilisateur, int $enigmeId){
        return $this->resoudre_gateway->getCode($mailUtilisateur, $enigmeId);
    }

    public function getLastEnigmeEnded(string $mailUtilisateur) : Enigme{
        $id=$this->resoudre_gateway->getLastEnigmeEnded($mailUtilisateur);
        if ($id == 0){
            return $this->enigme_gateway->findByOrdre(1)[0];
        }
        return $this->enigme_gateway->findById($id)[0];
    }

    public function getEtatPartie($idPartie){
        return $this->partie_gateway->getEtat($idPartie);
    }

    public function getLesJoueurs($idPartie) : array{
        $lesMailJoeurs = $this->partie_gateway->getLesMailJoueurs($idPartie);
        $lesJoueurs = array();
        foreach ($lesMailJoeurs as $mail){
            $lesJoueurs[] = $this->utilisateur_gateway->getUtilisateurByEmail($mail);
        }
        return $lesJoueurs;
    }

    public function getEnigmebyPartieIdAndIndex($idPartie, $index) : Enigme{
        $idEnigme = $this->partie_gateway->findEnigmeIdInPartieWithIndex($idPartie, $index);
        return $this->enigme_gateway->findById($idEnigme)[0];
    }

    public function findEnigmeById(int $enigmeId) : Engine{
        return $this->enigme_gateway->findById($enigmeId)[0];
    }

    public function getLastIndex(int $idPartie) : int{
        return $this->resoudre_gateway->getLastIndex($idPartie);
    }

    public function getLastOrdre() :int {
        return $this->enigme_gateway->getLastOrdre();
    }

    public function getDateDebut($idPartie) : DateTime{
        return $this->partie_gateway->getDateDebut($idPartie);
    }

    public function getPointsAtTheEnd(string $mailUtilisateur, int $idPartie) : int{
        global $playerNumberPerGame;
        $points = 0;
        $result = $this->resoudre_gateway->getAllByPartieAndUtilisateur($mailUtilisateur, $idPartie);
        foreach ($result as $row){
            if ($row['temps'] == 0){
                $points += 0;
            }
            else if ($row['classement'] == 1){
                $points += $this->enigme_gateway->findById($row['enigme'])[0]->getPoints();
            }
            else if ($row['classement'] == 2){
                $points += $this->enigme_gateway->findById($row['enigme'])[0]->getPoints() * 0.75;
            }
            else if ($row['classement'] == 3){
                $points += $this->enigme_gateway->findById($row['enigme'])[0]->getPoints() * 0.5;
            }
            else{
                $points += $this->enigme_gateway->findById($row['enigme'])[0]->getPoints() * 0.25;
            }
        }
        return $points;
    }
    public function getTempsAtTheEnd(string $mailUtilisateur, int $idPartie) : int{
        $temps = 0;
        $result = $this->resoudre_gateway->getAllByPartieAndUtilisateur($mailUtilisateur, $idPartie);
        foreach ($result as $row){
            $temps += $row['temps'];
        }
        return $temps;
    }
    public function getEndGameInfo(int $idPartie) : array{
        $lesJoueurs = $this->getLesJoueurs($idPartie);
        $lesPoints = array();
        $lesTemps = array();
        foreach ($lesJoueurs as $joueur){
            $lesPoints[] = $this->getPointsAtTheEnd($joueur->getEmail(), $idPartie);
            $lesTemps[] = $this->getTempsAtTheEnd($joueur->getEmail(), $idPartie);
        }
        $maxPoints = max($lesPoints);
        $maxIndex = array_keys($lesPoints, $maxPoints)[0];
        $vainqueur = $lesJoueurs[$maxIndex];
        array_multisort($lesPoints, SORT_DESC, $lesJoueurs, $lesTemps);
        return array($lesJoueurs, $lesPoints, $lesTemps, $vainqueur);
    }
    public function checkGameIsEnd(int $idPartie) : bool{
        $lesMailJoueurs = $this->partie_gateway->getLesMailJoueurs($idPartie);
        $lesIdEnigmes = $this->partie_gateway->getLesIdEnigmes($idPartie);
        foreach ($lesMailJoueurs as $mail){
            foreach ($lesIdEnigmes as $idEnigme){
                if ($this->resoudre_gateway->checkEnigmeIsEndedInPartie($mail, $idEnigme, $idPartie) == false){
                    return false;
                }
            }
        }
        return true;
    }
    public function endGame(int $idPartie) {
        $this->partie_gateway->endGame($idPartie);
    }

    public function checkUserIsInPartie(string $mailUtilisateur) : bool{
        return $this->partie_gateway->checkUserIsInPartie($mailUtilisateur);
    }
    public function majDateDebut(int $idPartie) {
        $this->partie_gateway->majDateDebut($idPartie);
        $_SESSION['tpsMaxPartie'] = $this->calculTpsMaXPartie($idPartie);
    }
    public function quitQueue(string $mailUtilisateur, int $idPartie) {
        $this->partie_gateway->quitQueue($mailUtilisateur, $idPartie);
        if ($this->partie_gateway->getPlayerNumber($idPartie) == 0){
            $this->partie_gateway->delete($idPartie);
        }
    }
    public function skipEnigme(string $mailUtilisateur, int $idPartie, int $idEnigme) {
        $this->resoudre_gateway->skipEnigme($mailUtilisateur, $idPartie, $idEnigme);
    }
    public function quitGame(string $mailUtilisateur, int $idPartie) {
        $lesIdEnigmes = $this->partie_gateway->getLesIdEnigmes($idPartie);
        foreach ($lesIdEnigmes as $idEnigme){
            if ($this->resoudre_gateway->checkEnigmeIsEndedInPartie($mailUtilisateur, $idEnigme, $idPartie) == false){
                $this->resoudre_gateway->skipEnigme($mailUtilisateur, $idPartie, $idEnigme);
            }
        }
        $this->partie_gateway->quitGame($mailUtilisateur, $idPartie);
    }
    public function getPlayersPseudo(int $idPartie) : array{
        $this->partie_gateway->getLesMailJoueurs($idPartie);
        $lesPseudos = array();
        foreach ($this->partie_gateway->getLesMailJoueurs($idPartie) as $mail){
            $lesPseudos[] = $this->utilisateur_gateway->getPseudoByEmail($mail);
        }
        return $lesPseudos;
    }
    public function getPlayersEmail(int $idPartie) : array{
        $lesJoueurs = $this->getLesJoueurs($idPartie);
        return $lesJoueurs;
    }
    public function getPlayerScore(string $mailUtilisateur,int $idPartie) : array{
        global $playerNumberPerGame;
        $score = array();
        $result = $this->resoudre_gateway->getAllByPartieAndUtilisateur($mailUtilisateur, $idPartie);
        foreach ($result as $row){
            if ($row['temps'] == 0){
                $score[] = 0;
            }
            else if ($row['classement'] == 1){
                $score[] = $this->enigme_gateway->findById($row['enigme'])[0]->getPoints();
            }
            else if ($row['classement'] == 2){
                $score[] = $this->enigme_gateway->findById($row['enigme'])[0]->getPoints() * 0.75;
            }
            else if ($row['classement'] == 3){
                $score[] = $this->enigme_gateway->findById($row['enigme'])[0]->getPoints() * 0.5;
            }
            else{
                $score[] = $this->enigme_gateway->findById($row['enigme'])[0]->getPoints() * 0.25;
            }
        }
        return $score;
    }
}