#include "NeuralNetworkWindow.h"
#include "NeuralNetwork.h"
#include "Muele.h"

CNeuralNetworkWindow::CNeuralNetworkWindow(CMuele *pMueleClass, int yPossiton) : pMuele(pMueleClass), hiddenNeurons(30)
{
	gui::IGUIEnvironment *pGuiEnv = this->pMuele->pGuiEnv;

	pWindow = pGuiEnv->addWindow(core::rect<s32>(1, yPossiton, 385, 455), false, L"Neural Network Setup");

	s32 fontHeight = pMuele->pGuiFont->getDimension(L"X").Height;
	s32 lineHeight = fontHeight + 4;
	s32 yPos = fontHeight + 10;
	s32 xEnd = pWindow->getRelativePosition().getWidth() - 4;
	s32 xPos = 23;


	pWindow->addChild(pGuiEnv->addButton(core::rect<s32>(xPos, yPos, xPos+165, yPos+fontHeight), 0, ID_BUTTON_LOAD_NET, L"load NET"));
	xPos+=170;
	pWindow->addChild(pGuiEnv->addButton(core::rect<s32>(xPos, yPos, xPos+165, yPos+fontHeight), 0, ID_BUTTON_SAVE_NET, L"save NET"));
	xPos=23;
	yPos+=lineHeight;
	pWindow->addChild(pGuiEnv->addButton(core::rect<s32>(xPos, yPos, xPos+165, yPos+fontHeight), 0, ID_BUTTON_LOAD_DEFAULT, L"load default NET"));
	xPos+=170;
	pWindow->addChild(pGuiEnv->addButton(core::rect<s32>(xPos, yPos, xPos+165, yPos+fontHeight), 0, ID_BUTTON_SAVE_DEFAULT, L"save default NET"));
	yPos+=lineHeight;
	xPos=3;
	pWindow->addChild(pGuiEnv->addStaticText(L"Network Informations", core::rect<s32>(xPos, yPos, xEnd, yPos + 8 * lineHeight ), true, true, 0, -1, true ));
	xPos=15;
	yPos+=lineHeight;
	pWindow->addChild(pTextName=pGuiEnv->addStaticText(      L"          name:", core::rect<s32>(xPos, yPos, xEnd, yPos + lineHeight), false, false));
	yPos+=lineHeight;
	pWindow->addChild(pTextTrainCount=pGuiEnv->addStaticText(L"  train cycles:", core::rect<s32>(xPos, yPos, xEnd, yPos + lineHeight)));
	yPos+=lineHeight;
	pWindow->addChild(pTextTime=pGuiEnv->addStaticText(      L" training time:", core::rect<s32>(xPos, yPos, xEnd, yPos + lineHeight)));
	yPos+=lineHeight;
	pWindow->addChild(pTextNeurons=pGuiEnv->addStaticText(L" Neurons I/H/O:", core::rect<s32>(xPos, yPos, xEnd, yPos + lineHeight)));
	yPos+=lineHeight;
	pWindow->addChild(pTextError=pGuiEnv->addStaticText( L"    max. error:", core::rect<s32>(xPos, yPos, xEnd, yPos + lineHeight)));
	yPos+=lineHeight;
	pWindow->addChild(pTextLearnRate=pGuiEnv->addStaticText( L"    learn rate:", core::rect<s32>(xPos, yPos, xEnd, yPos + lineHeight)));
	yPos+=lineHeight;
	pWindow->addChild(pTextStepSize=pGuiEnv->addStaticText( L"     step size:", core::rect<s32>(xPos, yPos, xEnd, yPos + lineHeight)));
	
	yPos+=lineHeight+3;
	xPos=3;
	pWindow->addChild(pGuiEnv->addStaticText(L"Network Settings", core::rect<s32>(xPos, yPos, xEnd, yPos + 9 * lineHeight ), true, true, 0, -1, true ));
	xPos=15;
	yPos+=lineHeight+3;
	pWindow->addChild(pGuiEnv->addStaticText(L"        train cycles:", core::rect<s32>(xPos, yPos, xPos + 180, yPos + lineHeight)));
	xPos=190;
	pWindow->addChild(pEditTrainCylcles=pGuiEnv->addEditBox(L"2000000", core::rect<s32>(xPos, yPos - 2, xPos + 150, yPos + lineHeight), true));
	xPos=15;
	yPos+=lineHeight+7;
	pWindow->addChild(pGuiEnv->addStaticText(L"      hidden neurons:", core::rect<s32>(xPos, yPos, xPos + 180, yPos + lineHeight)));
	xPos=190;
	pWindow->addChild(pEditNeurons=pGuiEnv->addEditBox(L"30", core::rect<s32>(xPos, yPos - 2, xPos + 80, yPos + lineHeight), true));
	xPos=15;
	yPos+=lineHeight+7;
	pWindow->addChild(pGuiEnv->addStaticText(L"max. error start/end:", core::rect<s32>(xPos, yPos, xPos + 180, yPos + lineHeight)));
	xPos=190;
	pWindow->addChild(pEditMaxErrorStart=pGuiEnv->addEditBox(L"0.0002", core::rect<s32>(xPos, yPos - 2, xPos + 80, yPos + lineHeight), true));
	pWindow->addChild(pEditMaxErrorEnd=pGuiEnv->addEditBox(L"0.0001", core::rect<s32>(xPos + 100, yPos - 2, xPos + 180, yPos + lineHeight), true));
	xPos=15;
	yPos+=lineHeight+7;
	pWindow->addChild(pGuiEnv->addStaticText(L"learn rate start/end:", core::rect<s32>(xPos, yPos, xPos + 180, yPos + lineHeight)));
	xPos=190;
	pWindow->addChild(pEditTrainRateStart=pGuiEnv->addEditBox(L"0.2", core::rect<s32>(xPos, yPos - 2, xPos + 80, yPos + lineHeight), true));
	pWindow->addChild(pEditTrainRateEnd=pGuiEnv->addEditBox(L"0.1", core::rect<s32>(xPos + 100, yPos - 2, xPos + 180, yPos + lineHeight), true));
	xPos=15;
	yPos+=lineHeight+4;
	pWindow->addChild(pGuiEnv->addStaticText(L" step size start/end:", core::rect<s32>(xPos, yPos, xPos + 180, yPos + lineHeight)));
	xPos=190;
	pWindow->addChild(pEditStepSizeStart=pGuiEnv->addEditBox(L"0.1", core::rect<s32>(xPos, yPos - 2, xPos + 80, yPos + lineHeight), true));
	pWindow->addChild(pEditStepSizeEnd=pGuiEnv->addEditBox(L"0.1", core::rect<s32>(xPos + 100, yPos - 2, xPos + 180, yPos + lineHeight), true));
	xPos=15;
	yPos+=lineHeight+3;
	pWindow->addChild(pCheckLargeInputVector=pGuiEnv->addCheckBox(false, core::rect<s32>(xPos, yPos - 2, xPos + 180, yPos + lineHeight), 0, ID_CHECK_LARGE_VECTOR, L"large input vector" ));
	xPos+=180;
	pWindow->addChild(pCheckStopAutomatically=pGuiEnv->addCheckBox(true, core::rect<s32>(xPos, yPos - 2, xPos + 180, yPos + lineHeight), 0, ID_CHECK_LARGE_VECTOR, L"stop automatically" ));
	yPos+=lineHeight+9;
	xPos=3;
	pWindow->addChild(pGuiEnv->addStaticText(L"Training", core::rect<s32>(xPos, yPos, xEnd, yPos + 2 * lineHeight + 3), true, true, 0, -1, true ));
	xPos=15;
	yPos+=lineHeight+3;
	xPos = 12;
	pWindow->addChild(pGuiEnv->addButton(core::rect<s32>(xPos, yPos, xPos+115, yPos+fontHeight), 0, ID_BUTTON_START_TRAINING, L"start"));
	xPos+=120;
	pWindow->addChild(pGuiEnv->addButton(core::rect<s32>(xPos, yPos, xPos+115, yPos+fontHeight), 0, ID_BUTTON_STOP_TRAINING, L"pause"));
	xPos+=120;
	pWindow->addChild(pGuiEnv->addButton(core::rect<s32>(xPos, yPos, xPos+115, yPos+fontHeight), 0, ID_BUTTON_CONTINUE_TRAINING, L"continue"));

	gui::IGUIElement* root = pMuele->pGuiEnv->getRootGUIElement();
	((gui::IGUIButton*)root->getElementFromId(ID_BUTTON_CONTINUE_TRAINING, true))->setVisible(false);

	yPos+=lineHeight;
	
	pWindow->getCloseButton()->setVisible(false);
	pWindow->addChild(pGuiEnv->addButton(pWindow->getCloseButton()->getRelativePosition(), 0, ID_BUTTON_CLOSE_NN, L"x"));

	pWindow->updateAbsolutePosition();
}

/********************************************************************/

CNeuralNetworkWindow::~CNeuralNetworkWindow(void)
{
}

/********************************************************************/

void CNeuralNetworkWindow::onGuiEvent(SEvent &event)
{
	gui::IGUIElement* root = pMuele->pGuiEnv->getRootGUIElement();

	if ( event.EventType == EET_GUI_EVENT )
	{
		s32 id = event.GUIEvent.Caller->getID();

		switch( event.GUIEvent.EventType )
		{
		case gui::EGET_FILE_SELECTED:
			{
				// load the network file, selected in the file open dialog
				gui::IGUIFileOpenDialog* dialog = 
					(gui::IGUIFileOpenDialog*)event.GUIEvent.Caller;
				if ( !pMuele->pNeuralNetwork->loadNet(dialog->getFilename()) )
					pMuele->createInformationWindow(300, 120, L"Error", L"Could not load file. Make sure that you select a neural network file.");
				break;
			}
		case gui::EGET_BUTTON_CLICKED:
			switch ( id )
			{
			case ID_BUTTON_CLOSE_NN: toggleVisiblility(); break;
			case ID_BUTTON_SAVE_NOW:
				pMuele->pNeuralNetwork->saveNet( root->getElementFromId(ID_EDIT_FILE_NAME, true)->getText() );
				event.GUIEvent.Caller->getParent()->remove();
				break;
			case ID_BUTTON_SAVE_NET:
			{
				gui::IGUIWindow *pWindow =	pMuele->createWindow(280, 140, L"Filename");
				pWindow->addChild( pMuele->pGuiEnv->addStaticText(L"Enter a filename without extension.", core::rect<s32>( 5, 25, 275, 60)));
				pWindow->addChild( pMuele->pGuiEnv->addEditBox(L"",core::rect<s32>(20, 65, 260, 85), true, pWindow, ID_EDIT_FILE_NAME ));
				pWindow->addChild( pMuele->pGuiEnv->addButton(core::rect<s32>(145, 110, 270, 130), 0, ID_BUTTON_GENERAL_CLOSE, L"cancel"));	
				pWindow->addChild( pMuele->pGuiEnv->addButton(core::rect<s32>(10, 110, 140, 130), pWindow, ID_BUTTON_SAVE_NOW, L"save network"));
				pWindow->updateAbsolutePosition();
				break;
			}
			case ID_BUTTON_LOAD_NET:
				pMuele->pGuiEnv->addFileOpenDialog(L"Please choose the network file to load");
				break;

			case ID_BUTTON_SAVE_DEFAULT_NOW:
				event.GUIEvent.Caller->getParent()->remove();
				if ( !pMuele->pNeuralNetwork->saveNet(NULL) )
					pMuele->createInformationWindow(300, 120, L"Error", L"Could not save default network. Could not write file.");
				break;
			case ID_BUTTON_SAVE_DEFAULT: 
			{
				gui::IGUIWindow *pWindow =	pMuele->createWindow(280, 120, L"Save as default");
				pWindow->addChild( pMuele->pGuiEnv->addStaticText(L"Actual network will be written as default.", core::rect<s32>( 5, 25, 275, 85)));
				pWindow->addChild( pMuele->pGuiEnv->addButton(core::rect<s32>(145, 90, 270, 110), 0, ID_BUTTON_GENERAL_CLOSE, L"cancel"));	
				pWindow->addChild( pMuele->pGuiEnv->addButton(core::rect<s32>(10, 90, 140, 110), pWindow, ID_BUTTON_SAVE_DEFAULT_NOW, L"write now"));
				pWindow->updateAbsolutePosition();
				break;
			}
			case ID_BUTTON_LOAD_DEFAULT: 
				if ( !pMuele->pNeuralNetwork->loadNet(NULL) )
					pMuele->createInformationWindow(300, 120, L"Error", L"Could not load default network. Make shure that a default network was saved.");
				break;
			case ID_BUTTON_TRAIN_SURE:
				pMuele->pNeuralNetwork->wasTrained = false;
				event.GUIEvent.Caller->getParent()->remove();
			case ID_BUTTON_START_TRAINING:
				{
					if ( pMuele->pNeuralNetwork->wasTrained )
					{
						gui::IGUIWindow *pWindow =	pMuele->createWindow(280, 100, L"Warning");
						pWindow->addChild( pMuele->pGuiEnv->addStaticText(L"Actual network will be lost.", core::rect<s32>( 5, 25, 275, 45)));
						pWindow->addChild( pMuele->pGuiEnv->addButton(core::rect<s32>(145, 60, 270, 80), 0, ID_BUTTON_GENERAL_CLOSE, L"cancel"));	
						pWindow->addChild( pMuele->pGuiEnv->addButton(core::rect<s32>(10, 60, 140, 80), pWindow, ID_BUTTON_TRAIN_SURE, L"start training"));

						pWindow->updateAbsolutePosition();
						break;
					}
					core::stringc s;

					s = pEditNeurons->getText();
					hiddenNeurons = atoi(s.c_str());
					delete pMuele->pNeuralNetwork;
					pMuele->pNeuralNetwork = new CNeuralNetwork(pMuele->boardSizeX, pMuele->boardSizeY, pMuele->boardSizeZ, pMuele->winFieldCount, hiddenNeurons, pCheckLargeInputVector->isChecked(), pMuele);

					pMuele->pNeuralNetwork->setLearnParameters();
					pMuele->trainingRunning=true;
					pMuele->pNeuralNetworkWindow->pTextName->setText(L"          name:");
					resetWindow(true);
					break;
				}
			case ID_BUTTON_STOP_TRAINING:
				pMuele->trainingRunning=false;
				resetWindow(false);
				break;
			case ID_BUTTON_CONTINUE_TRAINING:
				pMuele->trainingRunning=true;
				resetWindow(false);
				break;
			}
			
		break;
		}
	}
}

/********************************************************************/

void CNeuralNetworkWindow::resetWindow(bool setHiddenNeurons)
{
	gui::IGUIElement* root = pMuele->pGuiEnv->getRootGUIElement();
	core::stringw s;
	CLargeInteger number(4);

	s = L"trained cycles: ";
	number = pMuele->pNeuralNetwork->gamesCount - pMuele->pNeuralNetwork->actualCycle;
	s += number.toString();
	s += " / "; 
	number = pMuele->pNeuralNetwork->gamesCount;
	s += number.toString();
	pTextTrainCount->setText(s.c_str());
	s = L" neurons I/H/O: ";
	s += pMuele->pNeuralNetwork->numberInputNeurons-1;
	s += "/";
	s += pMuele->pNeuralNetwork->numberHiddenNeurons-1;
	s += "/1";
	pTextNeurons->setText(s.c_str());

	s = " training time: ";
	s += (int)(pMuele->pNeuralNetwork->trainingTime / 3600000);
	s += " h  ";
	s += (int)((pMuele->pNeuralNetwork->trainingTime / 60000) % 60);
	s += " m  ";
	s += (int)((pMuele->pNeuralNetwork->trainingTime / 1000) % 60);
	s += " s  ";
	pTextTime->setText(s.c_str());
	s = "    max. error: ";
	s += pMuele->pNeuralNetwork->maxErrorStart;
	s += "/";
	s += pMuele->pNeuralNetwork->maxErrorEnd;
	pTextError->setText(s.c_str());
	s = "    learn rate: ";
	s += pMuele->pNeuralNetwork->trainRateStart;
	s += "/";
	s += pMuele->pNeuralNetwork->trainRateEnd;
	pTextLearnRate->setText(s.c_str());
	s = "     step size: ";
	s += pMuele->pNeuralNetwork->stepSizeStart;
	s += "/";
	s += pMuele->pNeuralNetwork->stepSizeEnd;
	pTextStepSize->setText(s.c_str());


	if ( setHiddenNeurons )
	{
		s = hiddenNeurons;
		pEditNeurons->setText(s.c_str());
	}
	

	if ( pMuele->trainingRunning )
	{
		((gui::IGUIButton*)root->getElementFromId(ID_BUTTON_STOP_TRAINING, true))->setVisible(true);
		((gui::IGUIButton*)root->getElementFromId(ID_BUTTON_CONTINUE_TRAINING, true))->setVisible(false);
	}
	else
	{
		((gui::IGUIButton*)root->getElementFromId(ID_BUTTON_STOP_TRAINING, true))->setVisible(false);
		((gui::IGUIButton*)root->getElementFromId(ID_BUTTON_CONTINUE_TRAINING, true))->setVisible(true);
	}

	if ( pMuele->pNeuralNetwork->actualCycle == 0 )
	{
		((gui::IGUIButton*)root->getElementFromId(ID_BUTTON_STOP_TRAINING, true))->setVisible(false);
		((gui::IGUIButton*)root->getElementFromId(ID_BUTTON_CONTINUE_TRAINING, true))->setVisible(false);
	}
}

/********************************************************************/

void CNeuralNetworkWindow::toggleVisiblility()
{
	pWindow->setVisible(!pWindow->isVisible());
	pMuele->pGuiEnv->getRootGUIElement()->bringToFront(pWindow);
}
