<?php

namespace duel\arena;

use pocketmine\event\{Listener, HandlerList};
use pocketmine\event\player\{PlayerExhaustEvent, PlayerInteractEvent, PlayerQuitEvent, PlayerDeathEvent, PlayerDropItemEvent, PlayerCommandPreprocessEvent};
use pocketmine\event\entity\{EntityLevelChangeEvent, EntityDamageEvent, EntityDamageByEntityEvent};
use pocketmine\level\Position;
use pocketmine\utils\Config;
use pocketmine\entity\Effect;
use pocketmine\item\Item;
use pocketmine\Player;
use duel\Mod_TopModule;
use pocketmine\math\Vector3;

use duel\Loader;
use pocketmine\Server;

class Arena implements Listener, ArenaStats
{
    private $status; 
    public $MOD_top_module;
    private $config;
    private $hits = [];
    private $combos = [];
    private $loader;
    private $data;
    public $players = [];
    private $oponente;
    public $stat = self::WAIT;
    private $time;
    private $register = false;
    
    public function __construct(array $data)
    {
        $this->data = $data;
        $this->loader = Loader::getInstance();
        $this->status = new Config($this->loader->getDataFolder() . 'status.yml', Config::YAML);
        $this->config = new Config($this->loader->getDataFolder() . 'config.yml', Config::YAML);
        $this->ip = new Config($this->loader->getDataFolder() . 'SanityIP.yml', Config::YAML);
        $this->kb = new Config($this->loader->getDataFolder() . 'Boxing_KB.yml', Config::YAML);
        $this->time = $this->config->get('Tempo_Iniciar');
        if (!$this->register) {
            $this->load();
        }
    }
    
public function join(Player $player)
{
    if (count($this->players) >= 2 ?? $this->stat == self::RUN ?? $this->inArena($player)) {
        return;
    }
    $player->teleport(new Position(...$this->data['lobby']));
    $this->players[] = $player;
    
    foreach ($this->players as $target) {
        $targetName = $target->getName();
        $playerStatus = $this->status->get($player->getName());

        if (!$this->status->exists($targetName)) {
            $this->status->set($targetName, [
                'Hits' => 0,
                'Vitorias' => 0,
                'Derrotas' => 0
            ]);
        }

        if ($targetName === $player->getName()) {
            $target->sendMessage(' ');
            $target->sendMessage('§8(§9Boxing§8) §l§a' . count($this->players) . ' §r§7- §a' . $player->getName() . '§7 entered the boxing ring. ');
            $target->sendMessage(' ');
        } else {
            $target->sendMessage(' ');
            $target->sendMessage('§8(§9Boxing§8) §l§a' . count($this->players) . ' §r§7- §a' . $player->getName() . '§7 entered the boxing ring. ');
            $target->sendMessage(' §7Total Hits: §a' . ($playerStatus['Hits'] ?? 0) . ' §7| §7Victories: §a' . ($playerStatus['Vitorias'] ?? 0) . ' §7| §7Defeats: §a' . ($playerStatus['Derrotas'] ?? 0));
            $target->sendMessage(' ');
        }
    }
        $player->setGamemode(Player::ADVENTURE);
        $player->getInventory()->clearAll();
        $player->setHealth(20);
        $player->setFood(20);
        $player->removeAllEffects();
        $defaultkb = new Vector3(0, 0, 0); 
        $player->knockBack($player, 0, $defaultkb->x, $defaultkb->z, $defaultkb->y);
        if($this->config->get('Menu_Status') === 'Sim') {
        $player->getInventory()->setItem(3, Item::get(Item::BOOK)->setCustomName('§r§l'.$this->config->get('Cor_Eventos_Arena').'Status§r'));
        $player->getInventory()->setItem(5, Item::get(Item::BED)->setCustomName('§r§l§cExit§r'));
        $player->getInventory()->setItem(8, Item::get(Item::GOLD_INGOT)->setCustomName('§r§l§eCredits§r'));
        } else {
        $player->getInventory()->setItem(4, Item::get(Item::BED)->setCustomName('§r§l§cExit§r'));
        $player->getInventory()->setItem(8, Item::get(Item::GOLD_INGOT)->setCustomName('§r§l§eCredits§r'));
        }
}
    
    public function quit(Player $player, $msg = false, $quit = false)
    {
        unset($this->players[array_search($player, $this->players)]);
        if (count($this->players) == 0) {
            $this->close();
        }
        $player->removeAllEffects();
       
        if ($quit) {
             $player->setHealth(20);
             $player->setFood(20);
        }
        $player->teleport($this->loader->getServer()->getDefaultLevel()->getSpawnLocation());
        
        if ($msg) {
             foreach ($this->players as $target) {
             	$target->sendMessage(' ');
                 $target->sendMessage('§8(§9Boxing§8) §l§c' . count($this->players) . ' §r§7- §c' . $player->getName() . '§7 exited the lobby. ');
             	$target->sendMessage(' ');
             }
        }
    }
    
public function dead(Player $player)
{
    $winner = null;
    $msg1 = false;
    $msg2 = false;
    
    foreach ($this->players as $target) {
        if ($target != $player) {
            $winner = $target;
            if (!$msg1) {

                $playerStatus = $this->status->get($winner->getName());
                $playerStatus['Vitorias'] += 1;
                $this->loader->MOD_top_module->onWin($winner);
                $this->loader->MOD_top_module->reloadData();
                
                $wins = $this->loader->getServer()->getPluginManager()->getPlugin("WinSystem");
				$wins->duelsWins($winner);
                
                $this->status->set($winner->getName(), $playerStatus);

                $playerStatus2 = $this->status->get($this->oponente[$winner->getName()]);
                $playerStatus2['Derrotas'] += 1;
                $this->status->set($this->oponente[$winner->getName()], $playerStatus2);
                $this->status->save();
                
                $winner->sendMessage(' ');
                $winner->sendMessage('§7You won the boxing match against §a' . $this->oponente[$winner->getName()] . '');
                $winner->sendMessage(' ');
                $winner->sendMessage(' §7Hits from Your Opponent: §a' . $this->hits[$this->oponente[$winner->getName()]] . '');
                $winner->sendMessage(' §7Combos From Your Opponent: §a' . $this->combos[$this->oponente[$winner->getName()]] . '');
                $winner->sendMessage(' ');
                $winner->sendMessage('§7Seus Hits: §a' . $this->hits[$winner->getName()] . '');
                $winner->sendMessage('§7Your Combos: §a' . $this->combos[$winner->getName()] . '');
                $winner->sendMessage(' ');
                $winner->sendMessage('§7 Total Victories: §l§a'.$playerStatus['Vitorias'].'');
                $winner->sendMessage('§7 Time Spent: §l§a' . gmdate('i:s', $this->time) . '§r');
                $winner->sendMessage(' ');
                $msg1 = true;
            }
        }
        $this->quit($target);
        
        if ($player === null) {
            return;
        } elseif (!$msg2) {
            $playerStatus = $this->status->get($player->getName());
            $player->sendMessage(' ');
            $player->sendMessage('§7You §l§cLost§r§7 The Boxing Match Against §c' . $this->oponente[$player->getName()] . '');
            $player->sendMessage(' ');
            $player->sendMessage(' §7Hits from your opponent: §c' . $this->hits[$this->oponente[$player->getName()]] . '');
            $player->sendMessage(' §7Combos from your opponent: §c' . $this->combos[$this->oponente[$player->getName()]] . '');
            $player->sendMessage(' ');
            $player->sendMessage('§7Your Hits: §c' . $this->hits[$player->getName()] . '');
            $player->sendMessage('§7Your Combos: §c' . $this->combos[$player->getName()] . '');
            $player->sendMessage(' ');
            $player->sendMessage('§7Total Defeats: §l§c'.$playerStatus['Derrotas'].'§r');
            $player->sendMessage('§7Time: §l§c' . gmdate('i:s', $this->time) . '§r');
            $player->sendMessage(' ');
            $msg2 = true;
        }
    }
}

    public function inArena(Player $player): bool
    {
        return in_array($player, $this->players);
    }
    
    public function tick()
    {
        switch ($this->stat) {
            case self::WAIT:
                if (count($this->players) >= 2) {
                    $this->time--;
                    foreach ($this->players as $player) {
                        $player->sendTip('§r§7Match starting in: '.$this->config->get('Cor_Eventos_Arena').'' . $this->time . 's');
                    }
                    if ($this->time == 0) {
                        $this->startGame();
                    }
                } else {
                    foreach ($this->players as $player) {
                        $player->sendTip('§r§cWaiting for one more player...');
                    }
                    if ($this->time != $this->config->get('Tempo_Iniciar')) {
                        $this->time = $this->config->get('Tempo_Iniciar');
                    }
                }
                break;
            case self::RUN:
                foreach ($this->players as $player) {
                    $player->sendTip('       §r§7Time: '.$this->config->get('Cor_Eventos_Arena').'' . gmdate('i:s', $this->time) . '§r  §7' . gmdate('i:s', $this->config->get('Tempo_Maximo')) . '');
                }
                if ($this->time == $this->config->get('Tempo_Maximo')) {
                    $this->closeGame();
                }
                $this->time++;
                break;
        }
    }
    
    public function msg()
    {
        switch ($this->stat) {
            case self::RUN:
                foreach ($this->players as $player) {
                    $player->setFood(20);
                    $player->sendPopup('§7Hits: '.$this->config->get('Cor_Eventos_Arena').'' . $this->hits[$player->getName()] . '§7'.$this->config->get('Cor_Eventos_Arena').'/' . $this->config->get('Hits_Maximo') . ' §7Combos: '.$this->config->get('Cor_Eventos_Arena').'' . $this->combos[$player->getName()] . '');
                }
                break;
        }
    }
    
    public function startGame()
    {
        /** @var Player */
        $first = $this->players[0];
        /** @var Player */
        $second = $this->players[1];
        
        $this->hits[$first->getName()] = 0;
        $this->combos[$first->getName()] = 0;
        $this->hits[$second->getName()] = 0;
        $this->combos[$second->getName()] = 0;
        $first->teleport(new Position(...$this->data['pos1']));
        $first->getInventory()->clearAll();
        $second->getInventory()->clearAll();
        
        $this->oponente[$first->getName()] = $second->getName();
        $this->oponente[$second->getName()] = $first->getName();
        
        $inv = $first->getInventory();
        $inv->addItem(Item::get(Item::DIAMOND_SWORD, 0, 1));
        $inv = $second->getInventory();
        $inv->addItem(Item::get(Item::DIAMOND_SWORD, 0, 1));
        
        $kbbox = new Vector3($this->kb->get('KB_X'), $this->kb->get('KB_Y'), $this->kb->get('KB_Z'));
        $first->knockBack($first, $this->kb->get('Velocidade'), $kbbox->x, $kbbox->z, $kbbox->y);
        $second->knockBack($second, $this->kb->get('Velocidade'), $kbbox->x, $kbbox->z, $kbbox->y);
        
        $first->addEffect(Effect::getEffect(11)->setAmplifier(50)->setDuration(99999999)); 
        $first->addEffect(Effect::getEffect(1)->setAmplifier(0)->setDuration(99999999)); 
        $second->addEffect(Effect::getEffect(1)->setAmplifier(0)->setDuration(99999999)); 
        $second->addEffect(Effect::getEffect(11)->setAmplifier(50)->setDuration(99999999));
        
        $first->sendMessage(' ');
        $first->sendMessage('§7The Duel Of Arena: '.$this->config->get('Cor_Eventos_Arena').' Has Begun');
        $first->sendMessage(' §7Maximum Time '.$this->config->get('Cor_Eventos_Arena').'' . gmdate('i:s', $this->config->get('Tempo_Maximo')) . '');
        $first->sendMessage('  §7Opponent: '.$this->config->get('Cor_Eventos_Arena').'' . $second->getName() . '');
        $first->sendMessage(' ');
        $first->sendPopup(''.$this->config->get('Cor_Eventos_Arena').'Boxing §7Started!');
        $second->teleport(new Position(...$this->data['pos2']));
        $second->sendMessage(' ');
        $second->sendMessage('§7The Duel Of Arena: '.$this->config->get('Cor_Eventos_Arena').'Has begun ');
        $second->sendMessage(' §7Maximum Time: '.$this->config->get('Cor_Eventos_Arena').'' . gmdate('i:s', $this->config->get('Tempo_Maximo')) . '');
        $second->sendMessage('  §7Opponent: '.$this->config->get('Cor_Eventos_Arena').'' . $first->getName() . '');
        $second->sendMessage(' ');
        $second->sendPopup(''.$this->config->get('Cor_Eventos_Arena').'Boxing §7Started!');
        
        $this->stat = self::RUN;
        $this->time = 0;
    }
    
    /**
      * Time over game.
      */
    public function closeGame()
    {
        foreach ($this->players as $player)  {
            $player->sendMessage(' ');
            $player->sendMessage('§8(§9Boxing§8) §r§cTime is up, there was no winner!');
            $player->sendMessage(' ');
            $player->sendPopup('§r§cTime is up, there was no winner!');
            $this->quit($player, false, true);
        }
    }
    
    /**
      * Reset game
      */
    public function close()
    {
        $this->stat = self::WAIT;
        $this->players = [];
        $this->time = $this->config->get('Tempo_Iniciar');
    }
    
    /**
      * Load game.
      */
    public function load()
    {
        $this->loader->getServer()->getPluginManager()->registerEvents($this, $this->loader);
        
        $level = $this->data['lobby'][3];
        
        if (!$this->loader->getServer()->isLevelLoaded($level)) {
            $this->loader->getServer()->loadLevel($level);
        }
        
        $this->data['lobby'][3]  = $this->loader->getServer()->getLevelByName($this->data['lobby'][3]);
        
        $level = $this->data['pos1'][3];
        if (!$this->loader->getServer()->isLevelLoaded($level)) {
            $this->loader->getServer()->loadLevel($level);
        }
        
        $this->data['pos1'][3] = $this->loader->getServer()->getLevelByName($this->data['pos1'][3]);
        $this->data['pos2'][3] = $this->loader->getServer()->getLevelByName($this->data['pos2'][3]);
        
        $this->register = true;
    }
    
    public function onDeath(PlayerDeathEvent $event)
    {
        $player = $event->getPlayer();
        
        if ($this->inArena($player) && $this->stat == self::RUN) {
        	 $event->setDrops([]);
             $event->setKeepInventory(false);
        	 $this->dead($player);
             return;
        }
    }

    public function onQuit(PlayerQuitEvent $event)
    {
    	$player = $event->getPlayer();
    
        if($this->inArena($player)) {
        	$this->stat == self::WAIT ? $this->quit($player, true, true) : $this->dead($player);
        }
    }
    
    /**
      * @param PlayerDropItemEvent $event
      */
    public function onDrop(PlayerDropItemEvent $event)
    {
    	$player = $event->getPlayer();
    
        if ($this->inArena($player)) {
        	 $event->setCancelled(true);
             return;
        }
    }
    
    /**
      * @param PlayerComm&&PreprocessEvent $event
      */
    public function onCommandPreprocess(PlayerCommandPreprocessEvent $event)
    {
    	$player = $event->getPlayer();
        $message = strtolower($event->getMessage());
        
        if ($this->inArena($player) && $message{0} == '/') {
        	 $event->setCancelled(true);
             $command = explode(' ', $message);
        	 if ($command[0] == '/sair') {
        	      $this->stat == self::WAIT ? $this->quit($player, true, true) : $this->dead($player);
                  return;
        	 } else {
        	      $player->sendMessage('§7Use '.$this->config->get('Cor_Eventos_Arena').'/sair §7To Exit.');
                  return;
        	}
        }
    }
    
    /**
     * @param PlayerInteractEvent $event
     */
    public function onInteract(PlayerInteractEvent $event)
    {
        $player = $event->getPlayer();
        $item = $event->getItem();

        if($this->inArena($player) && $this->stat == self::WAIT) {
            $event->setCancelled(true);
            
            if ($item->getCustomName() == '§r§l§cExit§r') {
                 $this->quit($player, true);
                 return;
             }
            
            if ($item->getCustomName() == '§r§l§eCredits§r') {
                 $player->sendPopup('§7Developer §l'.$this->config->get('Cor_Eventos_Arena').'Slxyer§r');
                 return;
             }
            
            if ($item->getCustomName() == '§r§l'.$this->config->get('Cor_Eventos_Arena').'Status§r') {
            $playerStatus = $this->status->get($player->getName());
            $hits = $playerStatus['Hits'] ?? 0;
            $vitorias = $playerStatus['Vitorias'] ?? 0;
            $derrotas = $playerStatus['Derrotas'] ?? 0;

            $player->sendMessage(' ');
            $player->sendMessage(' §7Your Total Hits: '.$this->config->get('Cor_Eventos_Arena').'' . $hits);
            $player->sendMessage(' §7Victories: '.$this->config->get('Cor_Eventos_Arena').'' . $vitorias);
            $player->sendMessage(' §7Defeats: '.$this->config->get('Cor_Eventos_Arena').'' . $derrotas);
            $player->sendMessage(' ');
            $player->sendPopup('§7Status §l'.$this->config->get('Cor_Eventos_Arena').'Visible§r§7 no chat');
            return;
             }
        }
    }

/**
 * @param EntityDamageEvent $event
 */
public function onDamage(EntityDamageEvent $event) {
    $entity = $event->getEntity();
    
    if ($event instanceof EntityDamageByEntityEvent) {
        $damager = $event->getDamager();

        if ($entity instanceof Player && $damager instanceof Player && $this->inArena($entity)) {
            if ($this->stat == self::WAIT) {
                $event->setCancelled(true);
                $damager->sendPopup('§cThe boxing match has not yet started.');
            } else {
            	
                $this->hits[$damager->getName()]++;
                $this->combos[$damager->getName()]++;
                $this->combos[$entity->getName()] = 0;

                    $playerStatus = $this->status->get($damager->getName());
                    $playerStatus['Hits'] += 1;
                    $this->status->set($damager->getName(), $playerStatus);
                    $this->status->save();

                if ($this->hits[$damager->getName()] >= $this->config->get('Hits_Maximo')) {
                    if ($this->stat == self::WAIT) {
                        $this->quit($entity, true, true);
                    } else {
                        $this->dead($entity);
                    }
                }
            }
            return;
        }
    }
}

    /**
     * Arena destruct.
     */
    public function __destruct()
    {
        HandlerList::unregisterAll($this);
    }
}