#include <unistd.h>
#include <application.h>
#include <windows.h>
#include <statusbar.h>
#include <menubar.h>
#include <textbox.h>
#include <dialogs.h>
#include <document.h>
#include <filebuffer.h>
#include <editwindow.h>
#include <clipboard.h>
#include <globalfns.h>
#include <vector>
#include <signal.h>
#include <string>

Application :: Application(string *name)
{
	Window :: initialize(); //ncurses initialization routines
	bar = new Menubar;
	status = new Statusbar(1,80,24,0,Window::NOBORD,Window::YESCOUNTBORD,COLOR_BLACK,COLOR_WHITE);

	generateDialogs();

	commandLineFilename = *name;
	toQuit = false;
}

void Application :: run()
{
	initialize();
	int ch,modifier;

	while(toQuit == false)
	{
		ch = docArray[curDoc]->edwin->getChar();
		modifier = get_modifier();
		if(modifier == 8) //ALT is pressed
		{
			curs_set(0);
			int choice = bar->menuHandler(ch);
			if(choice != -1)
				processChoice(choice);
			curs_set(1);
		}
		else if(modifier == 4) //CTRL is pressed
		{
			checkCtrl(ch);
		}
		else
		{
			switch(ch)
			{
				case KEY_F(1):
					about();
					break;
				case KEY_F(2):
					saveFile();
					break;
				case KEY_F(3):
					findString(true); 
					//true indicates findnext
					break;
				case KEY_F(4):
					closeFile(); 
					//true indicates findnext
					break;
				case KEY_F(5):
					switchDocs(false);
					//false indicates previous doc
					break;
				case KEY_F(6):
					switchDocs();
					break;
				default:
					insertStatusMessage("");
					docArray[curDoc]->edwin->processKey(ch);
					break;
			}
		}
	}
}

void Application :: initialize()
{
	ESCDELAY = 0;
				       
	bar->addWindow();
	status->addWindow();
	
	//opens file specified on command line or creates new file
	firstDoc();

	ClipBoard *temp = new ClipBoard;
	clipArray.push_back(temp);
	curClip = 0;
}

//opens file specified on command line or creates new file
void Application :: firstDoc()
{
	
	if(commandLineFilename.size() == 0)
	{
		string unnamed;
		Document *temp = new Document(unnamed);
		docArray.push_back(temp);
		curDoc = 0;
		docArray[curDoc]->edwin->reload();
		docArray[curDoc]->addWindow();
	}
	else
		openFile(commandLineFilename);
}

//closes current file without saving and creates new one in its place
void Application :: newFile()
{
	string unnamed;

	if(curDoc >= 0)
	//so that newFile doesn't fail even when called when no files are open
	//E.g: error opening commandLine file
	{
		delete docArray[curDoc];
	       vector <Document*>::iterator docIter = docArray.begin() + curDoc;
		docArray.erase(docIter);
	}

	Document *temp = new Document(unnamed);
	docArray.push_back(temp);
	curDoc = docArray.size() - 1;
	docArray[curDoc]->edwin->reload();
	docArray[curDoc]->addWindow();
}
	
void Application :: openFile(string filename)
{
	if(filename.size() == 0)//filename has not been specified by other means
	{
	//get Filename from dialog box and set it in document
		filename = fileOpenDialog->readInput();
		if(filename.size() == 0)
			return;
	}

	Document *temp = new Document(filename);
	docArray.push_back(temp);
	curDoc = docArray.size()-1 ;

	insertStatusMessage("Opening file...");
	int error = docArray[curDoc]->open(filename);
	
	//Error while opening file
	switch(error)
	{
		case 0: //no error
			docArray[curDoc]->edwin->reload();
			docArray[curDoc]->addWindow();
			insertStatusMessage("");
			break;
		default: //some error
			 delete docArray[curDoc];
			 docArray.pop_back();
			 curDoc = docArray.size() - 1;
			 insertStatusMessage("Error opening file!!");
			 if(curDoc < 0)
			//E.g: error opening commandLine file
				 newFile();
			 break;
	}
}

void Application :: saveFile()
{
	//check if file is named or unnamed
	string filename = docArray[curDoc]->getName();
	if(filename.size() == 0)
		filename = fileSaveDialog->readInput();
	if(filename.size() == 0)
		return;
	insertStatusMessage("Saving file...");
	int error = docArray[curDoc]->save(filename);
	switch(error)
	{
		case 0:
			//no error
			insertStatusMessage("File saved");
			docArray[curDoc]->setName(filename);
			break;
		default:
			insertStatusMessage("Error saving file!!");
			docArray[curDoc]->setName("");
			break;
	}
}

void Application :: saveAs()
{
	//display old filename in SaveAs dialog box
	string oldName = docArray[curDoc]->getName();
	string newName = fileSaveAsDialog->readInput(oldName);

	//if valid filename is input
	if(newName.size() != 0)
	{
		docArray[curDoc]->setName(newName);
		saveFile();
	}
}

void Application :: closeFile()
{
	if(docArray[curDoc]->isModified())
	{
		int choice = saveConfirmDialog->readInput();
		switch(choice)
		{
			case 0:
				saveFile();
				break;
			case 1:
				break;
			case 2://menu is cancelled
			default: 
				return;
		}
	}
	//if this is the only file open, create a new file
	if(docArray.size() == 1)
		newFile();
	else
	//delete current document and move to previous document
	{
		delete docArray[curDoc];
	    vector <Document*> :: iterator docIter = docArray.begin() + curDoc;
		docArray.erase(docIter);
		if(curDoc == 0)
			curDoc = docArray.size() - 1;
		else
			curDoc--;
		docArray[curDoc]->edwin->reload();
		docArray[curDoc]->addWindow();
	}
}

void Application :: quit()
{
	//make current document inactive
	docArray[curDoc]->edwin->reload(false);

	//make last document opened as active	
	curDoc = docArray.size()-1;
	while(docArray.size() > 0)
	{
		//make current document as active
		docArray[curDoc]->edwin->reload();
		docArray[curDoc]->addWindow();

		if(docArray[curDoc]->isModified() == true)
		{
			int choice = saveConfirmDialog->readInput();
			switch(choice)
			{
				case 0: //save 
					saveFile();
					break;
				case 1:	//don't save	
					break;
				case 2: //indicates Menu is cancelled
				default: 
					return;
			}
		}

		//delete current document from the vector
		docArray[curDoc]->edwin->reload(false);
		delete docArray[curDoc];
		vector<Document*>::iterator docIter = docArray.begin() + curDoc;
		docArray.erase(docIter);

		//update curDoc
		curDoc--;
		
	}
	toQuit = true;
}

void Application :: deleteFile()
{
	string filename = deleteFileDialog->readInput();
	int error = unlink(filename.c_str());
	switch(error)
	{
		case 0:
			insertStatusMessage("Deleted file");
			break;
		default:
			insertStatusMessage("Error deleting file!!");
			break;
	}			
}

void Application :: findString(bool findnext)
{
	if(findnext)
	{
		if(findstring.size() == 0)
			findstring = searchStringDialog->readInput();
	}
	else
		findstring = searchStringDialog->readInput(findstring);

	if(findstring.size() == 0)
		return;

	int logL,logC;
	if(docArray[curDoc]->find(findstring,logL,logC,findnext))
	{
		docArray[curDoc]->edwin->highlightString(findstring,logL,logC);
	}
	else
		insertStatusMessage("Not Found!");
}

void Application :: replaceString(bool replaceall)
{
	string oldStr;
	if(findstring.size() == 0)
	{
		oldStr = searchStringDialog->readInput();
		if(oldStr.size() == 0)
		{
			findstring = oldStr;
			return;
		}
	}
	else
		oldStr = findstring;

	string newStr = replaceStringDialog->readInput();
	if(newStr.size() == 0)
		return;
	int count = docArray[curDoc]->replace(oldStr,newStr,replaceall);
	//NOTE: count is a base 0 value. C-style
	if(count == -1)
		insertStatusMessage("No matches found!");
	else
	{
		char buf[1024];
		sprintf(buf,"Replaced %d occurrences",count+1);
		insertStatusMessage(buf);
		docArray[curDoc]->edwin->reload();
	}
}

void Application :: blockBegin()
{
	docArray[curDoc]->edwin->blockBegin();
}

void Application :: blockEnd()
{
	//mark end of block and highlight 
	if(docArray[curDoc]->edwin->blockEnd() == 0)
		return;
	docArray[curDoc]->edwin->startHighlight();
	docArray[curDoc]->edwin->reload();
	docArray[curDoc]->edwin->startHighlight(false);
}

void Application :: blockCopy(bool cut)
{
	if( ! docArray[curDoc]->copy(clipArray[curClip],cut) )
		return;

	ClipBoard *temp = new ClipBoard;
	clipArray.push_back(temp);
	curClip++;
	if(cut)
		docArray[curDoc]->edwin->reload();
}

void Application :: blockPaste(bool replace)
{
	if(curClip == 0)
	{
		insertStatusMessage("Nothing in Clipboard!!");
		return;
	}
	docArray[curDoc]->paste(clipArray[curClip-1],replace);	
}

void Application :: setRightMargin()
{
	string rightStr = toString(docArray[curDoc]->edwin->getRtMar());
	insertStatusMessage("Enter right margin >=0");
	rightStr = rightMarginDialog->readInput(rightStr);
	insertStatusMessage("");
	if(rightStr.size() == 0)
		return;
	int rtMar = toNum(rightStr);
	docArray[curDoc]->setRightMargin(rtMar);
}

void Application :: setLeftMargin()
{
	string leftStr = toString(docArray[curDoc]->edwin->getLtMar());
	insertStatusMessage("Enter left margin >=0");
	leftStr = leftMarginDialog->readInput(leftStr);
	insertStatusMessage("");
	if(leftStr.size() == 0)
		return;
	int ltMar = toNum(leftStr);
	docArray[curDoc]->setLeftMargin(ltMar);
}

void Application :: setTopMargin()
{
	string topStr = toString(docArray[curDoc]->edwin->getTopMar());
	insertStatusMessage("Enter top margin >=0");
	topStr = topMarginDialog->readInput(topStr);
	if(topStr.size() == 0)
		return;
	int topMar = toNum(topStr);
	docArray[curDoc]->setTopMargin(topMar);
}

void Application :: setBottomMargin()
{
	string bottomStr = toString(docArray[curDoc]->edwin->getBotMar());
	insertStatusMessage("Enter bottom margin >=0");
	bottomStr = bottomMarginDialog->readInput(bottomStr);
	insertStatusMessage("");
	if(bottomStr.size() == 0)
		return;
	int botMar = toNum(bottomStr);
	docArray[curDoc]->setBottomMargin(botMar);
}

void Application :: setTop()
{
	string topStr = toString(docArray[curDoc]->edwin->getTop());
	insertStatusMessage("Enter top position between 1 and 19");
	topStr = topPositionDialog->readInput(topStr);
	insertStatusMessage("");
	if(topStr.size() == 0)
		return;
	int top = toNum(topStr);
	docArray[curDoc]->setPosition(top,0);
}

void Application :: setLeft()
{
	string leftStr = toString(docArray[curDoc]->edwin->getLeft());
	insertStatusMessage("Enter left position between 0 and 75");
	leftStr = leftPositionDialog->readInput(leftStr);
	insertStatusMessage("");
	if(leftStr.size() == 0)
		return;
	int left = toNum(leftStr);
	docArray[curDoc]->setPosition(0,left);
}

void Application :: setHeight()
{
	string htStr = toString(docArray[curDoc]->edwin->getHeight());
	insertStatusMessage("Enter height between 5 and 23");
	htStr = heightDialog->readInput(htStr);
	insertStatusMessage(" ");
	if(htStr.size() == 0)
		return;
	int ht = toNum(htStr);
	docArray[curDoc]->setDimensions(ht,0);
}

void Application :: setWidth()
{
	string wdStr = toString(docArray[curDoc]->edwin->getWidth());
	insertStatusMessage("Enter width between 5 and 80");
	wdStr = widthDialog->readInput(wdStr);
	insertStatusMessage("");
	if(wdStr.size() == 0)
		return;
	int wd = toNum(wdStr);
	docArray[curDoc]->setDimensions(0,wd);
}
	
void Application :: switchDocs(bool next)
{
	docArray[curDoc]->edwin->reload(false); //false indicates inactive
	if(next == true)
	{
		if((unsigned)curDoc == docArray.size()-1)
			curDoc = 0;
		else
			curDoc++;
	}
	else
	{
		if(curDoc == 0)
			curDoc = docArray.size() -1;
		else
			curDoc--;
	}
	docArray[curDoc]->edwin->reload(); 
	docArray[curDoc]->addWindow();
}

void Application :: about()
{
	aboutWindow->setCursorVisibility(Window :: CURSOR_INVISIBLE);
	aboutWindow->addWindow();
	aboutWindow->getChar();
	aboutWindow->removeWindow();
	aboutWindow->setCursorVisibility(Window :: CURSOR_NORMAL);
}

void Application :: generateDialogs()
{
	fileOpenDialog = new DialogBox("FILE OPEN","Enter Filename");
	fileSaveDialog = new DialogBox("FILE SAVE","Enter Filemame");
	fileSaveAsDialog = new DialogBox("SAVE FILE AS", "Enter Filename");
	deleteFileDialog = new DialogBox("DELETE FILE", "Enter Filename");
 	searchStringDialog = new DialogBox("SEARCH", "Enter search string");
 	replaceStringDialog = new DialogBox("REPLACE", "Enter replace string");

      rightMarginDialog = new DialogBox("RIGHT MARGIN","Enter right margin",4);
	leftMarginDialog = new DialogBox("LEFT MARGIN","Enter left margin",4);
	topMarginDialog = new DialogBox("TOP MARGIN","Enter top margin",4);
    bottomMarginDialog = new DialogBox("BOTTOM MARGIN","Enter bottom margin",4);

    topPositionDialog = new DialogBox("TOP POSITION","Enter top position",4);
    leftPositionDialog = new DialogBox("LEFT POSITION","Enter left position",4);
	heightDialog = new DialogBox("HEIGHT OF WINDOW","Enter height",4);
	widthDialog = new DialogBox("WIDTH OF WINDOW","Enter width",4);
saveConfirmDialog = new DialogMenu("FILE NOT SAVED","Current File not saved...Save?",3,"`Yes","`No","`Cancel");
	createAboutWindow();
}

void Application :: createAboutWindow()
{
	aboutWindow=new Window(11,30,5,25,Window::YESBORD,Window::YESCOUNTBORD);
	aboutWindow->setColorCombination(COLOR_BLACK,COLOR_WHITE);
	aboutWindow->setCursorPosition(1,14);
	aboutWindow->writeText("ri");
	aboutWindow->setCursorPosition(3,8);
	aboutWindow->writeText("A TEXT EDITOR");
	aboutWindow->setCursorPosition(5,14);
	aboutWindow->writeText("BY");
	aboutWindow->setCursorPosition(7,6);
	aboutWindow->writeText("S PRAMOD BILIGIRI");
	aboutWindow->setCursorPosition(9,10);
	aboutWindow->writeText("K. S. I. T");
	aboutWindow->enableKeypad();
}

void Application :: processChoice(int choice)
{
	switch(choice)
	{
		case Menubar :: NEWFILE:
			newFile();
			break;

		case Menubar :: OPENFILE:	
			openFile();
			break;

		case Menubar :: SAVEFILE:
			saveFile();
			break;

		case Menubar :: SAVEAS:
			saveAs();
			break;

		case Menubar :: CLOSEFILE:
			closeFile();
			break;

		case Menubar :: QUIT:
			quit();
			break;

		case Menubar :: DELETEFILE:
			deleteFile();
			break;

		case Menubar :: STARTBLK:
			blockBegin();
			break;

		case Menubar :: ENDBLK:
			blockEnd();
			break;

		case Menubar :: CUT:
			blockCopy(true); //true indicates cut
			break;

		case Menubar :: COPY:
			blockCopy(); 
			break;

		case Menubar :: PASTE:
			blockPaste();
			break;

		case Menubar :: FIND:
			findString();
			break;

		case Menubar :: FINDNEXT:
			findString(true); //true indicates "findnext"
			break;

		case Menubar :: REPLACE:
			replaceString(true); //true indicates "replaceall" 
			break;

		case Menubar :: RIGHTM:
			setRightMargin(); 
			break;

		case Menubar :: LEFTM:
			setLeftMargin(); 
			break;

		case Menubar :: TOPM:
			setTopMargin(); 
			break;

		case Menubar :: BOTTOMM:
			setBottomMargin(); 
			break;

		case Menubar :: SETH:
			setHeight(); 
			break;

		case Menubar :: SETW:
			setWidth(); 
			break;

		case Menubar :: SETT:
			setTop(); 
			break;

		case Menubar :: SETL:
			setLeft(); 
			break;

		case Menubar :: NEXTFILE:
			switchDocs(); 
			break;

		case Menubar :: PREVFILE:
			switchDocs(false);  //false indicates prev. file
			break;

		case Menubar :: ABOUT:
			about();
			break;
	}
}

void Application :: insertStatusMessage(const char *message)
{
	status->insertMessage(message);
}

void Application :: insertStatusMessage(int line,int col)
{
	status->insertMessage(line,col);
}

void Application :: checkCtrl(int ch)
{
	switch(ch)
	{
		case 15: //^O
			openFile();
			break;

		case 14: //^N
			newFile();
			break;			

		case 1: //^A
			saveAs();
			break;
			
		case 4: //^D
			quit();
			break;

		case 5: //^E
			blockBegin();
			break;

		case 18: //^R
			blockEnd();
			break;

		case 16: //^P
			blockCopy();
			break;
			
		case 24: //^X
			blockCopy(true); //true indicates cut
			break;
			
		case 22: //^V
			blockPaste(); 
			break;

		case 6: //^F
			findString();
			break;
			
		case 7: //^G
			replaceString(true); //true indicates "replaceall" 
			break;
			
		case 8: //^H
			setHeight();
			break;

		case 23: //^W
			setWidth();
			break;

		case 20: //^T
			setTop();
			break;

		case 12: //^L
			setLeft();
			break;
	}
}
	
Application :: ~Application()
{
	delete bar;
	delete status;

	for(int i=0;(unsigned)i < docArray.size();i++)
		delete docArray[i];     
	for(int i=0;(unsigned)i < clipArray.size();i++)
		delete clipArray[i];     

	delete fileOpenDialog ;
	delete fileSaveDialog ;
	delete fileSaveAsDialog ;
	delete deleteFileDialog;
	delete saveConfirmDialog;

	delete searchStringDialog; 
	delete replaceStringDialog; 

	delete rightMarginDialog; 
	delete leftMarginDialog; 
	delete topMarginDialog ;
	delete bottomMarginDialog; 

	delete topPositionDialog ;
	delete leftPositionDialog ;
	delete heightDialog;
	delete widthDialog;  
	endwin();
}
