/*Copyright (C) 2010-2023 Duncan Deveaux

This file is part of Hikou no mizu.

Hikou no mizu is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Hikou no mizu is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Hikou no mizu.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "PlayerStatus.hpp"
#include "Player/Player.hpp"

#include "Tools/BitTools.hpp"
#include "Tools/ByteTools.hpp"

PlayerStatus::PlayerStatus() : PhysicsObjectStatus(), m_health(100.f),
m_state(StateIndex::Default), m_attack(AttackIndex::Default),
m_movingState(MovingState::NotMoving), m_animationTime(0.f),
m_lookingLeft(false)
{

}

void PlayerStatus::readFromPlayer(const Player &player)
{
    PhysicsObjectStatus::readFromObject(player);

    //Health points
    m_health = player.getHealth();

    //State and attack
    m_state = player.getState();
    m_attack = player.getAttack();

    const Animation *animation = player.getAnimation();
    if (animation != nullptr)
        m_animationTime = animation->getCurrentTime();

    //Direction, moving state and size
    m_lookingLeft = player.looksLeft();
    m_movingState = player.getMovingState();
    m_size = Vector(player.getBox().width, player.getBox().height);

    //Weapon inventory
    m_inventoryStatus.readFromPlayer(player);
}

bool PlayerStatus::readFromBuffer(std::list<std::uint8_t> &bytes)
{
    if (bytes.size() < 2)
        return false;

    std::vector<std::uint8_t> stateAttack =
        BitTools::unpackUInt8(BitPack::_44, bytes.front());
    bytes.pop_front();

    std::vector<std::uint8_t> moveDirection =
        BitTools::unpackUInt8(BitPack::_21, bytes.front());
    bytes.pop_front();

    m_state = StateDescription::readStateIndex(stateAttack[0]);
    m_attack = StateDescription::readAttackIndex(stateAttack[1]);

    m_movingState = StateDescription::readMovingState(moveDirection[0]);
    m_lookingLeft = (moveDirection[1] == 0);

    return (m_state != StateIndex::None &&
            ByteTools::readFloat(bytes, m_health) &&
            ByteTools::readFloat(bytes, m_animationTime) &&
            ByteTools::readVector(bytes, m_size) &&
            PhysicsObjectStatus::readFromBuffer(bytes) &&
            m_inventoryStatus.readFromBuffer(bytes));
}

bool PlayerStatus::writeToBuffer(std::list<std::uint8_t> &dst) const
{
    std::uint8_t stateIx = StateDescription::writeStateIndex(m_state);
    std::uint8_t attackIx = StateDescription::writeAttackIndex(m_attack);
    if (stateIx >= 16 || attackIx >= 16) //No space to fit in 4 bits
        return false;

    std::uint8_t movingState = StateDescription::writeMovingState(m_movingState);
    std::uint8_t lookingLeft = m_lookingLeft ? 0 : 1;

    dst.push_back(BitTools::packUInt8(BitPack::_44, {stateIx, attackIx}));
    dst.push_back(BitTools::packUInt8(BitPack::_21,
                                      {movingState, lookingLeft}));

    ByteTools::writeFloat(m_health, dst);
    ByteTools::writeFloat(m_animationTime, dst);
    ByteTools::writeVector(m_size, dst);

    return (PhysicsObjectStatus::writeToBuffer(dst) &&
            m_inventoryStatus.writeToBuffer(dst));
}

float PlayerStatus::getHealth() const
{
    return m_health;
}

StateIndex PlayerStatus::getState() const
{
    return m_state;
}

AttackIndex PlayerStatus::getAttack() const
{
    return m_attack;
}

MovingState PlayerStatus::getMovingState() const
{
    return m_movingState;
}

float PlayerStatus::getAnimationTime() const
{
    return m_animationTime;
}

bool PlayerStatus::getLooksLeft() const
{
    return m_lookingLeft;
}

const Vector &PlayerStatus::getSize() const
{
    return m_size;
}

const InventoryStatus &PlayerStatus::getInventoryStatus() const
{
    return m_inventoryStatus;
}
