/*Copyright (C) 2010-2012 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 "AnimationGroup.hpp"

AnimationGroup::AnimationGroup() :
m_ixAnimation(-1), m_ixBackAnimation(0), m_blockingAnimation(false)
{
    
}

void AnimationGroup::draw()
{
    //Update matrix
    Drawable::pushMatrix();
    Drawable::updateMatrix();
    
    if (m_ixAnimation != -1) //An animation was started
        m_animationsList[m_ixAnimation].draw(); //Render it
    
    Drawable::popMatrix();
}

void AnimationGroup::addAnimation(const Animation &animation, int animationIx)
{
    if (animationIx == -1)
    {
        m_animationsList.push_back(animation);
        m_animationsList.back().setReverse(true);
    }
    else if (animationIx >= 0 && animationIx <= static_cast<int>(m_animationsList.size()) - 1)
    {
        m_animationsList[animationIx] = animation;
        m_animationsList[animationIx].setReverse(true);
    }
}

void AnimationGroup::removeAnimation(int animationIx)
{
    if (animationIx >= 0 && animationIx <= static_cast<int>(m_animationsList.size()) - 1)
        m_animationsList.erase(m_animationsList.begin() + animationIx);
}

void AnimationGroup::startAnimation(int animationIx, bool blocking, int ixBackAnimation, bool force)
{
    //Will be updated in update()
    if (animationIx >= 0 && animationIx <= static_cast<int>(m_animationsList.size()) - 1) //Requested animation exists
    {
        if (!m_blockingAnimation || force)
        {
            //Change animation
            m_ixAnimation = animationIx;
            if (blocking)
            {
                m_ixBackAnimation = ixBackAnimation;
                m_blockingAnimation = true;
            }
            
            //Reset animation
            m_animationsList[m_ixAnimation].goToTime(0);
        }
    }
}

void AnimationGroup::update(float frameTime)
{
    if (animationFinished())
    {
        if (m_blockingAnimation)
        {
            m_blockingAnimation = false;
            startAnimation(m_ixBackAnimation, false);
            
            return;
        }
        
        m_animationsList[m_ixAnimation].goToTime(0);
    }
    
    m_animationsList[m_ixAnimation].goForward(frameTime / 1000);
}

void AnimationGroup::updateOrigin()
{
    setYOrigin(m_animationsList[m_ixAnimation].getHeight());
}

//Get and set methods
bool AnimationGroup::animationBlocking() const
{
    return m_blockingAnimation;
}

bool AnimationGroup::animationFinished() const
{
    return m_animationsList[m_ixAnimation].getCurrentTime() == m_animationsList[m_ixAnimation].getDuration();
}

void AnimationGroup::setXFlip(bool flip)
{
    if (flip != getXFlip())
    {
        Drawable::setXFlip(flip);
        
        float newPos = 0;
        if (flip == true) newPos = -m_animationsList[m_ixAnimation].getWidth();
        
        for (unsigned int i = 0; i < m_animationsList.size(); i++)
            m_animationsList[i].setXPosition(newPos);
    }
}

Box AnimationGroup::getBox(int animationIx) const
{
    int ix = m_ixAnimation;
    if (animationIx >= 0 && animationIx <= static_cast<int>(m_animationsList.size()) - 1)
        ix = animationIx;
    
    Box animBox;
    animBox.left = (getXPosition() - getXOrigin()) + m_animationsList[ix].getXPosition();
    animBox.top = (getYPosition() - getYOrigin()) + m_animationsList[ix].getYPosition();
    animBox.width = m_animationsList[ix].getWidth();
    animBox.height = m_animationsList[ix].getHeight();
    
    return animBox;
}

//Static methods
AnimationGroup AnimationGroup::loadFromXML(const std::string &xmlPath, const std::string &animationsPath, TextureManager &textureManager)
{
    //AnimationGroup initialization
    AnimationGroup animationGroup;
    
    //TinyXML initialization
    TiXmlDocument xmlFile( xmlPath.c_str() );
    xmlFile.LoadFile();
    
    TiXmlHandle hdl(&xmlFile);
    hdl = hdl.FirstChildElement().FirstChildElement();
    
    TiXmlElement *xmlAnimations = hdl.FirstChildElement().Element();
    while (xmlAnimations)
    {
        Animation animation;
        
        //Get informations about the animation (path, frames number)
        std::string animationPath = animationsPath + xmlAnimations->Attribute("name") + ".png";
        Texture *texture = textureManager.getTexture(animationPath);
        
        float duration = 0;
        xmlAnimations->QueryFloatAttribute("duration", &duration);
        
        animation.setTexture(*texture);
        animation.setDuration(duration);
        
        if (!xmlAnimations->NoChildren())
        {
            TiXmlElement *xmlFrames = xmlAnimations->FirstChildElement();
            
            while(xmlFrames)
            {
                int x = 0, y = 0, width = 0, height = 0, time = 1;
                xmlFrames->QueryIntAttribute("x", &x);
                xmlFrames->QueryIntAttribute("y", &y);
                
                xmlFrames->QueryIntAttribute("width", &width);
                xmlFrames->QueryIntAttribute("height", &height);
                
                xmlFrames->QueryIntAttribute("time", &time);
                
                Box frameData(x, y, width, height);
                
                for (int i = 0; i < time; i++)
                    animation.addFrame(frameData);
                
                xmlFrames = xmlFrames->NextSiblingElement();
            }
        }
        
        //Add the animation to the AnimationGroup
        animationGroup.addAnimation(animation);
        
        xmlAnimations = xmlAnimations->NextSiblingElement();
    }
    
    return animationGroup;
}
