/*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 "WeaponEtherDrawer.hpp"
#include "Weapon/Weapon.hpp"
#include "Engines/Resources/TextureManager.hpp"

#include "Tools/Log.hpp"
#include "Tools/Format.hpp"

///////////////////////
///WeaponEtherDrawer///
///////////////////////
WeaponEtherDrawer::WeaponEtherDrawer() : m_textureManager(nullptr)
{

}

void WeaponEtherDrawer::draw()
{
    std::map<const Weapon*, WeaponDrawer>::iterator it;
    for (it = m_weaponsDraw.begin(); it != m_weaponsDraw.end(); ++it)
    {
        it->second.update();
        it->second.draw();
    }
}

void WeaponEtherDrawer::attachTo(WeaponEther &ether,
                                 TextureManager &textureManager)
{
    m_textureManager = &textureManager;
    ether.setCallbackReceiver(this);
}

void WeaponEtherDrawer::weaponAdded(const Weapon &weapon)
{
    if (m_textureManager != nullptr)
    {
        WeaponEtherDrawer::WeaponDrawer drawer;
        drawer.attachTo(weapon,
                        m_textureManager->getTexture(weapon.getTexturePath()));
        drawer.lockToGrid(false);

        m_weaponsDraw[&weapon] = drawer;
    }
}

void WeaponEtherDrawer::weaponRemoved(const Weapon &weapon)
{
    std::map<const Weapon*, WeaponDrawer>::iterator it =
        m_weaponsDraw.find(&weapon);

    if (it != m_weaponsDraw.end())
        m_weaponsDraw.erase(it);
    else
    {
        Log::err("A weapon was removed but its associated drawer was not found."
                 " Clearing all drawers.");
        m_weaponsDraw.clear();
    }
}

//////////////////
///WeaponDrawer///
//////////////////
void WeaponEtherDrawer::WeaponDrawer::draw()
{
    //Update matrix
    Drawable::pushMatrix();
    Drawable::updateMatrix();

    m_sprite.draw();

    Drawable::popMatrix();
}

void WeaponEtherDrawer::WeaponDrawer::update()
{
    if (m_weapon != nullptr)
    {
        m_sprite.setRotation(m_weapon->getAngle());

        const Box &weaponBox = m_weapon->getBox();
        setXPosition(weaponBox.left + weaponBox.width / 2.f);
        setYPosition(weaponBox.top + weaponBox.height / 2.f);
    }
}

void WeaponEtherDrawer::WeaponDrawer::attachTo(const Weapon &weapon,
                                               Texture &texture)
{
    m_weapon = &weapon;

    //Initialize the sprite from the source texture
    const Box &textureSource = weapon.getSourceBox();
    const float textureWidth = static_cast<float>(texture.getWidth()),
                textureHeight = static_cast<float>(texture.getHeight());

    Box subRect;
    subRect.left = (textureWidth > 0.f) ?
        static_cast<float>(textureSource.left) / textureWidth : 0.f;
    subRect.top = (textureHeight > 0.f) ?
        static_cast<float>(textureSource.top) / textureHeight : 0.f;
    subRect.width = (textureWidth > 0.f) ?
        static_cast<float>(textureSource.width) / textureWidth : 1.f;
    subRect.height = (textureHeight > 0.f) ?
        static_cast<float>(textureSource.height) / textureHeight : 1.f;

    m_sprite.setTexture(texture);
    m_sprite.setSubRect(subRect);
    m_sprite.lockToGrid(false);

    //Scale the sprite to fit the weapon size
    const Box &weaponBox = weapon.getBox();
    if (m_sprite.getWidth() > 0.f && m_sprite.getHeight() > 0.f)
    {
        m_sprite.setXScale(weaponBox.width / m_sprite.getWidth());
        m_sprite.setYScale(weaponBox.height / m_sprite.getHeight());
    }

    //Center the sprite rotational origin
    m_sprite.setOrigin(weaponBox.width / 2.f, weaponBox.height / 2.f);
}
