/*
 * This source file is part of KEOS (Free 3D Engine)
 * For the latest info, see http://www.keosengine.org/
 * E-mails : thierry.vouriot@keosengine.org, yeri@keosengine.org
 * 
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * This program 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place - Suite 330, Boston, MA 02111-1307, USA, or go to
 * http://www.gnu.org/copyleft/lesser.txt.
 *
 */

#include "Keos.h"
using namespace Keos;

//-----------------------------------------------------------------------
/** Texture-mapping example.
 */
class CTextureMapping : public IRenderApp
{
    //! Vertex type
    struct TVertex
    {
        TVector3F     Position;
        ulong         Color;
        TVector2F     TexCoords;
    };
    //! Indice type
    typedef ushort TIndex;

    //! Vertex buffer
    CBuffer<TVertex> m_VertexBuffer;
    //! Index buffer
    CBuffer<TIndex> m_IndexBuffer;
    //! Vertex declaration
    CDeclarationPtr m_Declaration;  

    //! View matrix 2D
    CMatrix4 m_MatView2D;
    //! View Matrix 3D
    CMatrix4 m_MatView3D;
    //! Projection matrix 2D
    CMatrix4 m_MatProj2D;
    //! Projection matrix 3D
    CMatrix4 m_MatProj3D;

    //! Rotation matrix
    CMatrix4 RotX, RotY;
    //! Graphic string which display FPS informations
    CGraphicString*  m_pFPS;
    //! Texture
    CTexture         m_Texture;
        
    void Init(void) 
    {
        TVertex Vertices[] =
        {
	        {TVector3F(-10, 10,-10), GetRenderSystem()->ConvertColor(CColor::White), TVector2F(0.0f,0.0f) },
	        {TVector3F( 10, 10,-10), GetRenderSystem()->ConvertColor(CColor::White), TVector2F(1.0f,0.0f) },
	        {TVector3F(-10,-10,-10), GetRenderSystem()->ConvertColor(CColor::White), TVector2F(0.0f,1.0f) },
	        {TVector3F( 10,-10,-10), GetRenderSystem()->ConvertColor(CColor::White), TVector2F(1.0f,1.0f) },
        	
	        {TVector3F(-10, 10, 10), GetRenderSystem()->ConvertColor(CColor::White), TVector2F(1.0f,0.0f) },
	        {TVector3F(-10,-10, 10), GetRenderSystem()->ConvertColor(CColor::White), TVector2F(1.0f,1.0f) },
	        {TVector3F( 10, 10, 10), GetRenderSystem()->ConvertColor(CColor::White), TVector2F(0.0f,0.0f) },
	        {TVector3F( 10,-10, 10), GetRenderSystem()->ConvertColor(CColor::White), TVector2F(0.0f,1.0f) },
        	
	        {TVector3F(-10, 10, 10), GetRenderSystem()->ConvertColor(CColor::White), TVector2F(0.0f,0.0f) },
	        {TVector3F( 10, 10, 10), GetRenderSystem()->ConvertColor(CColor::White), TVector2F(1.0f,0.0f) },
	        {TVector3F(-10, 10,-10), GetRenderSystem()->ConvertColor(CColor::White), TVector2F(0.0f,1.0f) },
	        {TVector3F( 10, 10,-10), GetRenderSystem()->ConvertColor(CColor::White), TVector2F(1.0f,1.0f) },
        	
	        {TVector3F(-10,-10, 10), GetRenderSystem()->ConvertColor(CColor::White), TVector2F(0.0f,1.0f) },
	        {TVector3F(-10,-10,-10), GetRenderSystem()->ConvertColor(CColor::White), TVector2F(0.0f,0.0f) },
	        {TVector3F( 10,-10, 10), GetRenderSystem()->ConvertColor(CColor::White), TVector2F(1.0f,1.0f) },
	        {TVector3F( 10,-10,-10), GetRenderSystem()->ConvertColor(CColor::White), TVector2F(1.0f,0.0f) },

	        {TVector3F( 10, 10,-10), GetRenderSystem()->ConvertColor(CColor::White), TVector2F(0.0f,0.0f) },
	        {TVector3F( 10, 10, 10), GetRenderSystem()->ConvertColor(CColor::White), TVector2F(1.0f,0.0f) },
	        {TVector3F( 10,-10,-10), GetRenderSystem()->ConvertColor(CColor::White), TVector2F(0.0f,1.0f) },
	        {TVector3F( 10,-10, 10), GetRenderSystem()->ConvertColor(CColor::White), TVector2F(1.0f,1.0f) },
        	
	        {TVector3F(-10, 10,-10), GetRenderSystem()->ConvertColor(CColor::White), TVector2F(1.0f,0.0f) },
	        {TVector3F(-10,-10,-10), GetRenderSystem()->ConvertColor(CColor::White), TVector2F(1.0f,1.0f) },
	        {TVector3F(-10, 10, 10), GetRenderSystem()->ConvertColor(CColor::White), TVector2F(0.0f,0.0f) },
	        {TVector3F(-10,-10, 10), GetRenderSystem()->ConvertColor(CColor::White), TVector2F(0.0f,1.0f) }
        };
        m_VertexBuffer = GetRenderSystem()->CreateVertexBuffer(24, 0, Vertices);
        
        TDeclarationElement Decl[] =
        {
            {0, ELT_USAGE_POSITION, ELT_TYPE_FLOAT3},
            {0, ELT_USAGE_DIFFUSE,  ELT_TYPE_COLOR},
            {0, ELT_USAGE_TEXCOORD0, ELT_TYPE_FLOAT2}
        };
        m_Declaration  = GetRenderSystem()->CreateVertexDeclaration(Decl);

        // Init 3D transformation matrix
        m_MatView3D.LookAt(TVector3F(0, 0, -30), TVector3F(0, 0, 0));
        m_MatProj3D.PerspectiveFOV(3.14159f / 2, (Real)GetRenderTarget()->GetWidth()/(Real)GetRenderTarget()->GetHeight(), 1.0f, 300.0f);

        // Init 2D transformation matrix
        m_MatView2D.Identity();
        m_MatProj2D.OrthoOffCenter(0, 0, GetRenderTarget()->GetWidth(), GetRenderTarget()->GetHeight());

        CMediaManager::Instance().AddSearchPath("Textures");
        CMediaManager::Instance().AddSearchPath("Fonts");
        
		// Load font
        CFontManager::Instance().LoadFont("Atarian.ttf","Font", 14);

		// Create graphic string
        String strFPS = "Current FPS: \nAverage FPS: \nBest FPS: \n Worst FPS:";
		strFPS += "\n\nF1 / F2 to show / hide the console, escape to exit";
        m_pFPS = new CGraphicString(TVector2I(0, 0), strFPS, CColor(255, 255, 255), "Font");
		
		m_pFPS->Align(ALIGN_LEFT | ALIGN_BOTTOM, CRectangle(10, 10, GetRenderTarget()->GetWidth()-20, GetRenderTarget()->GetHeight()-20)); 
		
        // Add a "look" to the console
        CConsole::Instance().ChangeLook(new Console::CLookDefault("Font", TVector2F(GetRenderTarget()->GetWidth(), (Real)GetRenderTarget()->GetHeight() / 3.0), "ConsoleBG.jpg"));	
    
		// Register console command
		CConsole::Instance().RegisterCommand("load", Console::Bind(LoadTexture, *this), "load <texturefile>");

        // Load Texture
        m_Texture.CreateFromFile("wall.bmp", PXF_A8R8G8B8);
    }

	//! Load a texture
    void LoadTexture(const String& Filename)
    {
		m_Texture.CreateFromFile(Filename, PXF_A8R8G8B8);
    }

    void Render(void)
    {
        // Switch to 3D mode
        GetRenderSystem()->LoadMatrix(MAT_MODELVIEW,  m_MatView3D);
        GetRenderSystem()->LoadMatrix(MAT_PROJECTION, m_MatProj3D);

        // Save current view matrix
        GetRenderSystem()->PushMatrix(MAT_MODELVIEW);

        // Rotation
        GetRenderSystem()->LoadMatrixMult(MAT_MODELVIEW, RotX*RotY);
       
        // Display cube
        GetRenderSystem()->SetTexture(0, m_Texture.GetTexture());
        GetRenderSystem()->SetDeclaration(m_Declaration);
        GetRenderSystem()->SetVertexBuffer(0, m_VertexBuffer);
        
        GetRenderSystem()->DrawPrimitives( PT_TRIANGLESTRIP,  0, 2 );
        GetRenderSystem()->DrawPrimitives( PT_TRIANGLESTRIP,  4, 2 );
        GetRenderSystem()->DrawPrimitives( PT_TRIANGLESTRIP,  8, 2 );
        GetRenderSystem()->DrawPrimitives( PT_TRIANGLESTRIP, 12, 2 );
        GetRenderSystem()->DrawPrimitives( PT_TRIANGLESTRIP, 16, 2 );
        GetRenderSystem()->DrawPrimitives( PT_TRIANGLESTRIP, 20, 2 );
        
        // Get the last view matrix
        GetRenderSystem()->PopMatrix(MAT_MODELVIEW);

         // Switch to 2D mode
        GetRenderSystem()->LoadMatrix(MAT_MODELVIEW,  m_MatView2D);
        GetRenderSystem()->LoadMatrix(MAT_PROJECTION, m_MatProj2D);
        
        // Draw text
        m_pFPS->Draw();

		// Draw console
        CConsole::Instance().Draw();
    }

    void Update(void)
    {
        uint nCurrentTime = GetTime();
		
        // Change the rotation matrix
        RotX.SetRotationX(nCurrentTime / 1500.f);
        RotY.SetRotationY(nCurrentTime / 1000.f);

        // Update the FPS text
        m_pFPS->Text = "Current FPS: " + CStringConverter::ToString(GetRenderTarget()->GetLastFPS()) + 
                     "\nAverage FPS: " + CStringConverter::ToString(GetRenderTarget()->GetAverageFPS()) +
                     "\nBest FPS: " + CStringConverter::ToString(GetRenderTarget()->GetBestFPS()) +
                     "\nWorst FPS: " + CStringConverter::ToString(GetRenderTarget()->GetWorstFPS()) +
					 "\n\nf1 / f2 to show / hide the console, escape to exit";
        
        // Console update
        CConsole::Instance().Update();
    }

    void Release(void)
    {
		delete m_pFPS;
    }
};

int main(int argc, char* argv[])
{ 
	KEOS_START();
    try
    { 
        CTextureMapping TextureMapping;
		// Show the configuration dialog and initialise the system
        // You can skip this and use KeosRoot.RestoreConfig() to load configuration
        // settings if you were sure there are valid ones saved in config.xml
        if (KeosRoot.ShowConfigDialog())
		{
			KeosRoot.Initialise(true, "KEOS Sample: TextureMapping");
			KeosRoot.GetRenderSystem()->SetRenderApp(&TextureMapping);
			KeosRoot.StartRendering();
		}
    }
    catch (const CException& E)
    {
        MessageBox(NULL, E.what(), "Keos Exception", MB_ICONWARNING);
    }

    KEOS_SHUTDOWN();
	return EXIT_SUCCESS;
}