#include <common.hpp>
#include <entities.hpp>
#include <world.hpp>
#include <ui.hpp>

#include <tinyxml2.h>
using namespace tinyxml2;

extern Player	*player;						// main.cpp
extern World	*currentWorld;					// main.cpp

extern Menu  *currentMenu;
extern Menu   pauseMenu;
extern Menu   optionsMenu;

extern std::string xmlFolder;

extern void mainLoop(void);						// main.cpp

extern std::vector<NPC *> AIpreaddr;			// entities.cpp
extern std::vector<int (*)(NPC *)> AIpreload;	// entities.cpp

std::vector<XMLElement *> dopt;

void destroyEverything(void);

inline void segFault() {
	(*((int *)NULL))++;
}

int commonAIFunc(NPC *speaker){
	XMLDocument xml;
	XMLElement *exml,*oxml;

	static unsigned int oldidx = 9999;

	const char *name;
	unsigned int idx = 0;
	bool stop = false;

	/*
	 * Load the current world's XML file into memory for reading.
	 */

	xml.LoadFile(currentXML.c_str());
	exml = xml.FirstChildElement("Dialog");

	/*
	 * Search for the correct dialog block.
	 */

	while(strcmp(exml->Attribute("name"),speaker->name))
		exml = exml->NextSiblingElement();

	exml = exml->FirstChildElement();

	/*
	 * Search for which text block should be used.
	 */

	do{
		if(!strcmp(exml->Name(),"text")){
			if(exml->UnsignedAttribute("id") == (unsigned)speaker->dialogIndex){

				/*
				 * Handle any quest tags
				 */

				if((oxml = exml->FirstChildElement("quest"))){
					std::string qname;

					while ( oxml ) {
						if ( !(qname = oxml->StrAttribute("assign")).empty() )
							player->qh.assign(qname,"None",(std::string)oxml->GetText());
						else if( !(qname = oxml->StrAttribute("check")).empty() ){
							if(player->qh.hasQuest(qname) && player->qh.finish(qname)){
								goto CONT;
							}else{
								oldidx = speaker->dialogIndex;
								speaker->dialogIndex = oxml->UnsignedAttribute("fail");
								return commonAIFunc(speaker);
							}
						}

						oxml = oxml->NextSiblingElement();
					}
				}

CONT:

				/*
				 * Handle any 'give' requests.
				 */

				if((oxml = exml->FirstChildElement("give"))){
					while(oxml){
						player->inv->addItem(oxml->Attribute("id"),oxml->UnsignedAttribute("count"));
						oxml = oxml->NextSiblingElement();
					}
				}

				/*
				 * Handle any 'take' requests.
				 */

				if((oxml = exml->FirstChildElement("take"))){
					while(oxml){
						player->inv->takeItem(oxml->Attribute("id"),oxml->UnsignedAttribute("count"));
						oxml = oxml->NextSiblingElement();
					}
				}

				/*
				 * Handle dialog options.
				 */

				if((oxml = exml->FirstChildElement("option"))){

					/*
					 * Convert the list of options into a single colon-separated string.
					 */

					std::string optstr;

					while(oxml){

						/*
						 * Create a buffer big enough for the next option.
						 */

						optstr.append((std::string)":" + oxml->Attribute("text"));

						/*
						 * Append the next option.
						 */

						dopt.push_back(oxml);

						oxml = oxml->NextSiblingElement();
					}

					/*
					 * Get the player's choice, then set the XMLElement to the option's block.
					 */

					ui::dialogBox(speaker->name,optstr.c_str(),false,exml->GetText()+1);
					ui::waitForDialog();

					if(ui::dialogOptChosen)
						exml = dopt[ui::dialogOptChosen-1];

					while(!dopt.empty())
						dopt.pop_back();
				}else{

					/*
					 * No options - simply print the text.
					 */

					ui::dialogBox(speaker->name,NULL,false,exml->GetText());
					ui::waitForDialog();
				}

				/*
				 * Give another NPC dialog if requested.
				 */

				if((name = exml->Attribute("call"))){
					for(auto &n : currentWorld->npc){
						if(!strcmp(n->name,name)){
							if(exml->QueryUnsignedAttribute("callid",&idx) == XML_NO_ERROR)
								n->dialogIndex = idx;
							n->addAIFunc(commonAIFunc,false);
							break;
						}
					}
				}

				/*
				 * Handle the next dialog block if this one leads to another.
				 */

				if(exml->QueryUnsignedAttribute("nextid",&idx) == XML_NO_ERROR){
					speaker->dialogIndex = idx;

					if(exml->QueryBoolAttribute("stop",&stop) == XML_NO_ERROR && stop){
						speaker->dialogIndex = 9999;
						return 0;
					}else if(exml->QueryBoolAttribute("pause",&stop) == XML_NO_ERROR && stop){
						//speaker->dialogIndex = 9999;
						return 1;
					}else return commonAIFunc(speaker);
				}else{
					if(oldidx != 9999){
						speaker->dialogIndex = oldidx;
						oldidx = 9999;
						return 1;
					}else{
						speaker->dialogIndex = 9999;
						return 0;
					}
				}
				//return 1;
			}
		}

		exml = exml->NextSiblingElement();

	}while(exml);

	return 0;
}

void commonPageFunc( Mob *callee ){
	static bool lock = false;

	if ( !lock ) {
		lock = true;

		ui::drawPage( callee->heyid );
		ui::waitForDialog();

		callee->alive = false;
		lock = false;
	}
}

void commonTriggerFunc(Mob *callee){
	static bool lock = false;
	XMLDocument xml;
	XMLElement *exml;

	char *text,*pch;
	if(!lock){
		lock = true;

		xml.LoadFile(currentXML.c_str());
		exml = xml.FirstChildElement("Trigger");

		while(strcmp(exml->Attribute("id"),callee->heyid.c_str()))
			exml = exml->NextSiblingElement();

		player->vel.x = 0;

		ui::toggleBlackFast();
		ui::waitForCover();

		text = new char[256];
		strcpy(text,exml->GetText());
		pch = strtok(text,"\n");

		while(pch){
			ui::importantText(pch);
			ui::waitForDialog();

			pch = strtok(NULL,"\n");
		}

		delete[] text;

		ui::toggleBlackFast();

		callee->alive = false;
		lock = false;
	}
}

void initEverything(void){
	std::vector<std::string> xmlFiles;
	XMLDocument xml;

	/*
	 * Read the XML directory into an array.
	 */

	C("Scanning XML directory");
	if(getdir(std::string("./"+xmlFolder).c_str(),xmlFiles)){
		std::cout<<"Error reading XML files!!!1"<<std::endl;
		abort();
	}
	C("Done scanning XML directory");

	/*
	 * Sort the files alphabetically.
	 */

	C("Sorting XML files alphabetically");
	strVectorSortAlpha(&xmlFiles);
	C("Dpne sorting XML files alphabetically");


	/*
	 * Load the first file found as currentWorld.
	 */

	for(unsigned int i=0;i<xmlFiles.size();i++){
		if(xmlFiles[i] != "." && xmlFiles[i] != ".." && strcmp(xmlFiles[i].c_str()+xmlFiles[i].size()-3,"dat")){

			/*
			 * Read in the XML file.
			 */
			C("Setting current XML file");
			std::cout << "File to load: " << xmlFiles[i] << std::endl;
			currentWorld = loadWorldFromXML(xmlFiles[i]);
			C("Done setting current XML file");
			break;
		}
	}

	pauseMenu.items.push_back(ui::createParentButton({-256/2,0},{256,75},{0.0f,0.0f,0.0f}, "Resume"));
	pauseMenu.items.push_back(ui::createChildButton({-256/2,-100},{256,75},{0.0f,0.0f,0.0f}, "Options"));
	pauseMenu.items.push_back(ui::createButton({-256/2,-200},{256,75},{0.0f,0.0f,0.0f}, "Save and Quit", ui::quitGame));
	pauseMenu.items.push_back(ui::createButton({-256/2,-300},{256,75},{0.0f,0.0f,0.0f}, "Segfault", segFault));
	pauseMenu.child = &optionsMenu;
	pauseMenu.parent = NULL;


	optionsMenu.items.push_back(ui::createSlider({0-(float)SCREEN_WIDTH/4,0-(512/2)}, {50,512}, {0.0f, 0.0f, 0.0f}, 0, 100, "Master", &VOLUME_MASTER));
	optionsMenu.items.push_back(ui::createSlider({-200,100}, {512,50}, {0.0f, 0.0f, 0.0f}, 0, 100, "Music", &VOLUME_MUSIC));
	optionsMenu.items.push_back(ui::createSlider({-200,000}, {512,50}, {0.0f, 0.0f, 0.0f}, 0, 100, "SFX", &VOLUME_SFX));
	optionsMenu.child = NULL;
	optionsMenu.parent = &pauseMenu;
	// optionsMenu.push_back(ui::createButton({-256/2,-200},{256,75},{0.0f,0.0f,0.0f}, (const char*)("Save and Quit"), );

	/*
	 * Spawn the player and begin the game.
	 */

	player = new Player();
	player->sspawn(0,100);

	currentWorld->bgmPlay(NULL);
	atexit(destroyEverything);
}

void destroyEverything(void){
	currentWorld->save();
	//delete currentWorld;
	//delete[] currentXML;

	while(!AIpreload.empty())
		AIpreload.pop_back();
	while(!AIpreaddr.empty())
		AIpreaddr.pop_back();
}