--===================================================
--
--				GHM_CodeField_SyntaxHighlight
--  			GHM_CodeField_SyntaxHighlight.lua
--
--	          (description)
--
-- 	  (c)2011 The Gryphonheart Team
--			All rights reserved
--===================================================


local keywords = { "and", "break", "do", "else", "elseif", "end", "for", "function", "if", "in", "local", "not", "or", "return", "then", "while" };
local booleans = { "true", "false", "nil" };

local DEFAULT_SYNTAX_COLORS = {
	keyword = { 0.6, 0.6, 1.0 },
	comment = { 1.0, 0.6, 0.6 },
	string = { 0.6, 1.0, 0.6 },
	boolean = { 0.5, 0.9, 1.0 },
	number = { 0.8, 0.2, 0.8 },
};
local syntaxColors = {};
for i, v in pairs(DEFAULT_SYNTAX_COLORS) do
	local t = {};
	for i2, v2 in pairs(v) do
		t[i2] = v2;
	end
	syntaxColors[i] = t;
end


local time = 0;
local Start = function()
	time = GetTime();
end
local Check = function()
	if GetTime() - time > 2 then
		error("GHI. Syntax highlight caught in loop!")
	end
end
local lastText = "";

local ColorString = function(s, r, g, b)
	if not (s) or s == "" then return ""; end;
	if not (r) or not (g) or not (b) then return ""; end;
	return "|CFF" .. string.format("%.2x", r * 255) .. string.format("%.2x", g * 255) .. string.format("%.2x", b * 255) .. s .. "|r";
end

function GHM_GetSyntaxCatagories()
	local t = {};
	for i, _ in pairs(syntaxColors) do
		table.insert(t, i);
	end
	return t;
end

function GHM_GetSyntaxColor(catagory, default)
	local t = syntaxColors[catagory];
	if not (t) or default then
		t = DEFAULT_SYNTAX_COLORS[catagory];
	end
	if t then
		return t[1] or 0, t[2] or 0, t[3] or 0;
	end
	return 0, 0, 0;
end

function GHM_SetSyntaxColor(catagory, r, g, b)
	syntaxColors[catagory] = syntaxColors[catagory] or {};
	syntaxColors[catagory][1] = r;
	syntaxColors[catagory][2] = g;
	syntaxColors[catagory][3] = b;

	GHI_MiscData.SyntaxColor = syntaxColors;
	GHM_CodeField_UpdateColors()
end

function GHM_DisableSyntaxColor(catagory, disabled)
	syntaxColors[catagory] = syntaxColors[catagory] or {};
	syntaxColors[catagory].disabled = disabled;
	lastText = "";
	GHM_OptionsData.SyntaxColor = syntaxColors;
	GHM_CodeField_UpdateColors()
end

function GHM_IsSyntaxColorDisabled(catagory)
	return (syntaxColors[catagory] or {}).disabled;
end

local f = CreateFrame("Frame");
f:SetScript("OnEvent", function(...)
	if GHM_OptionsData and type(GHM_OptionsData.SyntaxColor) == "table" then
		for i, v in pairs(GHM_OptionsData.SyntaxColor) do
			syntaxColors[i] = v;
		end
	end
end)
f:RegisterEvent("VARIABLES_LOADED")


GHM_ColorSyntax = function(s, catagory)
	if GHM_IsSyntaxColorDisabled(catagory) then
		return s;
	end
	return ColorString(s, GHM_GetSyntaxColor(catagory));
end


local ValidateKeyword = function(word)
	if tContains(keywords,word) then
		return true, "keyword";
	elseif tContains(booleans, word) then
		return true, "boolean";
	end
	return false;
end

local toF1 = '[a-zA-Z_]';

local states = {
	none = {
		['-'] = {
			next = 'c1',
		},
		[toF1] = {
			next = 'f1',
		},
		['['] = {
			next = 's3',
		},
		["'"] = {
			next = 's2',
			highlight = {
				offset = 0,
				type = "start",
				category = "string",
			},
		},
		['"'] = {
			next = 's1',
			highlight = {
				offset = 0,
				type = "start",
				category = "string",
			},
		},
		['[0-9]'] = {
			next = 'n1',
			highlight = {
				offset = 0,
				type = "start",
				category = "number",
			},
		},
		['else'] = {
			next = 'none',
		},
	},

	-- Comment
	c1 = {
		['-'] = {
			next = 'c2',
			highlight = {
				offset = -1,
				type = "start",
				category = "comment",
			},
		},
		[toF1] = {
			next = 'f1',
		},
		['['] = {
			next = 's3',
		},
		["'"] = {
			next = 's2',
			highlight = {
				offset = 0,
				type = "start",
				category = "string",
			},
		},
		['"'] = {
			next = 's1',
			highlight = {
				offset = 0,
				type = "start",
				category = "string",
			},
		},
		['[0-9]'] = {
			next = 'n1',
			highlight = {
				offset = 0,
				type = "start",
				category = "number",
			},
		},
		['else'] = {
			next = 'none',
		},
	},
	c2 = {
		['['] = {
			next = 'c4',
		},
		['else'] = {
			next = 'c3',
		},
	},
	c3 = {
		['\n'] = {
			next = 'none',
			highlight = {
				offset = 0,
				type = "end",
				category = "comment",
			},
		},
		['else'] = {
			next = 'c3',
		},
	},
	c4 = {
		['['] = {
			next = 'c5',
		},
		['else'] = {
			next = 'c3',
		},
	},
	c5 = {
		[']'] = {
			next = 'c6',
		},
		['else'] = {
			next = 'c5',
		},
	},
	c6 = {
		[']'] = {
			next = 'none',
			highlight = {
				offset = 1,
				type = "end",
				category = "comment",
			},
		},
		['else'] = {
			next = 'c5',
		},
	},

	-- String
	s1 = {
		['\n'] = {
			next = 'none',
			highlight = {
				offset = 1,
				type = "end",
				category = "string",
			},
		},
		['"'] = {
			next = 'none',
			highlight = {
				offset = 1,
				type = "end",
				category = "string",
			},
		},
		['else'] = {
			next = 's1',
		},
	},
	s2 = {
		['\n'] = {
			next = 'n1',
			highlight = {
				offset = 1,
				type = "end",
				category = "string",
			},
		},
		["'"] = {
			next = 'none',
			highlight = {
				offset = 1,
				type = "end",
				category = "string",
			},
		},
		['else'] = {
			next = 's2',
		},
	},
	s3 = {
		[toF1] = {
			next = 'f1',
		},
		['['] = {
			next = 's4',
			highlight = {
				offset = -1,
				type = "start",
				category = "string",
			},
		},
		["'"] = {
			next = 's2',
			highlight = {
				offset = 0,
				type = "start",
				category = "string",
			},
		},
		['"'] = {
			next = 's1',
			highlight = {
				offset = 0,
				type = "start",
				category = "string",
			},
		},
		['-'] = {
			next = 'c1',
		},
		['[0-9]'] = {
			next = 'n1',
			highlight = {
				offset = 0,
				type = "start",
				category = "number",
			},
		},
		['else'] = {
			next = 'none',
		},
	},
	s4 = {
		[']'] = {
			next = 's5',
		},
		['else'] = {
			next = 's4',
		},
	},
	s5 = {
		[']'] = {
			next = 'none',
			highlight = {
				offset = 1,
				type = "end",
				category = "string",
			},
		},
		['else'] = {
			next = 's4',
		},
	},

	-- Number
	n1 = {
		['[0-9\.]'] = {
			next = 'n1',
		},
		['-'] = {
			next = 'c1',
			highlight = {
				offset = 0,
				type = "end",
				category = "number",
			},
		},
		['['] = {
			next = 's3',
			highlight = {
				offset = 0,
				type = "end",
				category = "number",
			},
		},
		['else'] = {
			next = 'none',
			highlight = {
				offset = 0,
				type = "end",
				category = "number",
			},
		},
	},
	f1 = {
		['-'] = {
			next = 'c1',
		},
		['[A-Za-z]'] = {
			next = 'f2',
			highlight = {
				offset = -1,
				type = "start",
				category = "keyword",
			},
		},

		["'"] = {
			next = 's2',
			highlight = {
				offset = 0,
				type = "start",
				category = "string",
			},
		},
		['"'] = {
			next = 's1',
			highlight = {
				offset = 0,
				type = "start",
				category = "string",
			},
		},
		['['] = {
			next = 's3',
		},
		['else'] = {
			next = 'none',
		},
	},
	f2 = {

		['[A-Za-z0-9]'] = {
			next = 'f2',
		},
		['-'] = {
			next = 'c1',
			highlight = {
				offset = 0,
				type = "end",
				category = "keyword",
				validate = ValidateKeyword,
			},
		},
		['else'] = {
			next = 'none',
			highlight = {
				offset = 0,
				type = "end",
				category = "keyword",
				validate = ValidateKeyword,
			},
		},
	},
};

function GHM_HighlightKeywords(text)
	local highlights = {};
	local currentHighlight;
	local state = 'none';

	local chars = {};
	for i=1,string.len(text) do
		chars[i] = string.sub(text,i,i);
	end
	--print(text);
	for i=1,#(chars) do
		local char = chars[i];
		--print(state);
		local info = (states[state][char]);

		if not(info) then
			for pattern,pInfo in pairs(states[state]) do
				if string.len(pattern) > 1 and not(pattern == "else") then
					if string.match(char,pattern) then
						info = pInfo;   --print(char,"matches",pattern)
					else
						--print(char,"no match",pattern)
					end
				end
			end
		end

		if not(info) then
			info = states[state]["else"];
		end

		if (info.highlight) then
			local hl = info.highlight;
			assert((currentHighlight and hl.type == "end" and hl.category == currentHighlight.category) or hl.type == "start","Incorrect type match",state,char)

			if hl.type == "start" then
				currentHighlight = {
					hstart = i+hl.offset,
					category = hl.category,
				}
			elseif hl.type == "end" then
				currentHighlight.hend = i+hl.offset;
				currentHighlight.validate = hl.validate;
				table.insert(highlights,currentHighlight);
				currentHighlight = nil;
			end
		end

		state = info.next;
	end

	--print(state,currentHighlight and currentHighlight.category);
	if currentHighlight then
		currentHighlight.hend = #(chars)+1;
		if state == "f2" then
			currentHighlight.validate = ValidateKeyword;
		end
		table.insert(highlights,currentHighlight);
	end

	local newText = "";
	local prevIndex = 0;
	for _,highlight in pairs(highlights) do
		newText = newText..strsubutf8(text,prevIndex,highlight.hstart-1);
		if highlight.validate then
			local text2 = strsubutf8(text,highlight.hstart,highlight.hend-1);
			local isHighlight,category = highlight.validate(text2)
			if isHighlight then
				newText = newText..GHM_ColorSyntax(text2,category);
			else
				newText = newText..text2;
			end
			prevIndex = highlight.hend;
		else

			newText = newText..GHM_ColorSyntax(strsubutf8(text,highlight.hstart,highlight.hend-1),highlight.category);
			prevIndex = highlight.hend;
		end
	end
	newText = newText..strsubutf8(text,prevIndex);
	return newText;
end


