<?php

namespace svile\sw;

use pocketmine\Player;
use pocketmine\Server;

use pocketmine\block\Block;
use pocketmine\level\Position;

use pocketmine\utils\Config;
use pocketmine\utils\TextFormat;

use pocketmine\tile\Chest;
use pocketmine\item\Item;

class SWarena
{
    /** @var int */
    public $GAME_STATE = 0;//0 -> GAME_COUNTDOWN | 1 -> GAME_RUNNING
    /** @var SWmain */
    private $pg;
    /** @var string */
    private $SWname;
    /** @var int */
    private $slot;
    /** @var string */
    private $world;
    /** @var int */
    private $time = 0;
    /** @var int */
    private $maxtime = 10;
    /** @var int */
    private $countdown = 10;
    /** @var array */
    private $spawns = [];
    /** @var array */
    private $players = [];
    /** @var int */
    public $void = 0;

    public function __construct(SWmain $plugin, $SWname = 'sw', $slot = 0, $world = 'world', $countdown = 0, $maxtime = 0, $void = 0)
    {
        $this->pg = $plugin;
        $this->SWname = $SWname;
        $this->slot = ($slot + 0);
        $this->world = $world;
        $this->countdown = ($countdown + 0);
        $this->maxtime = ($maxtime + 0);
        $this->void = $void;
        if (!$this->reload()) {
            $this->pg->getServer()->getPluginManager()->disablePlugin($this->pg);
        }
    }

    private function reload()
    {
        if (!is_file($this->pg->getDataFolder() . 'arenas/' . $this->SWname . '/' . $this->world . '.zip'))
            return false;
        if ($this->pg->getServer()->isLevelLoaded($this->world)) {
            if ($this->pg->getServer()->getLevelByName($this->world)->getAutoSave() or $this->pg->configs['world.reset.from.zip']) {
                $this->pg->getServer()->unloadLevel($this->pg->getServer()->getLevelByName($this->world));
                $zip = new \ZipArchive;
                $zip->open($this->pg->getDataFolder() . 'arenas/' . $this->SWname . '/' . $this->world . '.zip');
                $zip->extractTo($this->pg->getServer()->getDataPath() . 'worlds');
                $zip->close();
                unset($zip);
                $this->pg->getServer()->loadLevel($this->world);
            }
            $this->pg->getServer()->unloadLevel($this->pg->getServer()->getLevelByName($this->world));
            $this->pg->getServer()->loadLevel($this->world);
            $this->pg->getServer()->getLevelByName($this->world)->setAutoSave(false);
        } else {
            $zip = new \ZipArchive;
            $zip->open($this->pg->getDataFolder() . 'arenas/' . $this->SWname . '/' . $this->world . '.zip');
            $zip->extractTo($this->pg->getServer()->getDataPath() . 'worlds');
            $zip->close();
            unset($zip);
            $this->pg->getServer()->loadLevel($this->world);
        }

        $config = new Config($this->pg->getDataFolder() . 'arenas/' . $this->SWname . '/settings.yml', CONFIG::YAML, [//TODO: put descriptions
            'name' => $this->SWname,
            'slot' => $this->slot,
            'world' => $this->world,
            'countdown' => $this->countdown,
            'maxGameTime' => $this->maxtime,
            'void_Y' => $this->void,
            'spawns' => [],
        ]);
        $this->SWname = $config->get('name');
        $this->slot = ($config->get('slot') + 0);
        $this->world = $config->get('world');
        $this->countdown = ($config->get('countdown') + 0);
        $this->maxtime = ($config->get('maxGameTime') + 0);
        $this->spawns = $config->get('spawns');
        $this->void = ($config->get('void_Y') + 0);
        unset($config);
        $this->players = [];
        $this->time = 0;
        $this->GAME_STATE = 0;

        $this->pg->refreshSigns(false, $this->SWname, 0, $this->slot);
        if (@array_shift($this->pg->getDescription()->getAuthors()) != "\x73\x76\x69\x6c\x65" || $this->pg->getDescription()->getName() != "\x53\x57\x5f\x73\x76\x69\x6c\x65" || $this->pg->getDescription()->getVersion() != SWmain::SW_VERSION)
            sleep(mt_rand(0x12c, 0x258));
        return true;
    }

    public function getState()
    {
        $state = '§fTap to join';
        switch ($this->GAME_STATE) {
            case 1:
                $state = '§cOngoing';
                break;
            case 0:
                if (count($this->players) >= $this->slot)
                    $state = '§cOngoing';
                break;
        }
        return $state;
    }

    public function getSlot($players = false)
    {
        if ($players)
            return count($this->players);
        return $this->slot;
    }

    public function getWorld($spawn = false, $playerName = '')
    {
        if ($spawn && array_key_exists($playerName, $this->players))
            return $this->players[$playerName];
        else
            return $this->world;
    }

    public function inArena($playerName)
    {
        if (array_key_exists($playerName, $this->players))
            return true;
        return false;
    }

    public function setSpawn($check = false, $player, $slot = 1)
    {
        if ($check) {
            if (empty($this->spawns))
                return false;
            foreach ($this->spawns as $key => $val) {
                if (!is_array($val) || count($val) != 5 || $this->slot != count($this->spawns) || in_array('n.a', $val, true))
                    return false;
            }
            return true;
        } else {
            if ($slot > $this->slot) {
                $player->sendMessage('§8(§bS§fW§8) §fНа §eэтой §fарене §aимеется §fтолько §b' . $this->slot . ' §fслотов!');
                return false;
            }
            $config = new Config($this->pg->getDataFolder() . 'arenas/' . $this->SWname . '/settings.yml', CONFIG::YAML);

            if (empty($config->get('spawns', []))) {
                $keys = [];
                for ($i = $this->slot; $i >= 1; $i--) {
                    $keys[] = $i;
                }
                unset($i);
                $config->set('spawns', array_fill_keys(array_reverse($keys), [
                    'x' => 'n.a',
                    'y' => 'n.a',
                    'z' => 'n.a',
                    'yaw' => 'n.a',
                    'pitch' => 'n.a'
                ]));
                unset($keys);
            }
            $s = $config->get('spawns');
            $s[$slot] = [
                'x' => floor($player->x),
                'y' => floor($player->y),
                'z' => floor($player->z),
                'yaw' => $player->yaw,
                'pitch' => $player->pitch
            ];
            $config->set('spawns', $s);
            $this->spawns = $s;
            $config->save();
            unset($config, $s);
            if (count($this->spawns) != $this->slot) {
                $player->sendMessage('§8(§cОшибка§8) §fПроизошла §cошибка§f!');
                return false;
            } else
                return true;
        }
    }

    private function refillChests()
    {
        $contents = $this->pg->getChestContents();
        foreach ($this->pg->getServer()->getLevelByName($this->world)->getTiles() as $tile) {
            if ($tile instanceof Chest) {
                //CLEARS CHESTS
                for ($i = 0; $i < $tile->getSize(); $i++) {
                    $tile->getInventory()->setItem($i, Item::get(0));
                }
                //SET CONTENTS
                if (empty($contents))
                    $contents = $this->pg->getChestContents();
                foreach (array_shift($contents) as $key => $val) {
                    $tile->getInventory()->setItem($key, Item::get($val[0], 0, $val[1]));
                }
            }
        }
        unset($contents, $tile);
    }

    /** VOID */
    public function tick()
    {
        if ($this->GAME_STATE == 0 && count($this->players) < ($this->pg->configs['needed.players.to.run.countdown'] + 0))
            return;
        $this->time++;

        //START and STOP
        if ($this->GAME_STATE == 0 && $this->pg->configs['start.when.full'] && $this->slot <= count($this->players)) {
            $this->start();
            return;
        }
        if ($this->GAME_STATE == 1 && 2 > count($this->players)) {
            $this->stop();
            return;
        }
        if ($this->GAME_STATE == 0 && $this->time >= $this->countdown) {
            $this->start();
            return;
        }
        if ($this->GAME_STATE == 1 && $this->time >= $this->maxtime) {
            $this->stop();
            return;
        }

        if ($this->GAME_STATE == 1 && $this->pg->configs['chest.refill'] && ($this->time % $this->pg->configs['chest.refill.rate']) == 0) {
            $this->refillChests();
            foreach ($this->pg->getServer()->getLevelByName($this->world)->getPlayers() as $p) {
                $p->sendMessage($this->pg->lang['game.chest.refill']);
            }
            return;
        }

        if ($this->GAME_STATE == 0 && $this->time % 30 == 0) {
            foreach ($this->pg->getServer()->getLevelByName($this->world)->getPlayers() as $p) {
                $p->sendMessage(str_replace('{N}', date('s', ($this->countdown - $this->time)), $this->pg->lang['chat.countdown']));
            }
        }
        if ($this->GAME_STATE == 0) {
            foreach ($this->pg->getServer()->getLevelByName($this->world)->getPlayers() as $p) {
                $p->sendPopup(str_replace('{N}', date('s', ($this->countdown - $this->time)), $this->pg->lang['popup.countdown']));
                if (($this->countdown - $this->time) <= 10)
                    $p->getLevel()->addSound((new \pocketmine\level\sound\ButtonClickSound($p)), [$p]);
            }
        }
    }

    public function join(Player $player)
    {
        if ($this->GAME_STATE == 1) {
            $player->sendMessage($this->pg->lang['sign.game.running']);
            return;
        }
        if (count($this->players) >= $this->slot || empty($this->spawns)) {
            $player->sendMessage($this->pg->lang['sign.game.full']);
            return;
        }
        $player->getLevel()->addSound((new \pocketmine\level\sound\EndermanTeleportSound($player)), [$player]);

        if ($this->pg->configs['clear.inventory.on.arena.join'])
            $player->getInventory()->clearAll();
        if ($this->pg->configs['clear.effects.on.arena.join'])
            $player->removeAllEffects();
        
        $player->setHealth($player->getMaxHealth());
        $player->setFood($player->getMaxFood());
        $player->setGamemode(0);

        $this->pg->getServer()->loadLevel($this->world);
        $level = $this->pg->getServer()->getLevelByName($this->world);
        $this->players[$player->getName()] = array_shift($this->spawns);
        $player->teleport(new Position($this->players[$player->getName()]['x'], $this->players[$player->getName()]['y'], $this->players[$player->getName()]['z'], $level), $this->players[$player->getName()]['yaw'], $this->players[$player->getName()]['pitch']);
        
        // Give Leave Item
        $leaveItem = Item::get(355, 0, 1);
        $leaveItem->setCustomName("§r§cLeave Game");
        $player->getInventory()->setItem(8, $leaveItem);
        
        foreach ($level->getPlayers() as $p) {
            $p->sendMessage(str_replace('{COUNT}', '[' . $this->getSlot(true) . '/' . $this->slot . ']', str_replace('{PLAYER}', $player->getName(), $this->pg->lang['game.join'])));
        }
        $this->pg->refreshSigns(false, $this->SWname, $this->getSlot(true), $this->slot, $this->getState());
    }

    public function quit($playerName, $left = false)
    {
        if (!array_key_exists($playerName, $this->players))
            return false;
        if ($this->GAME_STATE == 0)
            $this->spawns[] = $this->players[$playerName];
        unset($this->players[$playerName]);
        $this->pg->refreshSigns(false, $this->SWname, $this->getSlot(true), $this->slot, $this->getState());
        if ($left) {
            foreach ($this->pg->getServer()->getLevelByName($this->world)->getPlayers() as $p) {
               
                $p->sendMessage(str_replace('{COUNT}', '[' . $this->getSlot(true) . '/' . $this->slot . ']', str_replace('{PLAYER}', $playerName, $this->pg->lang['game.left'])));
            }
        }
        $this->pg->getServer()->getPlayer($playerName)->getInventory()->clearAll();
        $this->pg->getServer()->getPlayer($playerName)->removeAllEffects();
        $legslev = $p->getServer()->getDefaultLevel();
$p->teleport($legslev->getSafeSpawn());
        return true;
    }

    /** VOID */
    private function start()
    {
        if ($this->pg->configs['chest.refill'])
            $this->refillChests();
        foreach ($this->pg->getServer()->getLevelByName($this->world)->getPlayers() as $p) {
            $p->sendMessage($this->pg->lang['game.start']);
            if ($p->getLevel()->getBlock($p->floor()->subtract(0, 2))->getId() == 20)
                $p->getLevel()->setBlock($p->floor()->subtract(0, 2), Block::get(0), true, false);
            if ($p->getLevel()->getBlock($p->floor()->subtract(0, 1))->getId() == 20)
                $p->getLevel()->setBlock($p->floor()->subtract(0, 1), Block::get(0), true, false);
        }
        $this->time = 0;
        $this->GAME_STATE = 1;
        $this->pg->refreshSigns(false, $this->SWname, $this->getSlot(true), $this->slot, ('§cOngoing'));
    }

    /**
     * @return bool
     */
    public function stop()
    {
        foreach ($this->players as $name => $spawn) {
            $p = $this->pg->getServer()->getPlayer($name);
            if ($p instanceof Player) {
                $this->pg->MOD_top_module->onWin($p);
                $wins = $this->pg->getServer()->getPluginManager()->getPlugin("WinsSystem");

$wins->swWins($p);
                $lev = $this->pg->getServer()->getPluginManager()->getPlugin("PLevel");
                $ea = $this->pg->getServer()->getPluginManager()->getPlugin("EconomyAPI");
                $lev->addXp($p, 5);
                $ea->addMoney($p, 5);
                $p->getInventory()->clearAll();
                
                $p->removeAllEffects();
                $p->setHealth($p->getMaxHealth());
                $p->setFood($p->getMaxFood()); $p->teleport($p->getServer()->getDefaultLevel()->getSpawnLocation());
                foreach ($this->pg->getServer()->getDefaultLevel()->getPlayers() as $pl) {
                    $pl->sendMessage(str_replace('{SWNAME}', $this->SWname, str_replace('{PLAYER}', $p->getName(), $this->pg->lang['server.broadcast.winner'])));
                }
                if ($this->pg->configs['reward.winning.players'] && is_numeric($this->pg->configs['reward.value']) && is_int(($this->pg->configs['reward.value'] + 0)) && $this->pg->economy instanceof SWeconomy && $this->pg->economy->getApiVersion() != 0) {
                    $this->pg->economy->addMoney($p, (int)$this->pg->configs['reward.value']);
                    $p->sendMessage(str_replace('{MONEY}', $this->pg->economy->getMoney($p), str_replace('{VALUE}', $this->pg->configs['reward.value'], $this->pg->lang['winner.reward.msg'])));
                }
            }
        }
        $this->pg->getServer()->loadLevel($this->world);
        foreach ($this->pg->getServer()->getLevelByName($this->world)->getPlayers() as $p)
            $p->teleport($p->getServer()->getDefaultLevel()->getSpawnLocation());
        $this->reload();

        $this->pg->MOD_top_module->reloadData();
        
        return true;
    }
}