// This file is part of ThriceBall.
//
// Author: Kai Hillenbrand (AnTeevY)
// © Copyright, 2009 by Kai Hillenbrand, http://www.anteevy.de

// --------------------------------------------------

#pragma once
#ifndef BASERENDERER_H
#define BASERENDERER_H

// extern includes
#include "Ogre.h"
#include "OgreConfigFile.h"
#include "tinyxml/tinyxml.h"

// intern includes
#include "baseframelistener.h"

// --------------------------------------------------

using namespace Ogre;

class CBaseRenderer
{
public:
	CBaseRenderer() : m_pFrameListener(0), m_pRoot(0), m_sResourcePath("settings/"),
		m_bDebugMode(false), m_bShadows(true), m_iSoundVol(50), m_iMusVol(50), m_iResolution(0), m_sLevelName("")
    {
		std::cout << "ThriceBall Log: Created Renderer" << std::endl;	// for the log
    }

    virtual ~CBaseRenderer()
    {
        if (m_pFrameListener)	
			delete m_pFrameListener;
        if (m_pRoot)			
			delete m_pRoot;
    }

    virtual void go(void)
    {
        if (!setup())
            return;

		m_pRoot->startRendering();	// start rendering phase
        destroyScene();				// clean up
    }

protected:
    Root *m_pRoot;
    Camera *m_pCamera;
    SceneManager *m_pSceneMgr;
    CBaseFrameListener *m_pFrameListener;
    RenderWindow *m_pWindow;
	String m_sResourcePath;

	// read-in XML variables
	bool	m_bDebugMode,	// debug mode?
			m_bShadows;		// shadows?
	int		m_iSoundVol,	// sound volume
			m_iMusVol,		// music volume
			m_iResolution;	// resolution
	String m_sLevelName;	// level name

    // these internal methods package up the stages in the startup process
    // sets up the application - returns false if the user chooses to abandon configuration
    virtual bool setup(void)
    {
		String pluginsPath;

		// only use plugins.cfg if not static
		#ifndef OGRE_STATIC_LIB
			pluginsPath = m_sResourcePath + "plugins.cfg";
			#ifdef _DEBUG
				pluginsPath = m_sResourcePath + "plugins_d.cfg";
			#endif
		#endif
			m_pRoot = new Root(pluginsPath, m_sResourcePath + "ogre.cfg");

        setupResources();				// defines the source of resources (other than current folder)
        if (!configure())				// configures the application (reads settings.xml)
			return false;

        chooseSceneManager();
        createCamera();					// done by CRenderer
        createViewports();

        // set default mipmap level (NB some APIs ignore this)
        TextureManager::getSingleton().setDefaultNumMipmaps(5);
		// initialise, parse scripts etc.
		ResourceGroupManager::getSingleton().initialiseAllResourceGroups();

        createScene();					// done by CRenderer
        createFrameListener();			// done by CRenderer

        return true;
    }

	virtual bool configure(void)
    {
		String filename = m_sResourcePath + "settings.xml";

		// load the xml doc and setup variables
		TiXmlDocument doc(filename.c_str());
		if (!doc.LoadFile())
			return false;

		TiXmlHandle hDoc(&doc);
		TiXmlElement *elemSettings = hDoc.FirstChildElement().Element();
		TiXmlElement *elemLevel = hDoc.FirstChildElement().FirstChild("Level").Element();
		TiXmlElement *elemGraphics = hDoc.FirstChildElement().FirstChild("Graphics").Element();
		TiXmlElement *elemSound = hDoc.FirstChildElement().FirstChild("Sound").Element();

		// Level
		m_sLevelName = elemLevel->GetText();

		// Graphics
		int iRendSub = 0, iDebugMode = 0, iAntiAlias = 0, iVidMode = 0, iFullScreen = 0, iShadows = 0;

		elemGraphics->QueryIntAttribute("RendSub",		&iRendSub);
		elemGraphics->QueryIntAttribute("DebugMode",	&iDebugMode);
		elemGraphics->QueryIntAttribute("AntiAlias",	&iAntiAlias);
		elemGraphics->QueryIntAttribute("VidMode",		&iVidMode);
		elemGraphics->QueryIntAttribute("FullScreen",	&iFullScreen);
		elemGraphics->QueryIntAttribute("Shadows",		&iShadows);

		// Sound
		elemSound->QueryIntAttribute("SoundVol",		&m_iSoundVol);
		elemSound->QueryIntAttribute("MusVol",			&m_iMusVol);

		m_iResolution = iVidMode;	// save the resolution

		// now apply the read-in data!
		RenderSystem *rs = 0;

		if (iRendSub == 0)	// direct3d
		{
			rs = m_pRoot->getRenderSystemByName("Direct3D9 Rendering Subsystem");

			// video mode
			if (iVidMode == 0)
				rs->setConfigOption("Video Mode", "1024 x 768 @ 32-bit colour");
			else if (iVidMode == 1)
				rs->setConfigOption("Video Mode", "1280 x 800 @ 32-bit colour");
			else if (iVidMode == 2)
				rs->setConfigOption("Video Mode", "1280 x 1024 @ 32-bit colour");
			else if (iVidMode == 3)
				rs->setConfigOption("Video Mode", "1440 x 900 @ 32-bit colour");
			else if (iVidMode == 4)
				rs->setConfigOption("Video Mode", "1680 x 1050 @ 32-bit colour");
			else if (iVidMode == 5)
				rs->setConfigOption("Video Mode", "1920 x 1080 @ 32-bit colour");
			else
				rs->setConfigOption("Video Mode", "1024 x 768 @ 32-bit colour");

			// anti aliasing
			if (iAntiAlias == 0)
				rs->setConfigOption("Anti aliasing", "None");
			else if (iAntiAlias == 1)
				rs->setConfigOption("Anti aliasing", "Level 2");
			else if (iAntiAlias == 2)
				rs->setConfigOption("Anti aliasing", "Level 8");
			else
				rs->setConfigOption("Anti aliasing", "None");
		}
		else	// opengl
		{
			rs = m_pRoot->getRenderSystemByName("OpenGL Rendering Subsystem");

			// video mode
			if (iVidMode == 0)
				rs->setConfigOption("Video Mode", "1024 x 768");
			else if (iVidMode == 1)
				rs->setConfigOption("Video Mode", "1280 x 800");
			else if (iVidMode == 2)
				rs->setConfigOption("Video Mode", "1280 x 1024");
			else if (iVidMode == 3)
				rs->setConfigOption("Video Mode", "1440 x 900");
			else if (iVidMode == 4)
				rs->setConfigOption("Video Mode", "1680 x 1050");
			else if (iVidMode == 5)
				rs->setConfigOption("Video Mode", "1920 x 1080");
			else
				rs->setConfigOption("Video Mode", "1024 x 768");

			// anti aliasing
			if (iAntiAlias == 0)
				rs->setConfigOption("FSAA", "0");
			else if (iAntiAlias == 1)
				rs->setConfigOption("FSAA", "2");
			else if (iAntiAlias == 2)
				rs->setConfigOption("FSAA", "8");
			else
				rs->setConfigOption("FSAA", "0");
		}

		// debug mode
		if (iDebugMode == 0)
			m_bDebugMode = false;
		else
			m_bDebugMode = true;

		// full screen
		if (iFullScreen == 0)
			rs->setConfigOption("Full Screen", "No");
		else
			rs->setConfigOption("Full Screen", "Yes");

		// shadows
		if (iShadows == 0)
			m_bShadows = false;
		else
			m_bShadows = true;

		// vsync on
		rs->setConfigOption("VSync", "Yes");

		// if in debug mode, show config dialog and discard the "old" settings
		if (m_bDebugMode)
			m_pRoot->showConfigDialog();
		else
			m_pRoot->setRenderSystem(rs);

		m_pWindow = m_pRoot->initialise(true, "ThriceBall");

		return true;
    }

    virtual void chooseSceneManager(void)
    {
        // create the SceneManager, in this case a generic one
        m_pSceneMgr = m_pRoot->createSceneManager(ST_GENERIC, "MainSceneManager");
    }
	virtual void createCamera(void){}			// this has to be overridden

	virtual void createFrameListener(void){}	// this has to be overridden

    virtual void createScene(void) = 0;			// pure virtual - this has to be overridden

    virtual void destroyScene(void)
	{
		delete this;
	}

    virtual void createViewports(void)
    {
        // create one viewport, entire window
        Viewport* vp = m_pWindow->addViewport(m_pCamera);
        vp->setBackgroundColour(ColourValue(0,0,0));

        // alter the camera aspect ratio to match the viewport
        m_pCamera->setAspectRatio(Real(vp->getActualWidth()) / Real(vp->getActualHeight()));
    }

    virtual void setupResources(void)
    {
        // load resource paths from config file
        ConfigFile cf;
        cf.load(m_sResourcePath + "resources.cfg");

        // go through all sections and settings in the file
        ConfigFile::SectionIterator seci = cf.getSectionIterator();

        String secName, typeName, archName;
        while (seci.hasMoreElements())
        {
            secName = seci.peekNextKey();
            ConfigFile::SettingsMultiMap *settings = seci.getNext();
            ConfigFile::SettingsMultiMap::iterator i;
            for (i = settings->begin(); i != settings->end(); ++i)
            {
                typeName = i->first;
                archName = i->second;
                ResourceGroupManager::getSingleton().addResourceLocation(archName, typeName, secName);
            }
        }
    }
};

#endif
