<?php

namespace bedwars\Inventory;

use pocketmine\network\protocol\UpdateBlockPacket;
use pocketmine\network\protocol\BlockEntityDataPacket;
use pocketmine\network\protocol\ContainerClosePacket;
use pocketmine\network\protocol\ContainerOpenPacket;

use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\ByteTag;

use pocketmine\inventory\BaseInventory;
use pocketmine\inventory\FakeBlockMenu;
use pocketmine\inventory\InventoryType;

use pocketmine\level\Position;

use pocketmine\block\Block;
use pocketmine\item\Item;

use pocketmine\scheduler\Task;

use pocketmine\tile\Tile;

use pocketmine\Server;
use pocketmine\Player;

class Inventory extends BaseInventory {
    
    /** @var event_callback */
    protected $event_callback_list = [];
	
    /**
     * slots
     * 
     * 0 1 2  3  4  5  6  7
     * 8 9 10 11 13 14 15 16
     * 17 18 19 20 21 22 23 24
     * 25 26
     */
    
    /**
    * @param Player $player
    * @param string $name 
    */
    public function __construct(Player $player, string $name = "Inventory") {
		$this->type = InventoryType::get(InventoryType::CHEST);
		$this->holder = new FakeBlockMenu($this, Position::fromObject($player->add(0, 2), $player->level));
		
		$this->size = $this->type->getDefaultSize();
		$this->title = $name;
		$this->name = $name; 
	}
	
    /**
     * @param Player $who
     */
    public function onOpen(Player $who) {
        if (!isset($who->customInventory)) {
            $who->customInventory = null;
        }
        if ($who->customInventory instanceof Inventory) {
            return false;
        }
        
        $pk = new UpdateBlockPacket();
	    $pk->x = $this->getHolder()->getX();
	    $pk->y = $this->getHolder()->getY();
		$pk->z = $this->getHolder()->getZ();
		$pk->blockId = Block::CHEST;
		$pk->blockData = 0;
	    $pk->flags = UpdateBlockPacket::FLAG_NONE;
	    $who->dataPacket($pk);
		
		$nbt = new CompoundTag();
		$nbt->id = new StringTag("id", Tile::CHEST);
		$nbt->CustomName = new StringTag("CustomName", $this->name);
		$nbt->x = new IntTag("x", $this->getHolder()->getX());
		$nbt->y = new IntTag("y", $this->getHolder()->getY());
		$nbt->z = new IntTag("z", $this->getHolder()->getZ());
		
		$nbtWrite = new NBT(NBT::LITTLE_ENDIAN);
		$nbtWrite->setData($nbt);
		
		$pk = new BlockEntityDataPacket();
		$pk->x = $this->getHolder()->getX();
		$pk->y = $this->getHolder()->getY();
		$pk->z = $this->getHolder()->getZ();
		$pk->namedtag = $nbtWrite->write(true);
		$who->dataPacket($pk);
		
		$inventory = $this;
        
        $this->process(function() use ($who, $inventory) {
            if ($who->customInventory instanceof Inventory) {
                $who->customInventory = null;
            }
            $inventory->viewers[spl_object_hash($who)] = $who;
            
            $pk = new ContainerOpenPacket();
	    	$pk->windowid = $who->getWindowId($inventory);
	    	$pk->type = $inventory->getType()->getNetworkType();
	    	$pk->slots = $inventory->getSize();
			$pk->x = $inventory->getHolder()->getX();
			$pk->y = $inventory->getHolder()->getY();
			$pk->z = $inventory->getHolder()->getZ();
	    	$who->dataPacket($pk);

	    	$inventory->sendContents($who);
	    	
	    	$who->customInventory = $inventory;
        }, 2);
	}
	
    /**
     * @param Player $who
     */
    public function onClose(Player $who) {
        if (!isset($who->customInventory)) {
            $who->customInventory = null;
        }
        
        if (!($who->customInventory instanceof Inventory)) {
            return false;
        }
        
        $block = $who->level->getBlock($this->getHolder());
        
        $pk = new UpdateBlockPacket();
	    $pk->x = $block->getX();
	    $pk->y = $block->getY();
	    $pk->z = $block->getZ();
	  	$pk->blockId = $block->getId();
    	$pk->blockData = $block->getDamage();
	    $pk->flags = UpdateBlockPacket::FLAG_NONE;
	    $who->dataPacket($pk);
		
        $inventory = $this;
        
	    $this->process(function() use ($who, $inventory) {
	        if (!isset($who->customInventory)) {
                $who->customInventory = null;
            }
	        if (!($who->customInventory instanceof Inventory)) {
	            return false;
	        }
            unset($inventory->viewers[spl_object_hash($who)]);
           
            $pk = new ContainerClosePacket();
            $pk->windowid = $who->getWindowId($inventory);
            $who->dataPacket($pk);
	        
	    	$who->customInventory = null;
        }, 4);
    }
    
    /**
     * @param int  $index
     * @param Item $item
     */
    public function setItem($index, Item $item) {
        $namedtag = $item->getNamedTag();
        
        if (!($namedtag instanceof CompoundTag)) {
            $namedtag = new CompoundTag();
        }
        
        $namedtag->EndBug = new ByteTag("EndBug", 1);
        $item->setNamedTag($namedtag);
        
        return parent::setItem($index, $item);
    }

    /**
     * @param callback $callback
     */
    public function addEvent(Player $player, $callback) {
        $this->event_callback_list[spl_object_hash($player)] = $callback;
    }
    
    /** 
     * @return array
    */
    public function getEvents() : array {
        return $this->event_callback_list;
    }
    
    /**
     * @param callback $callback
     */
    public function process($callback, $delay = 4) {
        Server::getInstance()->getScheduler()->scheduleDelayedTask(new class($callback) extends Task {
            /** @var callback */
            protected $callback;
            
            /**
             * @param callback $callback
             */
            public function __construct($callback) {
                $this->callback = $callback;
            }
            
            /** 
             * @param floar $currentTicks
             */
            public function onRun($currentTicks) {
                $callback = $this->callback;
                $callback();
            }
        }, $delay);
    }
}