#include "mof/Menu.hpp"
#include "mof/Effect.hpp"
#include "mof/utilities.hpp"
#include "mof/LayoutManager.hpp"
#include <boost/scoped_array.hpp>
#include "mof/ConsoleIO.hpp"
#include "mof/WidgetView.hpp"
#include "mof/GraphicsDevice.hpp"
#include "mof/mofAnimations.hpp"


struct mof::Menu::Impl{
    mof::WidgetView* pBackgroundView;
    mof::LayoutManager* pLayout;
    std::shared_ptr<mof::Matrix2D> pTranslation;
    int width;
    int height;
    
    MenuItem* items;
    int size;
    int indicator;

    Impl( const mof::Rectangle<int>& _bounds)
        : pBackgroundView(NULL) , 
        width(_bounds.endX - _bounds.beginX) , height(_bounds.endY - _bounds.beginY) ,
        items(NULL) , size(0) , indicator(0) , pLayout(NULL) {
            pTranslation = std::shared_ptr<mof::Matrix2D>(
                new mof::Matrix2D(
                	mof::Matrix2D::createTranslation(
                		mof::Vector2D(_bounds.beginX , _bounds.beginY )
                		)
                	)
                );
    }

    ~Impl(){
        for(int i = 0 ; i < size ; ++i){
            delete items[i].pView;
        }
        delete[] items;
        delete pLayout;
        delete pBackgroundView;
    }


    
};


mof::Menu::Menu
(
    mof::WidgetView* pBackgroundView ,
    const mof::Menu::MenuItem & front ,
    const mof::Menu::MenuItem& back ,
    const mof::Rectangle<int>& bounds ,
    mof::LayoutManager* pLayout
)
: m_pImpl(new Impl(bounds))
{
    m_pImpl->pLayout = pLayout;
    m_pImpl->pBackgroundView = pBackgroundView;

    m_pImpl->size = &back - &front + 1;
    if(m_pImpl->size <= 0)throw std::invalid_argument("the length of front-back is less than zero");

    m_pImpl->items = new MenuItem[m_pImpl->size];
    boost::scoped_array<mof::Rectangle<int> > boundList(new mof::Rectangle<int>[m_pImpl->size]);
    
    for(int i = 0 ; i < m_pImpl->size ; ++i){
        m_pImpl->items[i] = (&front)[i];
        mof::Rectangle<int> bounds = m_pImpl->items[i].pView->initialize();
        boundList[i] = bounds;
    }

    m_pImpl->pLayout->replace(m_pImpl->width , m_pImpl->height , boundList[0] , boundList[m_pImpl->size-1]);

    //TODO: LayoutAnimation‚ð‚Â‚­‚é
    for(int i = 0 ; i < m_pImpl->size ; ++i){
        const mof::Animation<mof::Matrix2D>::Handler children[] = 
        {
            mof::makeParametricHandler(m_pImpl->pTranslation) ,
            mof::makeConstantHandler
            (
                mof::Matrix2D::createTranslation( m_pImpl->pLayout->getPosition(i))
            )
        };
        
        m_pImpl->items[i].pView->setPosition
        (
            mof::Animation<mof::Matrix2D>::Handler
            (
                new mof::CascadingAnimation<mof::Matrix2D>(children[0] , children[1])
            )
        );
    }
    m_pImpl->pBackgroundView->setBounds(bounds);
    
}


mof::Menu::~Menu(){

}

void mof::Menu::show(){
    m_pImpl->pBackgroundView->show();
    for(int i = 0 ; i < m_pImpl->size ; ++i){
        m_pImpl->items[i].pView->show();
    }
}

void mof::Menu::close(){
    m_pImpl->pBackgroundView->close();
    for(int i = 0 ; i < m_pImpl->size ; ++i){
        m_pImpl->items[i].pView->close();
    }
}

void mof::Menu::up(){
    mof::Menu::MenuItem* pItem = &m_pImpl->items[m_pImpl->indicator];
    pItem->pView->blur();

    m_pImpl->indicator--;
    if(m_pImpl->indicator == -1)m_pImpl->indicator = m_pImpl->size -1;

    pItem = &m_pImpl->items[m_pImpl->indicator];
    pItem->pView->focus();
}

void mof::Menu::down(){
    mof::Menu::MenuItem* pItem = &m_pImpl->items[m_pImpl->indicator];
    pItem->pView->blur();

    m_pImpl->indicator++;
    if(m_pImpl->indicator == m_pImpl->size)m_pImpl->indicator = 0;

    pItem = &m_pImpl->items[m_pImpl->indicator];
    pItem->pView->focus();
}



void mof::Menu::left() {

}

void mof::Menu::right(){

}

void mof::Menu::performAction() const{
    if(!m_pImpl->items[m_pImpl->indicator].action)return;
    m_pImpl->items[m_pImpl->indicator].action();
}

void mof::Menu::draw() const{
    m_pImpl->pBackgroundView->draw();
    mof::GraphicsDevice::setViewport(m_pImpl->pBackgroundView->getBounds());
    for(int i = 0 ; i < m_pImpl->size ; ++i){
        m_pImpl->items[i].pView->getEffect()->draw();
    }
    mof::GraphicsDevice::setViewport(mof::Rectangle<int>(0 , 0 , 640 , 480));//TODO
}


void mof::Menu::update() {
    m_pImpl->pBackgroundView->update();
    for(int i = 0 ; i < m_pImpl->size ; ++i){
        m_pImpl->items[i].pView->update();
    }
}
