#include "CharacterBoard.hpp"
#include "mof/Matrix3D.hpp"
#include "mof/ConsoleIO.hpp"
#include "mof/KeyFrameAnimation.hpp"
#include "mof/RotationAnimation3D.hpp"
#include "mof/TranslationAnimation3D.hpp"
#include "mof/ScalingAnimation3D.hpp"
#include "mof/Constant.hpp"
#include "mof/CascadingAnimation.hpp"
#include "mof/LoopAnimation.hpp"
#include "mof/Interpolation.hpp"
#include "mof/Font.hpp"
#include "mof/Vector2D.hpp"
#include "mof/utilities.hpp"
#include <vector>


struct ::CharacterBoard::Impl{
    std::vector<mof::Board*> list;
    mof::Animation<mof::Matrix3D>::Handler worldMatrix;
    mof::FrameNumber frame;

    Impl()
        :  frame(0) ,
        worldMatrix(mof::makeConstantHandler(mof::Matrix3D::createIdentity())) 
    {
    }

    ~Impl(){
        for(std::vector<mof::Board*>::iterator itr = list.begin() ;
            itr != list.end() ;
            ++itr ){
            delete *itr;
        }
        
    }
};

::CharacterBoard::CharacterBoard( const mof::tstring& text)
: m_pImpl(new Impl())
{
    

    mof::Board* pBoard;
    pBoard = new mof::Board();
    pBoard->setColor(mof::makeConstantHandler(mof::Color4f(0.3f , 0.3f , 1)));
    m_pImpl->list.push_back(pBoard);

    pBoard = new mof::Board();
    pBoard->setColor(mof::makeConstantHandler(mof::Color4f(0.6f , 0.6f , 1)));
    m_pImpl->list.push_back(pBoard);

    mof::Font font(mof::Font::MS_GOTHIC , 70);
    std::shared_ptr<mof::PixelMap> pPixelMap( font.createText(text) );
    mof::Vector2D sizeVec(pPixelMap->shape()[0] , pPixelMap->shape()[1]);
    std::shared_ptr<mof::Texture> pTexture(new mof::Texture( pPixelMap));
    float endX = static_cast<float>(pPixelMap->shape()[0]) / pTexture->getWidth();
    float endY = static_cast<float>(pPixelMap->shape()[1]) / pTexture->getHeight();

    pBoard = new mof::Board();
    pBoard->setColor(mof::makeConstantHandler(mof::Color4f(1 , 1 , 1)));
    pBoard->setTexture(pTexture);
    pBoard->setTextureCoordinates(mof::Rectangle<float>(0 , 0 , endX , endY));
    m_pImpl->list.push_back(pBoard);
}

::CharacterBoard::~CharacterBoard(){

}

void ::CharacterBoard::setFrameNumber(mof::FrameNumber frame){
    m_pImpl->frame = frame;
    for(std::vector<mof::Board*>::iterator itr = m_pImpl->list.begin() ;
        itr != m_pImpl->list.end() ;
        ++itr ){
            (*itr)->setFrameNumber(frame);
    }
}


void ::CharacterBoard::nextFrame(){
    setFrameNumber(m_pImpl->frame + 1);
}


void ::CharacterBoard::prevFrame(){
    if(m_pImpl->frame > 0)setFrameNumber(m_pImpl->frame - 1);
}


void ::CharacterBoard::update(){
    m_pImpl->frame++;
    for(std::vector<mof::Board*>::iterator itr = m_pImpl->list.begin() ;
        itr != m_pImpl->list.end() ;
        ++itr ){
            (*itr)->update();
    }
}


void ::CharacterBoard::setWorldMatrix(const mof::Animation<mof::Matrix3D>::Handler& handler){
    m_pImpl->worldMatrix = handler;
    std::vector<mof::Animation<mof::Matrix3D>::Handler> handlers;
    handlers.push_back( mof::makeConstantHandler(
        mof::Matrix3D::createTranslation(mof::Vector3D(0 , 0.5f , 0))
        ));
    handlers.push_back( handler);
    
    
    m_pImpl->list.at(0)->setWorldMatrix(
        mof::Animation<mof::Matrix3D>::Handler(
        new mof::CascadingAnimation<mof::Matrix3D , mof::Multiply<mof::Matrix3D>>(handlers.front() , handlers.back())
        )
        );

    handlers.clear();
    handlers.push_back( mof::makeConstantHandler(
        mof::Matrix3D::createTranslation(mof::Vector3D(0 , 0.5f , 0.01f))
        ));
    handlers.push_back(mof::makeConstantHandler(
        mof::Matrix3D::createRotation(mof::Vector3D(0 , mof::deg2rad(180) , 0))
        ));
    handlers.push_back(handler);

    m_pImpl->list.at(1)->setWorldMatrix(
        mof::Animation<mof::Matrix3D>::Handler(
        new mof::CascadingAnimation<mof::Matrix3D , mof::Multiply<mof::Matrix3D>>(handlers.front() , handlers.back())
        )
        );

    handlers.clear();
    handlers.push_back( mof::makeConstantHandler(
        mof::Matrix3D::createTranslation(mof::Vector3D(0 , 0.5f , -0.01f))
        ));
    handlers.push_back( handler);
    
    
    m_pImpl->list.at(2)->setWorldMatrix(
        mof::Animation<mof::Matrix3D>::Handler(
        new mof::CascadingAnimation<mof::Matrix3D , mof::Multiply<mof::Matrix3D>>(handlers.front() , handlers.back())
        )
        );
    
}


mof::Matrix3D CharacterBoard::getWorldMatrix() const{
    return m_pImpl->worldMatrix->getValue(m_pImpl->frame);
}

mof::Vector3D CharacterBoard::getPosition() const{
    mof::Matrix3D m = getWorldMatrix();
    return mof::Vector3D(m.at(3 , 0) , m.at(3 , 1) , m.at(3 , 2));
}

void ::CharacterBoard::append(std::list<mof::Board*>& list_){
    list_.insert(list_.begin() , m_pImpl->list.begin(), m_pImpl->list.end());
    
}

void ::CharacterBoard::setFinishColor(mof::FrameNumber delay , mof::FrameNumber interval){
    {
        std::pair<mof::FrameNumber , mof::Color4f> keyFrames[] = {
            std::pair<mof::FrameNumber , mof::Color4f>( delay , mof::Color4f(0.3f , 0.3f , 1) ) ,
            std::pair<mof::FrameNumber , mof::Color4f>( delay + interval , mof::Color4f(1 , 1 , 1) )
        };
        m_pImpl->list.at(0)->setColor(
            mof::Animation<mof::Color4f>::Handler(new mof::KeyFrameAnimation<mof::Color4f>(keyFrames[0] , keyFrames[1]))
            );
    }

    {
        std::pair<mof::FrameNumber , mof::Color4f> keyFrames[] = {
            std::pair<mof::FrameNumber , mof::Color4f>( delay , mof::Color4f(1 , 1 , 1) ) ,
            std::pair<mof::FrameNumber , mof::Color4f>( delay + interval , mof::Color4f(0.3f , 0.3f , 1) )
        };
        m_pImpl->list.at(2)->setColor(
            mof::Animation<mof::Color4f>::Handler(new mof::KeyFrameAnimation<mof::Color4f>(keyFrames[0] , keyFrames[1]))
            );
    }    

}


void ::CharacterBoard::setPartColor(mof::FrameNumber delay , mof::FrameNumber interval){
    {
        std::pair<mof::FrameNumber , mof::Color4f> keyFrames[] = {
            std::pair<mof::FrameNumber , mof::Color4f>( delay , mof::Color4f(0 , 1 , 1 , 1) ) ,
            std::pair<mof::FrameNumber , mof::Color4f>( delay + interval , mof::Color4f(0 , 1 , 1 , 1) )
        };
        m_pImpl->list.at(0)->setColor(
            mof::Animation<mof::Color4f>::Handler(new mof::KeyFrameAnimation<mof::Color4f>(keyFrames[0] , keyFrames[1]))
            );
    }

    {
        std::pair<mof::FrameNumber , mof::Color4f> keyFrames[] = {
            std::pair<mof::FrameNumber , mof::Color4f>( delay , mof::Color4f(0 , 1 , 1 , 1) ) ,
            std::pair<mof::FrameNumber , mof::Color4f>( delay + interval , mof::Color4f(0 , 1 , 1 , 1) )
        };
        m_pImpl->list.at(1)->setColor(
            mof::Animation<mof::Color4f>::Handler(new mof::KeyFrameAnimation<mof::Color4f>(keyFrames[0] , keyFrames[1]))
            );
    }

    {
        std::pair<mof::FrameNumber , mof::Color4f> keyFrames[] = {
            std::pair<mof::FrameNumber , mof::Color4f>( delay , mof::Color4f(0.8f , 0.5f , 0.2f) ) ,
            std::pair<mof::FrameNumber , mof::Color4f>( delay + interval , mof::Color4f(0.8f , 0.5f , 0.2f) )
        };
        m_pImpl->list.at(2)->setColor(
            mof::Animation<mof::Color4f>::Handler(new mof::KeyFrameAnimation<mof::Color4f>(keyFrames[0] , keyFrames[1]))
            );
    }    

}
