// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010  Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include <nel/misc/types_nl.h>
#include "main_window.h"

// STL includes

// Qt includes
#include <QtGui/QtGui>
#include <QtGui/QTreeView>
#include <QtGui/QDirModel>
#include <QtGui/QUndoStack>
#include <QtGui/QScrollArea>

// NeL includes
// #include <nel/misc/debug.h>
#include <nel/misc/i18n.h>
#include <nel/3d/u_driver.h>

// Project includes
#include "command_log.h"
#include "graphics_viewport.h"
#include "graphics_config.h"

using namespace std;
using namespace NLMISC;

namespace NLQT {

namespace {

QString nli18n(const char *label)
{
	return QString::fromUtf16(CI18N::get(label).c_str());
}

} /* anonymous namespace */

CMainWindow::CMainWindow(const QMap<QString, QSize> &customSizeHints, QWidget *parent, Qt::WindowFlags flags)
	: QMainWindow(parent, flags),
	m_UndoStack(NULL), 
	m_IsGraphicsInitialized(false), m_IsGraphicsEnabled(false), 
	m_IsSoundInitialized(false), m_IsSoundEnabled(false), 
	m_GraphicsViewport(NULL), 
	m_CommandLog(NULL), m_CommandLogDock(NULL), 
	m_GraphicsConfig(NULL), m_GraphicsConfigScroll(NULL), m_GraphicsConfigDock(NULL), 
	m_FileMenu(NULL), m_EditMenu(NULL), m_ViewportMenu(NULL), m_WidgetsMenu(NULL), m_HelpMenu(NULL), 
	m_FileToolBar(NULL), m_EditToolBar(NULL),
	m_AboutAct(NULL), m_QuitAct(NULL), m_PrintDebugAct(NULL), 
	m_UndoAct(NULL), m_RedoAct(NULL), m_SaveScreenshotAct(NULL)
{
	setObjectName("CMainWindow");
	
	m_UndoStack = new QUndoStack(this);

	m_Configuration.init();
	
	m_OriginalPalette = QApplication::palette();
	m_Configuration.setAndCallback("QtStyle", CConfigCallback(this, &CMainWindow::cfcbQtStyle));
	m_Configuration.setAndCallback("QtPalette", CConfigCallback(this, &CMainWindow::cfcbQtPalette));
	
	m_Internationalization.init(&m_Configuration);
	m_Internationalization.enableCallback(CEmptyCallback(this, &CMainWindow::incbLanguageCode));

	m_GraphicsViewport = new CGraphicsViewport(this);
    setCentralWidget(m_GraphicsViewport);
	
	createActions();
	createMenus();
	createToolBars();
	createStatusBar();
	createDockWindows();
	
	incbLanguageCode();

	recalculateMinimumWidth();
	
	// As a special case, a QTimer with a timeout of 0 will time out as soon as all the events in the window system's event queue have been processed. This can be used to do heavy work while providing a snappy user interface.
	QTimer *timer = new QTimer(this);
	connect(timer, SIGNAL(timeout()), this, SLOT(updateRender()));
	// timer->start(); // <- timeout 0
	// it's heavy on cpu, though, when no 3d driver initialized :)
	timer->start(40); // 25fps
	
	m_IsGraphicsEnabled = m_GraphicsConfig->getGraphicsEnabled();
	connect(m_GraphicsConfig, SIGNAL(applyGraphicsConfig()), this, SLOT(applyGraphicsConfig()));
	m_Configuration.setAndCallback("SoundEnabled", CConfigCallback(this, &CMainWindow::cfcbSoundEnabled));
}

CMainWindow::~CMainWindow()
{
	delete m_GraphicsConfig; m_GraphicsConfig = NULL;

	m_Configuration.dropCallback("SoundEnabled");
	updateInitialization(false);
	m_Internationalization.disableCallback(CEmptyCallback(this, &CMainWindow::incbLanguageCode));
	m_Internationalization.release();
	m_Configuration.dropCallback("QtPalette");
	m_Configuration.dropCallback("QtStyle");
	m_Configuration.release();
}

void CMainWindow::setVisible(bool visible)
{
	// called by show()
	// code assuming visible window needed to init the 3d driver
	if (visible != isVisible())
	{
		if (visible)
		{
			QMainWindow::setVisible(true);
			updateInitialization(true);
		}
		else
		{
			updateInitialization(false);
			QMainWindow::setVisible(false);
		}
	}
}

void CMainWindow::updateInitialization(bool visible)
{
	bool done;
	do
	{
		done = true; // set false whenever change
		bool wantSound = m_IsSoundEnabled && visible;
		bool wantGraphics = m_IsGraphicsEnabled && visible;
		// bool wantLandscape = wantGraphics && m_IsGraphicsInitialized && isLandscapeEnabled;

		// .. stuff that depends on other stuff goes on top to prioritize deinitialization

		// Landscape
		// ...

		// Graphics (Driver)
		if (m_IsGraphicsInitialized)
		{
			if (!wantGraphics)
			{
				m_IsGraphicsInitialized = false;
				if (m_IsSoundInitialized)
					m_SoundUtilities.releaseGraphics();
				m_GraphicsViewport->release();
				done = false;
			}
		}
		else
		{
			if (wantGraphics)
			{
				m_GraphicsViewport->init(m_GraphicsConfig);
				if (m_IsSoundInitialized)
					m_SoundUtilities.initGraphics(m_GraphicsViewport);
				m_IsGraphicsInitialized = true;
				done = false;
			}
		}
		
		// Sound (AudioMixer)
		if (m_IsSoundInitialized)
		{
			if (!wantSound)
			{
				m_IsSoundInitialized = false;
				if (m_IsGraphicsInitialized)
					m_SoundUtilities.releaseGraphics();
				m_SoundUtilities.release();
				done = false;
			}
		}
		else
		{
			if (wantSound)
			{
				
				m_SoundUtilities.init(&m_Configuration, &m_Internationalization);
				if (m_IsGraphicsInitialized)
					m_SoundUtilities.initGraphics(m_GraphicsViewport);
				m_IsSoundInitialized = true;
				done = false;
			}
		}

	} while (!done);
}

void CMainWindow::createActions()
{
	m_QuitAct = new QAction(this);
	m_QuitAct->setShortcuts(QKeySequence::Quit);	
	connect(m_QuitAct, SIGNAL(triggered()), this, SLOT(close()));
	
	m_AboutAct = new QAction(this);
	connect(m_AboutAct, SIGNAL(triggered()), this, SLOT(about()));

	m_PrintDebugAct = new QAction(this);
	connect(m_PrintDebugAct, SIGNAL(triggered()), this, SLOT(printDebug()));

	m_UndoAct = m_UndoStack->createUndoAction(this);
	m_UndoAct->setShortcuts(QKeySequence::Undo);
	m_RedoAct = m_UndoStack->createRedoAction(this);
	m_RedoAct->setShortcuts(QKeySequence::Redo);

	m_SaveScreenshotAct = m_GraphicsViewport->createSaveScreenshotAction(this);
}

void CMainWindow::translateActions()
{
	m_QuitAct->setText(nli18n("ActionQuit"));
	m_QuitAct->setStatusTip(nli18n("ActionQuitStatusTip"));
	m_AboutAct->setText(nli18n("ActionAbout"));
	m_AboutAct->setStatusTip(nli18n("ActionAboutStatusTip"));
	m_PrintDebugAct->setText(nli18n("ActionPrintDebug"));
	m_PrintDebugAct->setStatusTip(nli18n("ActionPrintDebugStatusTip"));
	m_UndoAct->setText(nli18n("ActionUndo"));
	m_UndoAct->setStatusTip(nli18n("ActionUndoStatusTip"));
	m_RedoAct->setText(nli18n("ActionRedo"));
	m_RedoAct->setStatusTip(nli18n("ActionRedoStatusTip"));
	m_SaveScreenshotAct->setText(nli18n("ActionSaveScreenshot"));
	m_SaveScreenshotAct->setStatusTip(nli18n("ActionSaveScreenshotStatusTip"));
}

void CMainWindow::createMenus()
{
	m_FileMenu = menuBar()->addMenu(QString::null);
	//m_FileMenu->addAction(saveAct);
	//m_FileMenu->addSeparator();
	m_FileMenu->addAction(m_QuitAct);

	m_EditMenu = menuBar()->addMenu(QString::null);
	m_EditMenu->addAction(m_UndoAct);
	m_EditMenu->addAction(m_RedoAct);

	m_ViewportMenu = menuBar()->addMenu(QString::null);
	m_ViewportMenu->addAction(m_SaveScreenshotAct);
	
	m_WidgetsMenu = menuBar()->addMenu(QString::null);
	
	menuBar()->addSeparator();
	
	m_HelpMenu = menuBar()->addMenu(QString::null);
	m_HelpMenu->addAction(m_AboutAct);
}

void CMainWindow::translateMenus()
{
	m_FileMenu->setTitle(nli18n("MenuFile"));
	m_EditMenu->setTitle(nli18n("MenuEdit"));
	m_ViewportMenu->setTitle(nli18n("MenuViewport"));
	m_WidgetsMenu->setTitle(nli18n("MenuWidgets"));
	m_HelpMenu->setTitle(nli18n("MenuHelp"));
}

void CMainWindow::createToolBars()
{
	m_FileToolBar = addToolBar(QString::null);
	m_FileToolBar->addAction(m_QuitAct);
	m_FileToolBar->addAction(m_PrintDebugAct);

	m_EditToolBar = addToolBar(QString::null);
	m_EditToolBar->addAction(m_AboutAct);
}

void CMainWindow::translateToolBars()
{
	m_FileToolBar->setWindowTitle(nli18n("BarFile"));
	m_EditToolBar->setWindowTitle(nli18n("BarEdit"));
}

void CMainWindow::createStatusBar()
{
	statusBar()->showMessage(nli18n("StatusReady"));
}

void CMainWindow::createDockWindows()
{
	//QDockWidget *dock = new QDockWidget(tr("Test1"), this);
	//dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
	//customerList = new QListWidget(dock);
	//dock->setWidget(customerList);
	//addDockWidget(Qt::RightDockWidgetArea, dock);
	//m_WidgetsMenu->addAction(dock->toggleViewAction());

	//dock = new QDockWidget(tr("Test2"), this);
	//paragraphsList = new QListWidget(dock);
	//dock->setWidget(paragraphsList);
	//addDockWidget(Qt::RightDockWidgetArea, dock);
	//m_WidgetsMenu->addAction(dock->toggleViewAction());

	//connect(customerList, SIGNAL(currentTextChanged(QString)),
	//	this, SLOT(insertCustomer(QString)));
	//connect(paragraphsList, SIGNAL(currentTextChanged(QString)),
	//	this, SLOT(addParagraph(QString)));

	//dock = new QDockWidget(

	// CommandLog (Console)
	{
		m_CommandLogDock = new QDockWidget(this);
		m_CommandLogDock->setAllowedAreas(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea);
		m_CommandLog = new CCommandLog(m_CommandLogDock);
		m_CommandLogDock->setWidget(m_CommandLog);
		addDockWidget(Qt::BottomDockWidgetArea, m_CommandLogDock);
		m_WidgetsMenu->addAction(m_CommandLogDock->toggleViewAction());
	}

	// GraphicsConfig (Graphics Configuration)
	{
		m_GraphicsConfigDock = new QDockWidget(this);
		m_GraphicsConfigDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
		m_GraphicsConfigScroll = new QScrollArea();
		m_GraphicsConfigScroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
		m_GraphicsConfigScroll->setWidgetResizable(true);
		m_GraphicsConfig = new CGraphicsConfig(m_GraphicsConfigDock, &m_Configuration, &m_Internationalization, m_UndoStack);
		m_GraphicsConfigScroll->setWidget(m_GraphicsConfig);
		m_GraphicsConfigDock->setWidget(m_GraphicsConfigScroll);
		addDockWidget(Qt::RightDockWidgetArea, m_GraphicsConfigDock);
		m_WidgetsMenu->addAction(m_GraphicsConfigDock->toggleViewAction());
	}

	// AssetTree (Assets)
	{
		m_AssetTreeDock = new QDockWidget(this);
		m_AssetTreeDock->setAllowedAreas(Qt::AllDockWidgetAreas);
		m_AssetTreeView = new QTreeView(m_AssetTreeDock);
		m_AssetTreeModel = new QDirModel();
		m_AssetTreeView->setModel(m_AssetTreeModel);
		m_AssetTreeView->setSortingEnabled(true);
		m_AssetTreeDock->setWidget(m_AssetTreeView);
		addDockWidget(Qt::LeftDockWidgetArea, m_AssetTreeDock);
		m_WidgetsMenu->addAction(m_AssetTreeDock->toggleViewAction());
	}
}

void CMainWindow::translateDockWindows()
{
	m_CommandLogDock->setWindowTitle(nli18n("WidgetCommandLog"));
	m_GraphicsConfigDock->setWindowTitle(nli18n("WidgetGraphicsConfig"));
	m_AssetTreeDock->setWindowTitle(nli18n("WidgetAssetTree"));
}

void CMainWindow::recalculateMinimumWidth()
{
	if (m_GraphicsConfigScroll) 
		m_GraphicsConfigScroll->setMinimumWidth(m_GraphicsConfig->minimumSizeHint().width() + m_GraphicsConfigScroll->minimumSizeHint().width());
}

void CMainWindow::cfcbQtStyle(NLMISC::CConfigFile::CVar &var)
{
	QApplication::setStyle(QStyleFactory::create(var.asString().c_str()));
	recalculateMinimumWidth();
}

void CMainWindow::cfcbQtPalette(NLMISC::CConfigFile::CVar &var)
{
	if (var.asBool()) QApplication::setPalette(QApplication::style()->standardPalette());
	else QApplication::setPalette(m_OriginalPalette);
}

void CMainWindow::applyGraphicsConfig()
{
	// reinitializes the graphics system completely
	// heavy lifting is done in updateInitialization
	m_IsGraphicsEnabled = false;
	updateInitialization(isVisible());
	m_IsGraphicsEnabled = m_GraphicsConfig->getGraphicsEnabled();
	updateInitialization(isVisible());
}

void CMainWindow::cfcbSoundEnabled(NLMISC::CConfigFile::CVar &var)
{
	// temp, todo as above
	m_IsSoundEnabled = var.asBool(); // update loop inits
}

void CMainWindow::incbLanguageCode()
{
	setWindowTitle(nli18n("WindowTitle"));
	translateActions();
	translateMenus();
	translateToolBars();
	translateDockWindows();
	recalculateMinimumWidth();
}

void CMainWindow::about()
{
	QMessageBox::about(this, nli18n("AboutTitle"), nli18n("AboutText"));
}

void CMainWindow::updateRender()
{
	updateInitialization(isVisible());
	
	if (isVisible())
	{

		// call all update functions
		// 01. Update Utilities (configuration etc)
		m_Configuration.updateUtilities();

		// 02. Update Time (deltas)
		// ...

		// 03. Update Receive (network, servertime, receive messages)
		// ...
		
		// 04. Update Input (keyboard controls, etc)
		if (m_IsGraphicsInitialized)
			m_GraphicsViewport->updateInput();
		
		// 05. Update Weather (sky, snow, wind, fog, sun)
		// ...

		// 06. Update Entities (movement, do after possible tp from incoming messages etc)
		//      - Move other entities
		//      - Update self entity
		//      - Move bullets
		// ...

		// 07. Update Landscape (async zone loading near entity)
		// ...
		
		// 08. Update Collisions (entities)
		//      - Update entities
		//      - Update move container (swap with Update entities? todo: check code!)
		//      - Update bullets
		// ...
		
		// 09. Update Animations (playlists)
		//      - Needs to be either before or after entities, not sure, 
		//        there was a problem with wrong order a while ago!!!
		// ...
		
		// 10. Update Camera (depends on entities)
		// ...
		
		// 11. Update Interface (login, ui, etc)
		// ...
		
		// 12. Update Sound (sound driver)
		if (m_IsSoundInitialized)
			m_SoundUtilities.updateSound();
		
		// 13. Update Send (network, send new position etc)
		// ...
		
		// 14. Update Debug (stuff for dev)
		// ...
		
		if (m_IsGraphicsInitialized && !m_GraphicsViewport->getDriver()->isLost())
		{
			// 01. Render Driver (background color)			
			m_GraphicsViewport->renderDriver(); // clear all buffers
				
			// 02. Render Sky (sky scene)
			// ...
			
			// 04. Render Scene (entity scene)
			// ...
			
			// 05. Render Effects (flare)
			// ...
			
			// 06. Render Interface 3D (player names)
			// ...

			// 07. Render Debug 3D
			// ...

			// 08. Render Interface 2D (chatboxes etc, optionally does have 3d)
			// ...
				
			// 09. Render Debug 2D (stuff for dev)
			m_GraphicsViewport->renderDebug2D();

			// swap 3d buffers
			m_GraphicsViewport->getDriver()->swapBuffers();
		}
	}
}

void CMainWindow::printDebug()
{
	nldebug("This is a debug message.");
}

} /* namespace NLQT */

/* end of file */