#include "CodeEditor.h"

namespace Upp {

PythonSyntax::PythonSyntax() {}

void PythonSyntax::Highlight(const wchar *start, const wchar *end, HighlightOutput& hls, CodeEditor *editor, int line, int pos)
{
	InitKeywords();
	
	bool isComment = false;
	bool isStr = false;
	char strOpening;
	
	const wchar* p = start;
	while(p < end) {
		if((*p == '#' || isComment) && !isStr) {
			isComment = true;
			hls.Put(hl_style[INK_COMMENT]);
		}
		else
		if(*p == '\'' || *p == '\"' || isStr) {
			hls.Put(hl_style[INK_CONST_STRING]);
			if((*p == '\'' || *p == '\"') && p - 1 != start && *(p - 1) != '\\')
				if (!isStr || strOpening == *p) {
					isStr = !isStr;
					strOpening = (char)*p;
				}
		}
		else
		if(IsSeparator(p) || p == start) {
			WString w;
			bool isW = false;
			const wchar* bp = (p == start && !IsSeparator(p)) ? p : p + 1;
			while (bp != end && !IsSeparator(bp))
				w += *bp++;
			
			bool isPutted = false;
			if(IsSeparator(p)) {
				hls.Put(hl_style[INK_NORMAL]);
				isPutted = true;
			}
			if(IsKeyword(w)) {
				hls.Put(w.GetLength(), hl_style[INK_KEYWORD]);
				isW = true;
			}
			else
			if(IsSpecialVar(w)) {
				hls.Put(w.GetLength(), hl_style[INK_UPP]);
				isW = true;
			}
			else
			if(IsNumber(w)) {
				hls.Put(w.GetLength(), hl_style[INK_CONST_INT]);
				isW = true;
			}
			
			if(isW) {
				p += w.GetLength() - (isPutted ? 0 : 1);
			}
		}
		else
			hls.Put(hl_style[INK_NORMAL]);
		
		p++;
	}
}

void PythonSyntax::IndentInsert(CodeEditor& editor, int chr, int count)
{
	if(chr == '\n') {
		while(count--) {
			WString cursorLine = editor.GetWLine(editor.GetCursorLine());
			editor.InsertChar('\n', 1);
			
			Identation::Type idType = FindIdentationType(editor, cursorLine);
			char idChar = GetIdentationByType(idType);
			int mult = 1;
			if(idType == Identation::Space)
				mult = CalculateSpaceIndetationSize(editor);
			if(LineHasColon(cursorLine))
				editor.InsertChar(idChar, mult);
			editor.InsertChar(idChar, CalculateLineIndetations(cursorLine, idType));
		}
	}
	if(count > 0)
		editor.InsertChar(chr, count);
}

bool PythonSyntax::IsSeparator(const wchar* c)
{
	return    *c == ' ' || *c == ':' || *c == ',' || *c == '.' || *c == '('
	       || *c == ')' || *c == '[' || *c == ']' || *c == '{' || *c == '}'
	       || *c == '\t';
}

bool PythonSyntax::IsKeyword(const WString& w)
{
	return keywords.Find(w.ToString()) > -1;
}

bool PythonSyntax::IsSpecialVar(const WString& w)
{
	return specialVars.Find(w.ToString()) > -1;
}

bool PythonSyntax::IsNumber(const WString& w)
{
	RegExp exp("^-?[0-9]+$");
	return exp.Match(w.ToString());
}

void PythonSyntax::InitKeywords()
{
	static const char* pythonKeywords[] = {
		"and", "as", "assert", "break", "class", "continue", "def",
		"del", "elif", "else", "except", "finally", "for",
		"from", "global", "if", "import", "in", "is", "lambda",
		"not", "or", "pass", "raise", "return", "try",
		"while", "with", "yield",
		"None", "True", "False",
		NULL
	};
	static const char* pythonSpecialVars[] = {
		"self", "NotImplemented", "Ellipsis", "__debug__", "__file__", "__name__",
		NULL
	};
	
	LoadSyntax(pythonKeywords, pythonSpecialVars);
}

void PythonSyntax::LoadSyntax(const char* keywordsArray[], const char* specialVarsArray[])
{
	keywords.Clear();
	while(*keywordsArray)
		keywords.Add(*keywordsArray++);
	
	specialVars.Clear();
	while(*specialVarsArray)
		specialVars.Add(*specialVarsArray++);
}

bool PythonSyntax::LineHasColon(const WString& line)
{
	for(int i = line.GetLength() - 1; i >= 0; i--) {
		if(line[i] == ':')
			return true;
	}
	return false;
}

int PythonSyntax::CalculateLineIndetations(const WString& line, Identation::Type type)
{
	int count = 0;
	for(int i = 0; i < line.GetLength(); i++) {
		if(type == Identation::Tab && line[i] == '\t')
			count++;
		else
		if(type == Identation::Space && line[i] == ' ')
			count++;
		else
			break;
	}
	return count;
}

PythonSyntax::Identation::Type PythonSyntax::FindIdentationType(CodeEditor& editor, const WString& line)
{
	Identation::Type type = Identation::Unknown;
	if(line.StartsWith("\t"))
		type = Identation::Tab;
	else
	if(line.StartsWith(" "))
		type = Identation::Space;
	else {
		for(int i = 0; i < editor.GetLineCount(); i++) {
			WString cLine = editor.GetWLine(i);
			if(cLine.StartsWith("\t")) {
				type = Identation::Tab;
				break;
			}
			else
			if(cLine.StartsWith(" ")) {
				type = Identation::Space;
				break;
			}
		}
	}
	return type;
}

int PythonSyntax::CalculateSpaceIndetationSize(CodeEditor& editor)
{
	int current = 0;
	for(int i = 0; i < editor.GetLineCount(); i++) {
		WString line = editor.GetWLine(i);
		for(int j = 0; j < line.GetLength(); j++) {
			if(line[j] == ' ')
				current++;
			else
				break;
		}
		
		if(current > 0)
			break;
	}
	
	// TODO: 4 is magic numer - try to find the way to get this number from ide constants
	return current > 0 ? current : 4;
}

char PythonSyntax::GetIdentationByType(Identation::Type type)
{
	if(type == Identation::Space)
		return ' ';
	return '\t';
}

}
