/*
 * 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;

//-----------------------------------------------------------------------
/** Display a 3D Model.
 */
class CModelLoader : public IRenderApp
{
    //! View matrix 2D
    CMatrix4 m_MatView2D;
    //! View Matrix 3D
    CMatrix4 m_MatView3D;
    //! Projection matrix 2D
    CMatrix4 m_MatProj2D;
    //! Projection matrix 3D
    CMatrix4 m_MatProj3D;

	//! Camera position
    TVector3F      m_CameraPos;

    //! Rotation matrix
    CMatrix4 Rot;
    //! Graphic string which display FPS informations
    CGraphicString*  m_pFPS;
	//! Graphic string which display model filename and texture filename
	CGraphicString*  m_pGSModel;
	//! Model filename
	String			 m_strModel;
	//! Texture filename
	String			 m_strTexture;

	//! Model
    TModelPtr      m_Model;         
	
    void Init(void) 
    {
		m_CameraPos.Set(0, 0, -40);
        
        // Init 3D transformation matrix
        CMatrix4 MatView, MatProj, MatRotY, MatRotZ;
        MatRotY.SetRotationY(-3.14159f / 2, TVector3F(0, 0, 0));
        MatRotZ.SetRotationZ(-3.14159f / 2, TVector3F(0, 0, 0));
        m_MatView3D.LookAt(m_CameraPos, m_CameraPos + TVector3F(0.0f, 0.0f, 1.0f));
        m_MatView3D = MatRotY * MatRotZ * m_MatView3D;
        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());

        // Search path
        CMediaManager::Instance().AddSearchPath("Textures");
        CMediaManager::Instance().AddSearchPath("Fonts");
        CMediaManager::Instance().AddSearchPath("Models");

        // 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)); 
		
		m_strModel = "newcyb.md2";
		m_strTexture = "newcyb.tga";
		m_pGSModel = new CGraphicString(TVector2I(0, 0), "Model: " + m_strModel +"\nTexture: " + m_strTexture, CColor(255, 255, 255), "Font");
		m_pGSModel->Align(ALIGN_RIGHT | 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(Load, *this), "load <modelfile> <texturefile>");
        CConsole::Instance().RegisterCommand("loadmodel", Console::Bind(LoadModel, *this), "loadmodel <modelfile>");
        CConsole::Instance().RegisterCommand("loadtexture",   Console::Bind(LoadTexture, *this), "loadtexture <texturefile>");
		
		Load(m_strModel,m_strTexture);
    }

	//! Load a model and a texture
    void Load(const String& Filename, const String& Filename2)
    {
        LoadModel(Filename);
        LoadTexture(Filename2);
    }

	//! Load a model
    void LoadModel(const String& Filename)
    {
        m_Model = CMediaManager::Instance().LoadMediaFromFile<CModel>(Filename);
		m_strModel = Filename;
		m_pGSModel->Text = "Model: " + m_strModel + "\nTexture: (default)";
		m_pGSModel->Align(ALIGN_RIGHT | ALIGN_BOTTOM, CRectangle(10, 10, GetRenderTarget()->GetWidth()-20, GetRenderTarget()->GetHeight()-20)); 	
    }

	//! Load a texture
    void LoadTexture(const String& Filename)
    {
        m_Model->SetTexture(Filename);
		m_strTexture = Filename;
		m_pGSModel->Text = "Model: " + m_strModel + "\nTexture: " + m_strTexture;
		m_pGSModel->Align(ALIGN_RIGHT | ALIGN_BOTTOM, CRectangle(10, 10, GetRenderTarget()->GetWidth()-20, GetRenderTarget()->GetHeight()-20)); 
    }

    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, Rot);
       
		// Display model
        m_Model->Render();

        // 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();
		m_pGSModel->Draw();

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

    void Update(void)
    {
		uint nCurrentTime = GetTime();

        // Change the rotation matrix
        Rot.SetRotationZ(nCurrentTime / 1500.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)
    {
		KEOS_DELETE(m_pFPS);
		KEOS_DELETE(m_pGSModel);
    }
};

int main(int argc, char* argv[])
{ 
	KEOS_START();
    try
    { 
        CModelLoader ModelLoader;

		// 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: ModelLoader");
			KeosRoot.GetRenderSystem()->SetRenderApp(&ModelLoader);
			KeosRoot.StartRendering();
		}
    }
    catch (const CException& E)
    {
        MessageBox(NULL, E.what(), "Keos Exception", MB_ICONWARNING);
    }

    KEOS_SHUTDOWN();
	return EXIT_SUCCESS;
}