/*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 "PlayerDrawer.hpp"
#include "Engines/Resources/TextureManager.hpp"
#include "Player/Player.hpp"
#include "Player/PlayerMoves.hpp" ///< State

//////////////////
///PlayerDrawer///
//////////////////
PlayerDrawer::PlayerDrawer() : m_player(nullptr)
{

}

void PlayerDrawer::attachTo(const Player &player,
                            TextureManager &textureManager, bool showHitboxes)
{
    m_player = &player;

    //Init moves
    m_movesDraw.load(m_player->getStateInfo(), textureManager, showHitboxes);
    m_movesDraw.lockToGrid(false);

    //Init shadow
    constexpr float shadowRatio = .1f;

    m_shadowSize.x = m_player->getBox().width * 1.1f;
    m_shadowSize.y = m_shadowSize.x * shadowRatio;

    m_shadow = Polygon::circle(m_shadowSize.x / 2.f);
    m_shadow.lockToGrid(false);
    m_shadow.setBorderSize(0.f);
    m_shadow.setYScale(shadowRatio);
}

void PlayerDrawer::draw()
{
    update();
    m_shadow.draw();
    m_movesDraw.draw();
}

void PlayerDrawer::update()
{
    if (m_player == nullptr)
        return;

    //Update moves
        //State
    m_movesDraw.update(m_player->getState(), m_player->getAttack());

        //Flip
    m_movesDraw.setXFlip(m_player->looksLeft());

        //Origin
    const Animation *playerAnimation = m_player->getAnimation();
    if (playerAnimation != nullptr)
    {
        Box bodyBox;
        playerAnimation->getBodyBox(bodyBox);

        m_movesDraw.setOrigin(bodyBox.left * playerAnimation->getXScale(),
            (bodyBox.top + bodyBox.height) * playerAnimation->getYScale());
    }

        //Position
    const Box &playerBox = m_player->getBox();
    if (!m_player->looksLeft()) m_movesDraw.setXPosition(playerBox.left);
    else m_movesDraw.setXPosition(playerBox.left + playerBox.width);

    m_movesDraw.setYPosition(playerBox.top + playerBox.height);

    //Update shadow
    float shadowDist = m_player->shadowProjection();
    if (shadowDist < 0.f) shadowDist = 0.f;

    m_shadow.setPosition(
        playerBox.left - (m_shadowSize.x - playerBox.width) / 2.f,
        playerBox.top + playerBox.height + shadowDist - m_shadowSize.y / 2.f);
    m_shadow.setUniformColor(
        Color(0, 0, 0, static_cast<GLubyte>(90.f / (shadowDist/50.f + 1.f))));
}

///////////////////////
///PlayerMovesDrawer///
///////////////////////
void PlayerDrawer::MovesDrawer::draw()
{
    //Update matrix
    Drawable::pushMatrix();
    Drawable::updateMatrix();

    if (m_attack != AttackIndex::Default)
        m_statesDraw[m_state].attacksDraw[m_attack].draw();
    else
        m_statesDraw[m_state].animationDraw.draw();

    Drawable::popMatrix();
}

void PlayerDrawer::MovesDrawer::update(StateIndex state, AttackIndex attack)
{
    m_state = state;
    m_attack = attack;
}

void PlayerDrawer::MovesDrawer::load(
    const std::map<StateIndex, const State*> &stateInfo,
    TextureManager &textureManager, bool showHitboxes)
{
    std::map<StateIndex, const State*>::const_iterator it;
    for (it = stateInfo.begin(); it != stateInfo.end(); ++it)
    {
        const State *state = it->second;
        if (state == nullptr)
            continue;

        const Animation &stateAnim = state->animation;
        const std::map<AttackIndex, Attack> &stateAttacks = state->attacks;

        //Load state default animation
        Texture &stateAnimTexture =
            textureManager.getTexture(stateAnim.getPath());
        m_statesDraw[it->first].animationDraw.attachTo(stateAnim,
                                                       stateAnimTexture);

        //Load state attacks
        std::map<AttackIndex, Attack>::const_iterator it_attacks;
        for (it_attacks = stateAttacks.begin();
             it_attacks != stateAttacks.end();
             ++it_attacks)
        {
            Texture &attackTexture = textureManager.getTexture(
                it_attacks->second.getAnimation().getPath());
            m_statesDraw[it->first].attacksDraw[it_attacks->first].attachTo(
                it_attacks->second, attackTexture, showHitboxes);
        }
    }
}
