#include "widget/ImageTiler.hpp"
#include "mof/GraphicsDevice.hpp"
#include "mof/ConsoleIO.hpp"
#include "mof/Sprite.hpp"
#include "mof/utilities.hpp"
#include <vector>
#include <iomanip>

const int TIP_SIZE = 32;

struct ImageTiler::Impl{
    std::shared_ptr<mof::Texture> pTexture;
    std::vector<mof::VertexXYZRHWCUV> vertices;
    mof::Rectangle<float> region;
	mof::Color color_;
	bool visible_;
	bool update_;
    
    
    Impl()
        : region(0 , 0 , 0 , 0), visible_(true), color_(mof::createColor(255, 255, 255)),
		  update_(true)
    {}

    ~Impl(){}
        
    //{{{ _addVerticesOfInternal
    void _addVerticesOfInternal( )
    {
    
        const int tipMaxWidth = 32;
        const int tipMaxHeight = 32;
  
        float sumY = region.beginY;
        while(  sumY < region.endY  )
        {
            // regionをオーバーしないようにチップ高さを調節
            float tipHeight = tipMaxHeight;
            float tipTextureHeight = 0.25f;
            if(  tipMaxHeight > region.endY - sumY ) 
            {
                tipHeight = region.endY - sumY;
                tipTextureHeight = tipTextureHeight * tipHeight  / tipMaxHeight;
            }
            
            float sumX = region.beginX;
            while( sumX < region.endX  )
            {
                // regionをオーバーしないようにチップ幅を調節
                float tipWidth = tipMaxWidth;
                float tipTextureWidth = 0.25f;
                if( tipMaxWidth > region.endX - sumX ) 
                {
                    tipWidth = region.endX - sumX;
                    tipTextureWidth = tipTextureWidth * tipWidth  / tipMaxWidth;
                }
  
                mof::Sprite::append(
                    vertices , 
                    mof::Rectangle<float>(sumX , sumY , sumX + tipWidth , sumY + tipHeight ) ,
                    color_ ,
                    mof::Rectangle<float>( 0.25 , 0.25 , 0.25 + tipTextureWidth , 0.25 + tipTextureHeight )
                );
  
                sumX += tipWidth;
            } // while sumX
            sumY += tipHeight;
        } // while sumY

    }
    //}}}
    //{{{ _addVerticesOfExternal
    void _addVerticesOfExternal( )
    {
        const int tipMaxWidth = 32;
        const int tipMaxHeight = 32;
  
        float sumY = region.beginY;
        while(  sumY < region.endY  )
        {
            // regionをオーバーしないようにチップ高さを調節
            float tipHeight = tipMaxHeight;
            float tipTextureHeight = 0.25f;
            if( tipMaxHeight > region.endY - sumY ) 
            {
                tipHeight = region.endY - sumY;
                tipTextureHeight = tipTextureHeight * tipHeight  / tipMaxHeight;
            }
            
            mof::Sprite::append(
                vertices , 
                mof::Rectangle<float>( region.beginX - tipMaxWidth , sumY , region.beginX , sumY + tipHeight ) ,
                color_ ,
                mof::Rectangle<float>( 0.0 , 0.25 , 0.25 , 0.25 + tipTextureHeight )
            );
  
            mof::Sprite::append(
                vertices , 
                mof::Rectangle<float>( region.endX , sumY , region.endX + tipMaxWidth , sumY + tipHeight ) ,
                color_ ,
                mof::Rectangle<float>( 0.5 , 0.25 , 0.75 , 0.25 + tipTextureHeight )
            );

            sumY += tipHeight;
        } // while sumY

 
        float sumX = region.beginX;
        while(  sumX < region.endX  )
        {
            // regionをオーバーしないようにチップ幅を調節
            float tipWidth = tipMaxWidth;
            float tipTextureWidth = 0.25f;
            if( tipMaxWidth > region.endX - sumX ) 
            {
                tipWidth = region.endX - sumX;
                tipTextureWidth = tipTextureWidth * tipWidth  / tipMaxWidth;
            }
            
            mof::Sprite::append(
                vertices , 
                mof::Rectangle<float>( sumX , region.beginY - tipMaxHeight , sumX + tipWidth , region.beginY ) ,
                color_ ,
                mof::Rectangle<float>( 0.25 , 0.0 , 0.25 + tipTextureWidth , 0.25 )
            );
  
            mof::Sprite::append(
                vertices , 
                mof::Rectangle<float>( sumX , region.endY , sumX + tipWidth , region.endY + tipMaxHeight ) ,
                color_ ,
                mof::Rectangle<float>( 0.25 , 0.5 , 0.25 + tipTextureWidth , 0.75 )
            );

            sumX += tipWidth;
        } // while sumY

        mof::Sprite::append(
            vertices , 
            mof::Rectangle<float>( region.beginX - tipMaxWidth , region.beginY - tipMaxHeight , region.beginX , region.beginY ) ,
            color_ ,
            mof::Rectangle<float>( 0.0 , 0.0 , 0.25 , 0.25 )
        );
  
        mof::Sprite::append(
            vertices , 
            mof::Rectangle<float>( region.endX , region.beginY - tipMaxHeight , region.endX + tipMaxWidth , region.beginY ) ,
            color_ ,
            mof::Rectangle<float>( 0.5 , 0.0 , 0.75 , 0.25 )
        );

        mof::Sprite::append(
            vertices , 
            mof::Rectangle<float>( region.beginX - tipMaxWidth , region.endY  , region.beginX , region.endY + tipMaxHeight ) ,
            color_ ,
            mof::Rectangle<float>( 0.0 , 0.5 , 0.25 , 0.75 )
        );
  
        mof::Sprite::append(
            vertices , 
            mof::Rectangle<float>( region.endX , region.endY , region.endX + tipMaxWidth , region.endY + tipMaxHeight ) ,
            color_ ,
            mof::Rectangle<float>( 0.5 , 0.5 , 0.75 , 0.75 )
        );


    }
    //}}}
};

ImageTiler::ImageTiler( const std::shared_ptr<mof::Texture>& pTexture)
: m_pImpl(new Impl)
{
    m_pImpl->pTexture = pTexture;
}

ImageTiler::~ImageTiler(){}
//{{{ setRegion
void ImageTiler::setRegion( const mof::Rectangle<float>& region ){
    
    if(m_pImpl->region == region)return;
    m_pImpl->region = region;
    m_pImpl->update_ = true;
}
//}}}
//{{{ setColor
void ImageTiler::setColor(mof::Color color)
{
    if (m_pImpl->color_ == color) return;
    m_pImpl->color_ = color;
    m_pImpl->update_ = true;

}
//}}}
//{{{ draw
void ImageTiler::draw() const{
    if (!m_pImpl->visible_) return;
	if (m_pImpl->update_) {
		m_pImpl->vertices.clear();
    	m_pImpl->_addVerticesOfInternal();
    	m_pImpl->_addVerticesOfExternal();
		m_pImpl->update_ = false;
	}
    if (m_pImpl->vertices.empty()) return;

    mof::GraphicsDevice::setTexture( m_pImpl->pTexture.get( ) );
    mof::GraphicsDevice::drawVertexArray(
       m_pImpl->vertices.front() ,
       m_pImpl->vertices.back() ,
       mof::PRIMITIVE_TYPE_TRIANGLELIST
    );
}
//}}}
//{{{ setVisible
void ImageTiler::setVisible(bool visible) 
{
	m_pImpl->visible_ = visible;
}
//}}}
