#include <filebuffer.h>
#include <textbox.h> //for tabSpace
#include <vector>
#include <iostream>
#include <fstream>
#include <string>
#include <textbox.h>

FileBuffer :: FileBuffer()
{
	modified = false;
}

int FileBuffer :: readData(string name)
{
	string *line;
	ifstream infile (name.c_str());
	if(!infile)
		return(-3); //Error while opening file
	char ch;
	line = new string;
	while(infile.get(ch))
	{
		if(!((ch >= 32 && ch < 127) || ch == '\n' || ch == '\t'))
                     continue;

		if(ch == '\t')
		{
			for(int i = 1; i <= TextBox::tabSpace; i++)
				line->push_back(' ');
		}
		if(ch>=32 && ch <=126)
		{
			line->push_back(ch);
		}
		if(ch == '\n')
		{
			lineVec.push_back(line);
			line = new string;
		}
	}
	modified = false;
	return(0); //success
}

int FileBuffer :: writeData(string name)
{
	ofstream outfile (name.c_str());
	if( !outfile )
		return(-3); //error opening file
	vector<string*>::iterator lineIter = lineVec.begin();

	while(lineIter != lineVec.end())	
	{
		outfile << **lineIter << endl;
		lineIter++;
	}
	modified = false;
	return(0); //success
}

int FileBuffer :: getLength(int line)
{
	if((unsigned)line == lineVec.size())
		return(0);
	vector<string*>::iterator lineIter = lineVec.begin() + line;
	return((*lineIter)->size());
}

int FileBuffer :: getNumLines()
{
	return(lineVec.size());
}

//returns character at line & col, or -1 on error
int FileBuffer :: getChar(int line,int col)
{
	vector<string*>::iterator lineIter = lineVec.begin();
	while(lineIter != lineVec.end() && line > 0)
	{
		line--;
		lineIter++;
	}
	if(lineIter == lineVec.end())
		return(-1);
	if((unsigned)col > (*lineIter)->size())
		return(-1);
	return((*lineIter)->at(col));
}

void FileBuffer :: display()
{
	vector<string*>::iterator lineIter = lineVec.begin();

	while( lineIter !=  lineVec.end() )
       	{
		cout << **lineIter << endl;
		lineIter++;
	}
}
			
void FileBuffer :: delLine(int line,int startCol,int numCols)
{
	vector <string*> :: iterator lineIter = lineVec.begin() + line;

	if(startCol == -1)
		startCol = 0;
	if(numCols == -1)
		numCols = (*lineIter)->size() - startCol;

	(*lineIter)->erase(startCol,numCols);
	
	if((*lineIter)->size() == 0)
	{
		delete(*lineIter);
		lineVec.erase(lineIter);
	}
	modified = true;
}

void FileBuffer :: clearLine(int line,int col)
{
	if((unsigned)line == lineVec.size())
		lineVec.push_back(new string);
	
	vector <string*> :: iterator lineIter = lineVec.begin() + line;
	string :: iterator s_iter = (*lineIter)->begin() + col;
	(*lineIter)->erase(s_iter,(*lineIter)->end());
	modified = true;
}

void FileBuffer :: insChar(char ch,int line,int col)
{
	if((unsigned)line == lineVec.size())
	{
		string *temp = new string;
		temp->push_back(ch);
		lineVec.push_back(temp);
	}
	else
	{
		vector <string*> :: iterator lineIter = lineVec.begin() + line;
		(*lineIter)->insert(col,1,ch);
	}
	modified = true;
}

void FileBuffer :: insLine(string item,int line)
{
	string *temp = new string(item);
	if((unsigned)line == lineVec.size())
		lineVec.push_back(temp);
	else
		lineVec.insert(lineVec.begin()+line,temp);
	modified = true;
}

string* FileBuffer :: getString(int line,int startCol,int numCols)
{
	if((unsigned)line == lineVec.size())
		return((new string));
	vector <string*> :: iterator lineIter = lineVec.begin() + line;
	string *temp;
	if(startCol == -1)
		startCol = 0;
	if(numCols == -1)
		numCols = (*lineIter)->size();
	temp = new string((*lineIter)->substr(startCol,numCols));
	return(temp);
}

void FileBuffer :: putString(string *item,int line,int startCol)
{
	vector <string*> :: iterator lineIter = lineVec.begin() + line;
	if(startCol == -1)
		startCol = (*lineIter)->size();
	(*lineIter)->insert(startCol,*item);
	modified = true;
}

int FileBuffer :: findString(string item,int &logLine,int &logColumn,int startLine,int startColumn)
{
	vector <string*> :: iterator lineIter = lineVec.begin() + startLine;
	string::size_type pos; //to store index or npos (if not found)

	while(lineIter != lineVec.end())
	{
		pos = (*lineIter)->find(item,startColumn);
		if(pos != string::npos) //if item found
		{
			//calculate line number 
			logLine = lineIter - lineVec.begin();
			logColumn = pos;

			return(1);
		}
		lineIter++;
		startColumn = 0;
	}
	logLine = logColumn = -1;
	return(0);
}

int FileBuffer :: replaceString(string oldStr,string newStr,int startLine,int startColumn,bool all)
{
	int count=-1;
	int logL,logC;
	vector <string*> :: iterator lineIter;
	if(all == false) //replace only next occurrence
	{
		if(findString(oldStr,logL,logC,startLine,startColumn))
		{
			lineIter = lineVec.begin() + logL;
			(*lineIter)->replace(logC,oldStr.size(),newStr);
			count ++;
		}
	}
	else //replace all occurrences
	{
		while(findString(oldStr,logL,logC,startLine,startColumn))
		{
			lineIter = lineVec.begin() + logL;
			(*lineIter)->replace(logC,oldStr.size(),newStr);
			count++;
			startLine = logL;
			startColumn = logC;
			modified = true;
		}
	}
	return(count);
}

void FileBuffer :: copy(vector<string*> &buf,bool cut,int startLine,int startColumn,int endLine,int endColumn)
{
	vector <string*> :: iterator lineIter, blockEnd;

	lineIter = lineVec.begin() + startLine;	
	blockEnd = lineVec.begin() + endLine;

	//if block has only one line
	if(startLine == endLine)
	{
	string *temp = getString(startLine,startColumn,endColumn-startColumn+1);
		buf.push_back(temp);
		if(cut)
			delLine(startLine,startColumn,endColumn-startColumn+1);
		return;
	}

	//for the first line, copy from startColumn 
buf.push_back(new string((*lineIter)->begin()+startColumn, (*lineIter)->end()));

	if(cut)
	{
		if(startColumn == 0)
		{
			//delete line from vector itself
			delete (*lineIter);
			lineIter = lineVec.erase(lineIter);
		}
		else
		{
			//delete part of line
			(*lineIter)->erase(startColumn);
			lineIter++;
		}
	}
	else
		lineIter++;
	startLine++; //needed to cut data 
	
	//for the remaining lines, copy the entire line
	while(startLine < endLine)
	{
	     buf.push_back(new string((*lineIter)->begin(),(*lineIter)->end()));
		if(cut)
		{
			delete (*lineIter);
			lineIter = lineVec.erase(lineIter);
		}
		else
			lineIter++;
		startLine++;
	}

	//for last line, copy till endColumn  
	//+1 _probably_ 'cos end iterator must be one _past_ the limit
	string :: iterator strEnd; 
	strEnd = (*lineIter)->begin() + endColumn + 1;
	buf.push_back(new string((*lineIter)->begin(),strEnd));
	if(cut)
	{
		delete (*lineIter);
		lineIter = lineVec.erase(lineIter);
	}
}

void FileBuffer :: paste(vector <string*> *buf,bool replace,int startLine,int startCol)
{
	vector <string*> :: iterator lineIter;
	string *remain;
	vector <string*> :: iterator bufIter = buf->begin();

	lineIter = lineVec.begin() + startLine;

	//if pasting into empty file
	if(lineVec.size() == 0)
	{
		lineVec.push_back(new string(**bufIter));
		lineIter = lineVec.end();
		remain = new string; //empty string..to append to last line
		//in clipboard
		
	}
	else
	{
	//store the part of first line and erase it from the file
 	  remain = new string((*lineIter)->begin()+startCol,(*lineIter)->end());
	 (*lineIter)->erase((*lineIter)->begin() + startCol,(*lineIter)->end());
		(**lineIter) = (**lineIter) + (**bufIter);
		lineIter++;
	}
	
	//first line pasted, so increment clipboard pointer
	bufIter++;

	//now just insert entire lines till the last line in the clipboard
	string *temp; 
	while(bufIter != buf->end())
 	{
		temp = new string (**bufIter);

		if( lineIter == lineVec.end() )
		{
			lineVec.push_back(temp);
			//since a line is added, update lineIter so it still 
			//points to original insertion point
			lineIter = lineVec.end();
		}
		else
		{
			lineVec.insert(lineIter,temp);
			//since a line is added, increment lineIter so it still 
			//points to original insertion point
			lineIter++;
		}
		//increment clipboard iterator 
		bufIter++; 
	}

	//move to the last line of the added block (-1 'cos the 1st line of
	//buffer is appended to startLine itself)
	lineIter = lineVec.begin() + startLine + buf->size() - 1;

	//append the (erased) remaining part of first line to the end of the
	//last line of the block
	(**lineIter) = (**lineIter) + (*remain);
	modified = true;
}

bool FileBuffer :: isModified()
{
	return(modified);
}
