
--===================================================
--										Class  (to be moved later)
--===================================================	
function Class(_className,...)
	local obj,className;
	local parent = _parent;
	obj = CreateFrame("Frame"); -- making it a frame might create more memory use?
	
	local interfaces = {...}
	className = _className;
		
	obj.GetType = function(self)
		return className;
	end
	
	obj.IsClass = function(self,str)
		assert(type(str)=="string","Useage: IsClass(str)");
		return self:GetType() == str;
	end;
	
	obj.IsSameClass = function(self,other)
		assert(type(other)=="table","IsSameClass. Other class is not a table");
		if other.GetType then
			return self:GetType() == other:GetType()
		end
	end
	
	obj.InheritsInterface = function(self,_interface)
		for _,int in pairs(interfaces) do
			if (int == _interface) then
				return true;
			end
		end	
		return false;
	end
	
	obj.Dispose = function(self) -- Standard dispose func. This can be overwritten
		wipe(self);
	end
	
	return obj;
end

--===================================================
--										Helping functions
--===================================================	
function GHU_TestBG(obj)
	if obj.GetGui then obj = obj:GetGui() end
	obj:SetBackdrop( { 
		bgFile = "Interface\\DialogFrame\\UI-DialogBox-Gold-Background", 
		edgeFile = "edgeFile", tile = false, tileSize = 0, edgeSize = 32, 
		insets = { left = 0, right = 0, top = 0, bottom = 0 }
	});
	local color = {r=0.4,g=0.5,b=0.6}
	if GHI_EffectColors then
		color = GHI_EffectColors[GHI_ColorList[random(1,6)]];
	end
	obj:SetBackdropColor(color.r, color.g, color.b,1);
end


function strsubutf8(str,a,b)  -- modified from http://wowprogramming.com/snippets/UTF-8_aware_stringsub_7
	assert(type(str)=="string" and type(a)=="number","incorrect input strsubutf8");
	assert(not(b) or (type(b)=="number" and b <= strlenutf8(str)),"end pos larger than string lenght",b,strlenutf8(str));
	
	b = (b or strlenutf8(str));
	
	
	local start,_end = #str+1,#str+1;
  local currentIndex = 1
  local numChars = 0;
  if a <= 1 then
  	start = a;
  end
  if b <= 1 then
  	_end = b;
  end
  
  while currentIndex <= #str do
    local char = string.byte(str, currentIndex)
    if char > 240 then
      currentIndex = currentIndex + 4
    elseif char > 225 then
      currentIndex = currentIndex + 3
    elseif char > 192 then
      currentIndex = currentIndex + 2
    else
      currentIndex = currentIndex + 1
    end
    
    numChars = numChars +1;
    
    if numChars == a-1 then
    	start = currentIndex;
    end
    if numChars == b then
    	_end = currentIndex-1;
    end
  end
  return str:sub(start, _end)
end



-- class list
local Page,Area,Line,Text;

--===================================================
--										Document
--===================================================	

function GHU_Document(_parent,_width,_height,_xOff,_yOff)	
	local class = Class("GHU_Document");
	local parent,width,height = _parent,_width,_height;
	local xOff,yOff = abs(_xOff),-abs(_yOff);
	local pages = {};
	local layout,gui,input,backgroundTex;
	local focusPage,needUpdate;
	
	-- Create a std page layout
	layout = GHU_PageLayout();
	layout:AddArea("linked",width-40,height-40,10,10)
	
	
	local GetPageIndex = function(page)
		for i,p in pairs(pages) do
			if p == page then 
				return i;
			end
		end
		return;	
	end
	
	-- functions
	class.Click = function(self,btn,x,y) 
		-- remove the frames offset from the coordinates
		-- get the position for the given Click
		focusPage = pages[1];
		local t  = {focusPage:GetPos(x,y,true)};
				
		self:SetFocus(1,unpack(t));
			
	end
	class.BackwardsDelete = function(self)
		if focusPage.BackwardsDelete() then
		
		end
		needUpdate = true;
	end
	class.SetFocus = function(self,pageI,...)
		local x,y,font = pages[pageI]:SetFocus(...);
		focusPage = pages[pageI];
		class.Update();
	end
	class.ClearFocus = function(self)
		if focusPage then
			focusPage:SetFocus();
			focusPage = nil;		
		end
		input:Hide();
	end
	class.GetGui = function(self)
		return gui;	
	end
	class.Show = function(self)
		gui:Show();
		pages[1]:Show();
	end
	class.GetPage = function(self,i)
		return pages[i];	
	end
	
	-- Move Focus
	class.MoveLeft = function(self)
		if focusPage then
			if focusPage:MoveLeft() then
				-- moving failed	
				local i = GetPageIndex(focusPage);
				if i > 1 then
					focusPage = pages[i-1];
					self:MoveEnd();		
				end
			end
			needUpdate = true;
		end
	end
	class.MoveRight = function(self)
		if focusPage then
			if focusPage:MoveRight() then
				-- moving failed	
				local i = GetPageIndex(focusPage);
				if i < #(pages) then
					focusPage = pages[i+1];
					self:MoveHome();		
				end
			end
			needUpdate = true;
		end
	end
	class.MoveUp = function(self)
		if focusPage then
			if focusPage:MoveUp() then
				-- moving failed
				local i = GetPageIndex(focusPage);		
			end
			needUpdate = true;
		end
	end
	class.MoveDown = function(self)
		if focusPage then
			focusPage:MoveDown();
			needUpdate = true;
		end
		
	end
	class.MoveEnd = function(self)
		if focusPage then
			focusPage:MoveEnd();
			needUpdate = true;
		end
	end
	class.MoveHome = function(self)
		if focusPage then
			focusPage:MoveHome();
			needUpdate = true;
		end
	end	
	class.InsertText = function(self,text)
		if focusPage then
			focusPage:InsertText(text);
			for i=1,strlenutf8(text) do
				self:MoveRight();
			end
			needUpdate = true;
		end
	end
	
	local updateFocusPosition = function()
		-- update position of focus
		if focusPage then
			local x,y,font = focusPage:GetFocusCoordinate();
			input:SetFont(font);
			input:SetOffset(x,y);		
			input:Show();
		end
	end
	class.Update = function(self)
		needUpdate = false;
		for _,page in pairs(pages) do
			if page:NeedsUpdate() then
				page:Update();		
			end
		end
		updateFocusPosition();
	end
	
	
	gui = CreateFrame("Button",parent or UIParent); 
	gui.model = class;
	gui:SetWidth(width);
	gui:SetHeight(height);
	gui:ClearAllPoints();
	gui:SetPoint("TOPLEFT",parent,"TOPLEFT",xOff,yOff);
	gui:Hide();
	gui:RegisterForClicks("LeftButtonDown");
	gui:SetScript("OnClick",function(self,btn,down)
		 local x, y = GetCursorPosition();
		 local scale = self:GetEffectiveScale();
		 x,y = x/scale,(y/scale);
		 
		 x = x - xOff;
		 y = -(y - self:GetBottom())+self:GetHeight();
		 self.model:Click(btn,x,y);
	end);
	gui:SetScript("OnUpdate",function(self)
		if needUpdate then
			self.model:Update();
		end
	end);
	
	-- setup input marker
	input = GHU_KeyInput(class,function(key,char) --print(key);
		if key == "LEFT" then class:MoveLeft(); end
		if key == "RIGHT" then class:MoveRight(); end
		if key == "UP" then class:MoveUp(); end
		if key == "DOWN" then class:MoveDown(); end
		if key == "HOME" then class:MoveHome(); end
		if key == "END" then class:MoveEnd(); end
		
		if key == "BACKWARDS_DELETE" then class:BackwardsDelete(); end
		
		
		
		if key == "ESCAPE" then class:ClearFocus(); end
		if key == "CHAR" then class:InsertText(char); end
	end,false);
	
	
	-- background
	backgroundTex = GHU_DocTexture(class:GetGui(),width,height,0,0,5);
	backgroundTex:SetTextures(GHU_GetCollection("background","Green"));
	
	
	table.insert(pages,GHU_Page(class,layout,GHU_Font(),width-20,height-20,10,10));
	
	
	pages[1]:Update();
	
	
	return class;
end

