<?php
# Date: 03.02.2021 (11:05)

namespace Battles;

use Battles\Database\Db;
use Exceptions\GameException;

class UserStats extends User
{
    protected $strength;
    protected $dexterity;
    protected $intuition;
    protected $endurance;
    protected $intelligence;
    protected $wisdom;
    protected $health;
    protected $mana;
    protected $free_stat_points;
    private const STAT_MAXIMUM_AMOUNT = 40;
    private const ERROR_STAT_IS_MAXIMUM = 'Ошибка: Параметр достиг своего лимита!';
    private const ERROR_STAT_UNKNOWN = 'Ошибка: Неизвестный параметр!';

    //// Неизменяемые для игрока(!) переменные.
    // Удар кулаком всегда 1-2.
    protected int $minDamage = 1;
    protected int $maxDamage = 2;
    // Природная броня всегда 0.
    // Зачем их три, если во всех формулах она одна?
    protected int $headArmor = 0;
    protected int $chestArmor = 0;
    protected int $legArmor = 0;
    // Динамически рассчитываемые
    protected int $maxHealth;
    protected int $maxMana;

    /**
     * UserStats constructor.
     *
     * @param $user
     */
    public function __construct($user)
    {
        parent::__construct($user);
        $this->maxHealth = round(($this->endurance * 3) + ($this->endurance / 2) * ($this->level - 1) + ($this->endurance / 5) * (($this->level - 1) * ($this->level - 2) / 2));
        $this->maxMana = round(($this->wisdom * 3) + ($this->wisdom / 2) * ($this->level - 1) + ($this->wisdom / 5) * (($this->level - 1) * ($this->level - 2) / 2));
    }

    /**
     * Отдаёт информацию о базовом(!) стате.
     *
     * @param     $stat_name - имя стата. Может принимать значения 'strength', 'dexterity', 'intuition',
     *                          'endurance', 'intelligence', 'wisdom'.
     * @param int $isMainWindow - переключатель "главного окна". Если включить, дополнительно будет показывать ссылку
     *                          на повышение стата на 1, при условии наличия свободных очков статов.
     *
     * @return string
     */
    public function getStat($stat_name, int $isMainWindow = 0): string
    {
        if (!in_array($stat_name, ['strength', 'dexterity', 'intuition', 'endurance', 'intelligence', 'wisdom'])) {
            return self::ERROR_STAT_UNKNOWN;
        }
        if ($this->free_stat_points && $isMainWindow && $this->$stat_name < self::STAT_MAXIMUM_AMOUNT) {
            $this->$stat_name .= " <a href='/main.php?edit=" . mt_rand() . "&ups=$stat_name'>[+]</a>";
        }
        return $this->$stat_name;

    }

    /**
     * Повышает один из выбранных статов на 1, но не выше self::STAT_MAXIMUM_AMOUNT при условии наличия свободных очков
     * статов.
     *
     * @param string $stat_name - имя стата. Может принимать значения 'strength', 'dexterity', 'intuition',
     *                          'endurance', 'intelligence', 'wisdom'.
     *
     * @throws GameException
     */
    public function addOnePointToStat(string $stat_name)
    {
        if (!in_array($stat_name, ['strength', 'dexterity', 'intuition', 'endurance', 'intelligence', 'wisdom'])) {
            throw new GameException(self::ERROR_STAT_UNKNOWN);
        }
        if ($this->free_stat_points <= 0 || $this->$stat_name >= self::STAT_MAXIMUM_AMOUNT) {
            throw new GameException(self::ERROR_STAT_IS_MAXIMUM);
        } else {
            $query = "UPDATE users SET {$stat_name} = {$stat_name} + 1, free_stat_points = free_stat_points - 1 WHERE id = ?";
            Db::getInstance()->execute($query, $this->id);
        }
    }

    public function getMaxWeight(): int
    {
        return $this->strength * 4;
    }

    /**
     * @return mixed
     */
    public function getHealth()
    {
        return $this->health;
    }

    /**
     * @return mixed
     */
    public function getMana()
    {
        return $this->mana;
    }

    /**
     * @return mixed
     */
    public function getFreeStatPoints()
    {
        return $this->free_stat_points;
    }

    /**
     * @return float
     */
    public function getMaxHealth(): float
    {
        return $this->maxHealth;
    }

    /**
     * @return float
     */
    public function getMaxMana(): float
    {
        return $this->maxMana;
    }

    /**
     * @return int
     */
    public function getHeadArmor(): int
    {
        return $this->headArmor;
    }

    /**
     * @return int
     */
    public function getChestArmor(): int
    {
        return $this->chestArmor;
    }

    /**
     * @return int
     */
    public function getLegArmor(): int
    {
        return $this->legArmor;
    }

    public function getFullStats(): object
    {
        $stats = Db::getInstance()->ofetch("
        select
               strength,
               dexterity,
               intuition,
               endurance,
               intelligence,
               wisdom
        from users where id = $this->id");
        $itemBonuses = Db::getInstance()->ofetch("
        select
               sum(add_strength) as item_strength,
               sum(add_dexterity) as item_dexterity,
               sum(add_intuition) as item_intuition,
               sum(add_endurance) as item_endurance,
               sum(add_intelligence) as item_intelligence,
               sum(add_wisdom) as item_wisdom,
               sum(add_accuracy) as item_accuracy,
               sum(add_evasion) as item_evasion,
               sum(add_criticals) as item_criticals,
               sum(add_min_physical_damage) as item_min_physical_damage,
               sum(add_max_physical_damage) as item_max_physical_damage
        from inventory where dressed_slot != 0 and owner_id = $this->id");
        $effectBonuses = Db::getInstance()->ofetch("
        select
               sum(mod_strength) as effect_strength,
               sum(mod_dexterity) as effect_dexterity,
               sum(mod_intuition) as effect_intuition,
               sum(mod_endurance) as effect_endurance,
               sum(mod_intelligence) as effect_intelligence,
               sum(mod_wisdom) as effect_wisdom
        from users_effects where owner_id = $this->id");
        $obj = (object)[];
        $obj->strength = max(0,$stats->strength + $itemBonuses->item_strength + $effectBonuses->effect_strength);
        $obj->dexterity = max(0,$stats->dexterity + $itemBonuses->item_dexterity + $effectBonuses->effect_dexterity);
        $obj->intuition = max(0,$stats->intuition + $itemBonuses->item_intuition + $effectBonuses->effect_intuition);
        $obj->endurance = max(0,$stats->endurance + $itemBonuses->item_endurance + $effectBonuses->effect_endurance);
        $obj->intelligence = max(0,$stats->intelligence + $itemBonuses->item_intelligence + $effectBonuses->effect_intelligence);
        $obj->wisdom = max(0,$stats->wisdom + $itemBonuses->item_wisdom + $effectBonuses->effect_wisdom);
        $obj->accuracy = max(0, $itemBonuses->item_accuracy);
        $obj->evasion = max(0, $itemBonuses->item_evasion);
        $obj->criticals = max(0, $itemBonuses->item_criticals);
        $obj->min_physical_damage = max($this->minDamage, $this->minDamage + $itemBonuses->item_min_physical_damage);
        $obj->max_physical_damage = max($this->maxDamage, $this->maxDamage + $itemBonuses->item_max_physical_damage);
        return $obj;
    }

    public function levelUp(): string
    {
        $this->level += 1;
        $this->free_stat_points += 2;
        $this->saveStats();
        Chat::sendSys('Внимание, вы получили ' . $this->level . 'уровень. Доступны очки распределения параметров.');
        return 'Персонаж перешёл на ' . $this->level . 'уровень.';
    }

    /** Сохраняет в базу актуальные статы, здоровье, ману, свободные очки статов и уровень.
     *
     */
    private function saveStats()
    {
        $query = 'update users set strength = ?, dexterity = ?, intuition = ?, endurance = ?,
                 intelligence = ?, wisdom = ?, health = ?, mana = ?, free_stat_points = ?, 
                 level = ? where id = ?';
        $vals = [
            $this->strength, //set
            $this->dexterity,
            $this->intuition,
            $this->endurance,
            $this->intelligence,
            $this->wisdom,
            $this->health,
            $this->mana,
            $this->free_stat_points,
            $this->level,
            $this->id //where
        ];
        Db::getInstance()->execute($query, $vals);
    }
}