#include "mof/Sprite.hpp"
#include "mof/private/GraphicsDeviceImpl.hpp"
#include "mof/stream/Constant.hpp"
#include "mof/ConsoleIO.hpp"
#include "mof/Font.hpp"

namespace mof
{
    struct Sprite::Impl{
	    std::shared_ptr<Texture> pTexture;
		mof::Vector2D preferred_size_;
		bool visible_;

	    Impl()
			: visible_(true)
	    {
	    }

	    ~Impl(){
    	}
    };

    Sprite::Sprite
    (
	    const Rectangle<int>& rect ,
	    const std::shared_ptr<Texture>& pTexture , 
	    const Rectangle<float>& tRect 
	)
    : 
        m_pImpl( new Impl( ) ) ,
        Component2D( rect ) ,
        TextureComponent( tRect ) 
    {
	    m_pImpl->pTexture = pTexture;
		m_pImpl->preferred_size_ = mof::Vector2D(rect.getWidth(), rect.getHeight());
    }


    Sprite::Sprite
    (
	    const std::shared_ptr<Texture>& pTexture , 
	    const Rectangle<float>& tRect 
	)
    : 
        m_pImpl(new Impl()) ,
        Component2D( Rectangle<int>( 0 , 0 , pTexture->getWidth() , pTexture->getHeight() ) ) ,
        TextureComponent( tRect ) 
    {
	    m_pImpl->pTexture = pTexture;
		m_pImpl->preferred_size_ = mof::Vector2D(pTexture->getWidth(), pTexture->getHeight());
    }


    Sprite::~Sprite()
    {
    }   

    void Sprite::update()
    {
        m_textureStream.update();
        m_colorStream.update();
        m_sizeStream.update();
        m_positionStream.update();
    }

	void Sprite::setVisible(bool visible)
	{
		m_pImpl->visible_ = visible;
	}

    void Sprite::draw() const
    {
		if (!m_pImpl->visible_) return;
	    static VertexXYZRHWCUV vertices[4];
	
	    Rectangle<float> textureCoordinates = m_textureStream.value( );
	    Color color = m_colorStream.value( ).toColorCode( );
        
        Vector2D position = m_positionStream.value( );
        Vector2D size = m_sizeStream.value( );
        Vector2D begin = Vector2D( position.x , position.y );
	    Vector2D end = begin + Vector2D( size.x , size.y );
        
        vertices[0].x = begin.x -0.5f;
	    vertices[0].y = begin.y -0.5f;
	    vertices[0].z = 0;
	    vertices[0].rhw = 1;
	    vertices[0].color = color;
	    vertices[0].tu = textureCoordinates.beginX;
	    vertices[0].tv = textureCoordinates.beginY;

	    vertices[1].x = end.x -0.5f;
	    vertices[1].y = begin.y -0.5f;
	    vertices[1].z = 0;
	    vertices[1].rhw = 1;
	    vertices[1].color = color;
	    vertices[1].tu = textureCoordinates.endX;
	    vertices[1].tv = textureCoordinates.beginY;

	    vertices[2].x = begin.x -0.5f;
    	vertices[2].y = end.y -0.5f;
    	vertices[2].z = 0;
    	vertices[2].rhw = 1;
    	vertices[2].color = color;
    	vertices[2].tu = textureCoordinates.beginX;
    	vertices[2].tv = textureCoordinates.endY;

    	vertices[3].x = end.x -0.5f;
    	vertices[3].y = end.y -0.5f;
    	vertices[3].z = 0;
    	vertices[3].rhw = 1;
	    vertices[3].color = color;
	    vertices[3].tu = textureCoordinates.endX;
    	vertices[3].tv = textureCoordinates.endY;

    	GraphicsDevice::setTexture(m_pImpl->pTexture.get());
    	GraphicsDevice::drawVertexArray(vertices[0] , vertices[3] , PRIMITIVE_TYPE_TRIANGLESTRIP);
        
    }

    void Sprite::setPositionStream( const Vector2DStream& stream )
    {
        m_positionStream = stream;
    }
    
    void Sprite::setSizeStream( const Vector2DStream& stream )
    {
        m_sizeStream = stream;
    }
	
	void Sprite::setColorStream( const ColorStream& stream )
    {
        m_colorStream = stream;
    }


	mof::Vector2D Sprite::getPreferredSize() const
	{
		return m_pImpl->preferred_size_;
	}

    void Sprite::append
    (
	    std::vector<VertexXYZRHWCUV>& list ,
	    const Rectangle<float>& region , 
    	Color color , 
    	const Rectangle<float>& textureCoordinates
	)
    {
	    list.push_back
        (
	    	VertexXYZRHWCUV(Vector2D(region.beginX , region.beginY) , 
	    	color , textureCoordinates.beginX , textureCoordinates.beginY)
	    );

	    list.push_back
        (
		    VertexXYZRHWCUV(Vector2D(region.endX , region.beginY) , 
		    color , textureCoordinates.endX , textureCoordinates.beginY)
		);

	    list.push_back
        (
		    VertexXYZRHWCUV(Vector2D(region.beginX , region.endY) , 
		    color , textureCoordinates.beginX , textureCoordinates.endY)
		);

	    list.push_back
        (
		    VertexXYZRHWCUV(Vector2D(region.beginX , region.endY) , 
	    	color , textureCoordinates.beginX , textureCoordinates.endY)
		);

	    list.push_back
        (
		    VertexXYZRHWCUV(Vector2D(region.endX , region.beginY) , 
		    color , textureCoordinates.endX , textureCoordinates.beginY)
		);


	    list.push_back
        (
		    VertexXYZRHWCUV(Vector2D(region.endX , region.endY) , 
		    color , textureCoordinates.endX , textureCoordinates.endY)
		);

    }

    Sprite* Sprite::createTextSprite( const Font& font , const tstring& text)
    {
	    std::shared_ptr<PixelMap> pPixelMap( font.createText(text) );
	    std::shared_ptr<Texture> pTexture(new Texture( pPixelMap));

        return new Sprite
        (
		    Rectangle<int>( 0 , 0 , pPixelMap->shape()[0] ,  pPixelMap->shape()[1]) ,
		    pTexture ,
	    	Rectangle<real>
            (
			    0 , 0 , 
                static_cast<real>(pPixelMap->shape()[0]) / pTexture->getWidth() ,
			    static_cast<real>(pPixelMap->shape()[1]) / pTexture->getHeight() 
            )
		);

    }


} // namespace mof



