<?php

namespace Application\Model\Manager;

use Ainias\Core\Model\Manager\StandardManager;
use Application\Model\Repository\LevelRepository;
use Application\Model\Level;

class LevelManager extends StandardManager
{
    /** @var LevelRepository */
    protected $repository;

    /** @var WordManager */
    protected $wordManager;

    public function __construct(LevelRepository $repository, WordManager $wordManager, Level $entity = null)
    {
        $this->repository = $repository;
        $this->wordManager = $wordManager;
        parent::__construct($repository, $entity);
    }

    /**
     * @param int $id
     * @return Level
     */
    public function getEntityById($id)
    {
        return parent::getEntityById($id);
    }

    public function beginTransaction()
    {
        $this->repository->beginTransaction();
    }

    public function endTransaction()
    {
        $this->repository->endTransaction();
    }

    /**
     * @param \DateTime $dateTime
     * @param int $offset
     * @return array
     */
    public function findNewerThan($dateTime, $offset = 0)
    {
        return $this->repository->findNewerThanDate($dateTime, $offset);
    }

    public function countNewerThan($dateTime)
    {
        return $this->repository->countNewerThanDate($dateTime);
    }

    static function levelToArray(Level $level)
    {
        return [
            "id" => $level->getId(),
            "words" => json_decode($level->getWords()),
            "rotations" => json_decode($level->getPositions()),
            "lastUpdated" => $level->getLastUpdated()->format("Y-m-d H:i:s"),
            "language" => $level->getLang(),
            "rendererType" => $level->getRenderer(),
            "difficulty" => $level->getDifficulty(),
            "deleted" => $level->isDeleted(),
        ];
    }

    static function levelsToArray($levels)
    {
        $returnArray = [];
        /** @var Level $level */
        foreach ($levels as $level) {
            $returnArray[] = LevelManager::levelToArray($level);
        }
        return $returnArray;
    }

    public function getDoubleUsedWords()
    {
        $words = [];
        $sameWords = [];

        /** @var Level[] $level */
        $level = $this->findBy(["deleted" => 0]);

        $levelNumber = count($level);
        for ($i = 0; $i < $levelNumber; $i++) {
            $words[$i] = $words[$i] ?? json_decode($level[$i]->getWords());
            $wordLength = count($words[$i]);
            for ($j = $i + 1; $j < $levelNumber; $j++) {
                $words[$j] = $words[$j] ?? json_decode($level[$j]->getWords());
                for ($k = 0; $k < $wordLength; $k++) {
                    if (in_array($words[$i][$k], $words[$j])) {

                        $sameWords[$words[$i][$k]] = ($sameWords[$words[$i][$k]] ?? []);
                        $sameWords[$words[$i][$k]][] = $level[$i];
                        $sameWords[$words[$i][$k]][] = $level[$j];
                    }
                }
            }
        }
        foreach ($sameWords as $key => &$levels) {
            $sameWords[$key] = array_unique(LevelManager::levelsToArray($levels), SORT_REGULAR);
        }

        return $sameWords;
    }

    public function generateLevels()
    {
        $levelsToGenerate = [
            [
                "wordLength" => 12,
                "numWords" => 4,
                "numPositions" => 15,
                "renderer" => 160,
                "difficulty" => function () {
                    return 160;
                },
            ],
            [
                "wordLength" => 8,
                "numWords" => 4,
                "numPositions" => 10,
                "renderer" => 120,
                "difficulty" => function () {
                    return ((rand(1, 100) <= 5) ? 140 : 120);
                },
            ],
            [
                "wordLength" => 12,
                "numWords" => 6,
                "numPositions" => 21,
                "renderer" => 140,
                "difficulty" => function () {
                    return ((rand(1, 100) <= 5) ? 160 : 140);
                },
            ],
            [
                "wordLength" => 8,
                "numWords" => 6,
                "numPositions" => 14,
                "renderer" => 100,
                "difficulty" => function () {
                    return ((rand(1, 100) <= 5) ? 120 : 100);
                },
            ],
//            [
//                "wordLength" => 10,
//                "numWords" => 2,
//                "numPositions" => 5,
//                "renderer" => 60,
//                "difficulty" => function () {
//                    return ((rand(1,100) <= 5)?100:60);
//                },
//            ],
//            [
//                "wordLength" => 8,
//                "numWords" => 2,
//                "numPositions" => 4,
//                "renderer" => 40,
//                "difficulty" => function () {
//                    return ((rand(1,100) <= 5)?120:100);
//                },
//            ],

        ];

        //Für höhere Wahrscheinlichkeiten
        $levelsToGenerate[] = $levelsToGenerate[0];
        $levelsToGenerate[] = $levelsToGenerate[1];

        $levelGenerated = 0;

        $words = [];
        do {
//            var_dump($levelsToGenerate);
            $currentIndex = rand(0, count($levelsToGenerate) - 1);
            $words[$levelsToGenerate[$currentIndex]["wordLength"]] = $words[$levelsToGenerate[$currentIndex]["wordLength"]] ?? $this->wordManager->findWordsForLength($levelsToGenerate[$currentIndex]["wordLength"]);

            $currentWords = $words[$levelsToGenerate[$currentIndex]["wordLength"]];

//            var_dump(count($currentWords), $levelsToGenerate[$currentIndex]["numWords"]);

            if (count($currentWords) >= $levelsToGenerate[$currentIndex]["numWords"]) {
                $selectedWords = [];
                for ($i = 0; $i < $levelsToGenerate[$currentIndex]["numWords"]; $i++) {
                    $wordCount = count($currentWords);
                    $currentWordIndex = rand(0, $wordCount - 1);
                    $selectedWords[] = strtoupper($currentWords[$currentWordIndex]->getWord());
                    $currentWords[$currentWordIndex]->setUsed(true);
                    $this->wordManager->save($currentWords[$currentWordIndex], false);
                    array_splice($currentWords, $currentWordIndex, 1);
                }
                $words[$levelsToGenerate[$currentIndex]["wordLength"]] = $currentWords;
                $positions = [];
                do {
                    for ($i = 0; $i < $levelsToGenerate[$currentIndex]["numPositions"]; $i++) {
                        $positions[] = rand(0, 3);
                    }
                } while (count(array_filter($positions, function($position) {
                    return $position == 0;
                }))/$levelsToGenerate[$currentIndex]["numPositions"] >= 0.67);

                $level = new Level();
                $level->setWords(json_encode($selectedWords));
                $level->setPositions(json_encode($positions));
                $level->setRenderer($levelsToGenerate[$currentIndex]["renderer"]);
                $level->setDifficulty($levelsToGenerate[$currentIndex]["difficulty"]());
                $level->setLastUpdated(new \DateTime());
                $level->setLang(1); //German
                $level->setDeleted(false);
                $this->save($level, false);

                $levelGenerated++;
            } else {
                array_splice($levelsToGenerate, $currentIndex, 1);
            }

        } while (count($levelsToGenerate) > 0);

        $this->emFlush();
        $this->wordManager->emFlush();
        return $levelGenerated;
    }
}