<?php

namespace Battles;

use Battles\Database\DBPDO;
use Exceptions\GameException;

class User
{
    protected $id = 0;
    protected $login = '<em>Некто</em>';
    protected $pass;
    protected $email = '<em>неизвестно</em>';
    protected $realname;
    protected $borndate;
    protected $info;
    protected $level = 0;
    protected $align = 0;
    protected $clan;
    protected $money = 0;
    protected $strength = 0;
    protected $dexterity = 0;
    protected $intuition = 0;
    protected $endurance = 0;
    protected $intelligence = 0;
    protected $wisdom = 0;
    protected $health;
    protected $mana;
    protected $ip;
    protected $session_id;
    protected $admin = 0;
    protected $enter_game;
    protected $room;
    protected $block;
    protected $shadow;
    // Удар кулаком всегда 1-2.
    protected $minDamage = 1;
    protected $maxDamage = 2;
    //Броня без предметов не существует.
    protected $headArmor = 0;
    protected $chestArmor = 0;
    protected $legArmor = 0;
    protected $free_stat_points = 0;
    private const STAT_MAXIMUM_AMOUNT = 40;
    private const ERROR_STAT_IS_MAXIMUM = 'Ошибка: Параметр достиг своего лимита!';
    private const ERROR_STAT_UNKNOWN = 'Ошибка: Неизвестный параметр!';
    // Пока несуществующие, для совместимости.
    protected $married = 'Someone или нет.';
    protected $experience = 200;
    protected $battle = 0;
    protected $in_tower = 0; // Скорее башню похороним чем запустим...
    protected $zayavka = 0;
    // Динамически рассчитываемые
    protected $maxHealth = 5;
    protected $maxMana = 5;
    protected static $db;

    public const INFO_CHAR_LIMIT = 1500;

    public function __construct($user)
    {
        self::$db = DBPDO::INIT();
        $user_query = self::$db->fetch('SELECT * FROM users WHERE id = ? OR login = ?', [$user, $user]);
        foreach ($this as $key => $value) {
            if (isset($user_query[$key])) {
                $this->$key = $user_query[$key];
            }
        }
        $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
     * @throws GameException
     */
    public function getStat($stat_name, $isMainWindow = 0):string
    {
        $allowed = ['strength', 'dexterity', 'intuition', 'endurance', 'intelligence', 'wisdom'];
        if (in_array($stat_name, $allowed)) {
            if ($this->free_stat_points && $isMainWindow && $this->$stat_name < self::STAT_MAXIMUM_AMOUNT) {
                return sprintf('%s <a href="/main.php?edit=%s&ups=%s">[+]</a>', $this->$stat_name, mt_rand(), $stat_name);
            } else {
                return $this->$stat_name;
            }
        } else {
            throw new GameException(self::ERROR_STAT_UNKNOWN);
        }
    }

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

    }

    protected function showStarSign(): ?string
    {
        /*
         * 1 aries
         * 2 taurus
         * 3 gemini
         * 4 cancer
         * 5 leo
         * 6 virgo
         * 7 libra
         * 8 scorpio
         * 9 sagittarios
         * 10 capricorn
         * 11 aquarius
         * 12 pisches
        */
        $zodiac = [
            356 => "10",
            326 => "09",
            296 => "08",
            266 => "07",
            235 => "06",
            203 => "05",
            172 => "04",
            140 => "03",
            111 => "02",
            78 => "01",
            51 => "12",
            20 => "11",
            0 => "10",
        ];
        $dayOfYear = date("z", strtotime($this->borndate));
        $isLeapYear = date("L", strtotime($this->borndate)); //Высокосный?
        if ($isLeapYear && $dayOfYear > 59) {
            --$dayOfYear;
        }
        foreach ($zodiac as $day => $sign) {
            if ($dayOfYear > $day) {
                break;
            }
        }
        return $sign ?? null;
    }

    public function getHealth(): string
    {
        return $this->health . '/' . $this->maxHealth;
    }

    public function getMana(): string
    {
        return $this->mana . '/' . $this->maxMana;
    }

    public static function setUserEffect(int $userId, int $type, string $name, int $time):bool
    {
        return self::$db->execute('INSERT INTO users_effects (owner_id, type, name, remaining_time) VALUES (?,?,?,?)',[$userId, $type, $name, $time]);
    }

    public static function removeUserEffect(int $userId, int $type):bool
    {
        if (self::$db->fetch('SELECT 1 FROM users_effects WHERE owner_id = ? AND type = ?', [$userId, $type])) {
            self::$db->execute('DELETE FROM users_effects WHERE owner_id = ? AND type = ?', [$userId, $type]);
        }
        return false;
    }

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

    /**
     * @param int $id
     */
    public function setId(int $id): void
    {
        $this->id = $id;
    }

    /**
     * @return string
     */
    public function getLogin(): string
    {
        return $this->login;
    }

    /**
     * @param string $login
     */
    public function setLogin(string $login): void
    {
        $this->login = $login;
    }

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

    /**
     * @param mixed $pass
     */
    public function setPass($pass): void
    {
        $this->pass = $pass;
    }

    /**
     * @return string
     */
    public function getEmail(): string
    {
        return $this->email;
    }

    /**
     * @param string $email
     */
    public function setEmail(string $email): void
    {
        $this->email = $email;
    }

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

    /**
     * @param mixed $realname
     */
    public function setRealname($realname): void
    {
        $this->realname = $realname;
    }

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

    /**
     * @param mixed $borndate
     */
    public function setBorndate($borndate): void
    {
        $this->borndate = $borndate;
    }

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

    /**
     * @param mixed $info
     */
    public function setInfo($info): void
    {
        $this->info = $info;
    }

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

    /**
     * @param int $level
     */
    public function setLevel(int $level): void
    {
        $this->level = $level;
    }

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

    /**
     * @param int $align
     */
    public function setAlign(int $align): void
    {
        $this->align = $align;
    }

    /**
     * @return string
     */
    public function getClan(): string
    {
        return $this->clan;
    }

    /**
     * @param int $clan
     */
    public function setClan(string $clan): void
    {
        $this->clan = $clan;
    }

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

    /**
     * @param int $money
     */
    public function setMoney(int $money): void
    {
        $this->money = $money;
    }

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

    /**
     * @param int $strength
     */
    public function setStrength(int $strength): void
    {
        $this->strength = $strength;
    }

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

    /**
     * @param int $dexterity
     */
    public function setDexterity(int $dexterity): void
    {
        $this->dexterity = $dexterity;
    }

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

    /**
     * @param int $intuition
     */
    public function setIntuition(int $intuition): void
    {
        $this->intuition = $intuition;
    }

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

    /**
     * @param int $endurance
     */
    public function setEndurance(int $endurance): void
    {
        $this->endurance = $endurance;
    }

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

    /**
     * @param int $intelligence
     */
    public function setIntelligence(int $intelligence): void
    {
        $this->intelligence = $intelligence;
    }

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

    /**
     * @param int $wisdom
     */
    public function setWisdom(int $wisdom): void
    {
        $this->wisdom = $wisdom;
    }

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

    /**
     * @param mixed $ip
     */
    public function setIp($ip): void
    {
        $this->ip = $ip;
    }

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

    /**
     * @param mixed $session_id
     */
    public function setSessionId($session_id): void
    {
        $this->session_id = $session_id;
    }

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

    /**
     * @param int $admin
     */
    public function setAdmin(int $admin): void
    {
        $this->admin = $admin;
    }

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

    /**
     * @param mixed $enter_game
     */
    public function setEnterGame($enter_game): void
    {
        $this->enter_game = $enter_game;
    }

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

    /**
     * @param mixed $room
     */
    public function setRoom($room): void
    {
        $this->room = $room;
    }

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

    /**
     * @param mixed $block
     */
    public function setBlock($block): void
    {
        $this->block = $block;
    }

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

    /**
     * @param mixed $shadow
     */
    public function setShadow($shadow): void
    {
        $this->shadow = $shadow;
    }

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

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

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

    /**
     * @param int $headArmor
     */
    public function setHeadArmor(int $headArmor): void
    {
        $this->headArmor = $headArmor;
    }

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

    /**
     * @param int $chestArmor
     */
    public function setChestArmor(int $chestArmor): void
    {
        $this->chestArmor = $chestArmor;
    }

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

    /**
     * @param int $legArmor
     */
    public function setLegArmor(int $legArmor): void
    {
        $this->legArmor = $legArmor;
    }

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

    /**
     * @param int $free_stat_points
     */
    public function setFreeStatPoints(int $free_stat_points): void
    {
        $this->free_stat_points = $free_stat_points;
    }

    /**
     * @return string
     */
    public function getMarried(): string
    {
        return $this->married;
    }

    /**
     * @param string $married
     */
    public function setMarried(string $married): void
    {
        $this->married = $married;
    }

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

    /**
     * @param int $experience
     */
    public function setExperience(int $experience): void
    {
        $this->experience = $experience;
    }

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

    /**
     * @param int $battle
     */
    public function setBattle(int $battle): void
    {
        $this->battle = $battle;
    }

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

    /**
     * @param int $in_tower
     */
    public function setInTower(int $in_tower): void
    {
        $this->in_tower = $in_tower;
    }

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

    /**
     * @param int $zayavka
     */
    public function setZayavka(int $zayavka): void
    {
        $this->zayavka = $zayavka;
    }

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

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

}