<?php

/*
 *
 *  ____            _        _   __  __ _                  __  __ ____
 * |  _ \ ___   ___| | _____| |_|  \/  (_)_ __   ___      |  \/  |  _ \
 * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
 * |  __/ (_) | (__|   <  __/ |_| |  | | | | | |  __/_____| |  | |  __/
 * |_|   \___/ \___|_|\_\___|\__|_|  |_|_|_| |_|\___|     |_|  |_|_|
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * @author PocketMine Team
 * @link http://www.pocketmine.net/
 *
 *
 */

declare(strict_types=1);

namespace pocketmine\item;

use pocketmine\block\Block;
use pocketmine\data\SavedDataLoadingException;
use pocketmine\entity\Location;
use pocketmine\entity\object\FireworkRocket as FireworkEntity;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\player\Player;
use pocketmine\utils\Utils;
use function array_map;
use function mt_rand;

class FireworkRocket extends Item{

	public const TAG_FIREWORK_DATA = "Fireworks"; //TAG_Compound
	protected const TAG_FLIGHT_TIME_MULTIPLIER = "Flight"; //TAG_Byte
	public const TAG_EXPLOSIONS = "Explosions"; //TAG_List

	protected int $flightTimeMultiplier = 1;

	/** @var FireworkRocketExplosion[] */
	protected array $explosions = [];

	/**
	 * Returns the value that will be used to calculate a randomized flight duration
	 * for the firework (equals the amount of gunpowder used in crafting the rocket).
	 *
	 * The higher this value, the longer the flight duration.
	 */
	public function getFlightTimeMultiplier() : int{
		return $this->flightTimeMultiplier;
	}

	/**
	 * Sets the value that will be used to calculate a randomized flight duration
	 * for the firework.
	 *
	 * The higher this value, the longer the flight duration.
	 *
	 * @return $this
	 */
	public function setFlightTimeMultiplier(int $multiplier) : self{
		if($multiplier < 1 || $multiplier > 127){
			throw new \InvalidArgumentException("Flight time multiplier must be in range 1-127");
		}
		$this->flightTimeMultiplier = $multiplier;

		return $this;
	}

	/**
	 * @return FireworkRocketExplosion[]
	 */
	public function getExplosions() : array{
		return $this->explosions;
	}

	/**
	 * @param FireworkRocketExplosion[] $explosions
	 *
	 * @return $this
	 */
	public function setExplosions(array $explosions) : self{
		Utils::validateArrayValueType($explosions, function(FireworkRocketExplosion $_) : void{});
		$this->explosions = $explosions;

		return $this;
	}

	public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{
		//TODO: this would be nicer if Vector3::getSide() accepted floats for distance
		$position = $blockClicked->getPosition()->addVector($clickVector)->addVector(Vector3::zero()->getSide($face)->multiply(0.15));

		$randomDuration = (($this->flightTimeMultiplier + 1) * 10) + mt_rand(0, 12);

		$entity = new FireworkEntity(Location::fromObject($position, $player->getWorld(), Utils::getRandomFloat() * 360, 90), $randomDuration, $this->explosions);
		$entity->setOwningEntity($player);
		$entity->setMotion(new Vector3(
			(Utils::getRandomFloat() - Utils::getRandomFloat()) * 0.0023,
			0.05,
			(Utils::getRandomFloat() - Utils::getRandomFloat()) * 0.0023
		));
		$entity->spawnToAll();

		$this->pop();

		return ItemUseResult::SUCCESS;
	}

	protected function deserializeCompoundTag(CompoundTag $tag) : void{
		parent::deserializeCompoundTag($tag);

		$fireworkData = $tag->getCompoundTag(self::TAG_FIREWORK_DATA);
		if($fireworkData === null){
			throw new SavedDataLoadingException("Missing firework data");
		}

		$this->setFlightTimeMultiplier($fireworkData->getByte(self::TAG_FLIGHT_TIME_MULTIPLIER, 1));

		if(($explosions = $fireworkData->getListTag(self::TAG_EXPLOSIONS, CompoundTag::class)) !== null){
			foreach($explosions as $explosion){
				$this->explosions[] = FireworkRocketExplosion::fromCompoundTag($explosion);
			}
		}
	}

	protected function serializeCompoundTag(CompoundTag $tag) : void{
		parent::serializeCompoundTag($tag);

		$fireworkData = CompoundTag::create();
		$fireworkData->setByte(self::TAG_FLIGHT_TIME_MULTIPLIER, $this->flightTimeMultiplier);
		$fireworkData->setTag(self::TAG_EXPLOSIONS, new ListTag(array_map(fn(FireworkRocketExplosion $e) => $e->toCompoundTag(), $this->explosions)));

		$tag->setTag(self::TAG_FIREWORK_DATA, $fireworkData);
	}
}