-- rafftnixGUI.lua
-- author: rafftnix
-- date: 26.05.2013

-- Keine Ver�nderung ohne meine Erlaubnis!
-- No modification without my permission!
-- history: 
--  		v1.0 (August 2013 - GIANTS mod contest Agricultural Service Company hand in)
--			v1.1 (December 2013) - Forrest mod
-- 			v1.2 (November 2014) - scale and shiftableButton implemented, bugfixes
--			v1.3 (January 2015) - added textFocus, absolute image paths and mouseOver grafics
--			v1.4 (March 2015) - added scroll bar
-- 			v1.5 (June 2015) - added renderOverlay
-- 			v1.7 (April 2017) - added FS17

RafftnixGUIHelper = {}
RafftnixGUIHelper.modDir = g_currentModDirectory;

function RafftnixGUIHelper:loadMap()
	if not self.gameIsOn then
		self.GUIs = {}
		--g_currentMission.rafftnixGUIHelper = self;
		self.gameIsOn = true;
	end;
end;

function RafftnixGUIHelper:deleteMap()
	if self.gameIsOn then
		for a=1, table.getn(self.GUIs) do
			if self.GUIs[a].delete ~= nil then
				self.GUIs[a]:delete();
			end;
		end;
		self.gameIsOn = false;
	end;
end;

function RafftnixGUIHelper:mouseEvent(posX, posY, isDown, isUp, button)
	for a=1, table.getn(self.GUIs) do
		if self.GUIs[a].mouseEvent ~= nil then
			self.GUIs[a]:mouseEvent(posX, posY, isDown, isUp, button);
		end;
	end;
end;

function RafftnixGUIHelper:keyEvent(unicode, sym, modifier, isDown)
	for a=1, table.getn(self.GUIs) do
		if self.GUIs[a].keyEvent ~= nil then
			self.GUIs[a]:keyEvent(unicode, sym, modifier, isDown);
		end;
	end;
end;

function RafftnixGUIHelper:update(dt)
	for a=1, table.getn(self.GUIs) do
		if self.GUIs[a].update ~= nil then
			self.GUIs[a]:update(dt);
		end;
	end;
end;

function RafftnixGUIHelper:draw()
	for a=1, table.getn(self.GUIs) do
		if self.GUIs[a].draw ~= nil then
			self.GUIs[a]:draw();
		end;
	end;
end;

addModEventListener(RafftnixGUIHelper);


RafftnixGUI = {}
RafftnixGUI.version = 1.5;

local RafftnixGUI_mt = Class(RafftnixGUI);

function RafftnixGUI:new(mt, fullScreenMode, mouseActive, hideGUI)
	local self = {}
	
	if mt == nil then
		mt = RafftnixGUI_mt;
	end;
	setmetatable(self, mt);
	
	table.insert(RafftnixGUIHelper.GUIs, self);
	self.baseElement = self:createElement(nil, 0, 0, 1, 1);
	self.baseElement:setVisibility(false);
	self.fullScreenMode = fullScreenMode;
	self.updateNeededElements = {}
	self.hideGUI = hideGUI;
	self.mouseActive = mouseActive;
	self.mousePosXBackup = 0.5;
	self.mousePosYBackup = 0.5;
	
	return self;
end;

function RafftnixGUI:open()
	if g_gui.currentGui ~= nil and g_gui.currentGui.baseElement ~= nil and g_gui.currentGui.fullScreenMode ~= nil then
		g_gui.currentGui:close();
	end;

	self.baseElement.visibility = true;
	if self.fullScreenMode then
		g_gui:showGui("RafftnixGUI");
		g_currentMission.isPlayerFrozen = true;
		g_gui.currentGui = self;
		
		-- stop vehicles if a gui is open and you can not control the vehicle
		--if g_currentMission.controlledVehicle ~= nil and g_currentMission.controlledVehicle.motor ~= nil then
		--	g_currentMission.controlledVehicle.motor:setSpeedLevel(0, true)
		--end;
	end;
	if self.mouseActive then
		InputBinding.setShowMouseCursor(true);
	end;
end;

function RafftnixGUI:close()
	self.baseElement.visibility = false;
	if self.fullScreenMode then
		InputBinding.setShowMouseCursor(false);
		g_currentMission.isPlayerFrozen = false;
		g_gui.currentGui = nil;
	end;
	if self.mouseActive then
		InputBinding.setShowMouseCursor(false);
	end;
end;

function RafftnixGUI:onOpen()
	self:open();
end;

function RafftnixGUI:onClose()
	self:close();
end;

function RafftnixGUI:delete()
	self:deleteElement(self.baseElement);
end;

function RafftnixGUI:deleteElement(element)
	if element.overlayId ~= nil then
		delete(element.overlayId);
		element.overlayId = nil;
	end;

	for a=1, table.getn(element.elements) do
		self:deleteElement(element.elements[a]);
	end;
	element = nil;
end;

function RafftnixGUI:createElement(baseElement, posX, posY, width, height)
	local element = {}
	element.elements = {}
	element.visibility = true;
	element.posX = posX;
	element.posY = posY;
	element.width = width;
	element.height = height;
	element.isDisabled = false;
	element.isSelected = false;
	element.isSelectable = true;
	element.mouseOver = false;
	element.scaleX = 1;
	element.scaleY = 1;
	
	function element:setVisibility(visibility)
		element.visibility = visibility;
	end;
	
	function element:setPosition(posX, posY)
		element.posX = posX;
		element.posY = posY;
	end;
	
	function element:setScale(scaleX, scaleY)
		element.scaleX = scaleX;
		element.scaleY = scaleY;
	end;
	
	function element:setDimension(width, height)
		element.width = width;
		element.height = height;
	end;
	
	function element:setIsSelectable(isSelectable)
		element.isSelectable = isSelectable;
	end;
	
	function element:setDisabled(state)
		element.isDisabled = state;
		if state then
			if element.textColor ~= nil then
				element.textColor[4] = 0.5;
			end;
		else
			if element.textColor ~= nil then
				element.textColor[4] = 1;
			end;
		end;
		for a=1, table.getn(element.elements) do
			element.elements[a]:setDisabled(state);
		end;
	end;
	
	function element:registerMouseOver(filename, absolutePath)
		if absolutePath == nil or not absolutePath then
			filename = RafftnixGUIHelper.modDir..filename;
		end;
		element.mouseOverOverlayId = createImageOverlay(filename);
	end;
	
	if baseElement ~= nil then
		table.insert(baseElement.elements, element);
	end;
	
	return element;
end;

function RafftnixGUI:createText(baseElement, posX, posY, width, height, text, textSize, textColor)
	local element = self:createElement(baseElement, posX, posY, width, height);
	
	element.text = text;
	element.textSize = textSize;
	element.textFocusX = "left";
	element.textFocusY = "bottom";
	
	
	if textColor == 1 then
		element.textColor = {1, 1, 1, 1};
	else
		element.textColor = textColor;
	end;
	
	function element:setText(text)
		element.text = text;
	end;
	
	function element:setTextColor(textColor)
		element.textColor = textColor;
	end;
	
	function element:setTextFocus(focusX, focusY) -- left middle right, top center bottom
		element.textFocusX = focusX;
		element.textFocusY = focusY;
	end;

	return element;
end;

function RafftnixGUI:createButton(baseElement, posX, posY, width, height, text, textSize, textColor, onClickFunction, object)
	local element = self:createText(baseElement, posX, posY, width, height, text, textSize, textColor, object);
	
	element.onClickFunction = onClickFunction;
	element.onClickCallbackTarget = object;
		
	return element;
end;

function RafftnixGUI:createTextInput(baseElement, posX, posY, width, height, defaultText, textSize, textColor, maxCharacters)
	local element = self:createText(baseElement, posX, posY, width, height, defaultText, textSize, textColor);
	
	element.maxCharacters = Utils.getNoNil(maxCharacters, 30);
	element.textInput = true;
	
	return element;
end;

function RafftnixGUI:createList(baseElement, posX, posY, width, height, heightPerEntry, maxVisibleEntrys, selectEntryCallbackFunction, selectEntryCallbackTarget, markSelectedItem)
	local element = self:createElement(baseElement, posX, posY, width, height);
	
	element.isList = true;
	element.maxVisibleEntrys = maxVisibleEntrys;
	element.selectedEntry = 1;
	element.highestVisibleEntry = 1;
	element.heightPerEntry = heightPerEntry;
	element.selectEntryCallbackFunction = selectEntryCallbackFunction;
	element.selectEntryCallbackTarget = selectEntryCallbackTarget;
	element.markSelectedItem = Utils.getNoNil(markSelectedItem, false);
	
	function element:scrollUp()
		element.highestVisibleEntry = math.max(1, element.highestVisibleEntry-1);
	end;
	
	function element:scrollDown()
		element.highestVisibleEntry = math.min(table.getn(element.elements) - element.maxVisibleEntrys, element.highestVisibleEntry+1);
	end;
	
	return element;
end;

function RafftnixGUI:createImage(baseElement, posX, posY, width, height, filename, absolutePath)
	local element = self:createElement(baseElement, posX, posY, width, height);
	
	if filename == nil then
		print("RafftnixGUI: Image filename missing");
		printCallstack();
	end;
	
	if absolutePath == nil or not absolutePath then
		filename = RafftnixGUIHelper.modDir..filename;
	end;
	
	element.overlayId = createImageOverlay(filename);
	return element;
end;

function RafftnixGUI:createImgButton(baseElement, posX, posY, width, height, filename, onClickFunction, object, absolutePath)
	local element = self:createButton(baseElement, posX, posY, width, height, "", 1, 1, onClickFunction, object);
	
	if absolutePath == nil or not absolutePath then
		filename = RafftnixGUIHelper.modDir..filename;
	end;
	
	element.overlayId = createImageOverlay(filename);
	return element;
end;

function RafftnixGUI:createShiftableImgButton(baseElement, posX, posY, width, height, filename, onClickFunction, object, absolutePath)
	local element = self:createButton(baseElement, posX, posY, width, height, "", 1, 1, onClickFunction, object);
	element.isShiftable = true;
	element.isShifting = false;
	
	if absolutePath == nil or not absolutePath then
		filename = RafftnixGUIHelper.modDir..filename;
	end;
	
	element.overlayId = createImageOverlay(filename);
	return element;
end;

function RafftnixGUI:createScrollBar(baseElement, posX, posY, width, height, needleWidth, maxXScroll, onScrollFunction, object, filename, absolutePath)
	local element = self:createElement(baseElement, posX, posY, width, height);
	element.onScrollFunction = onScrollFunction;
	element.onScrollCallbackTarget = object;
	element.needleWidth = needleWidth;
	element.maxXScroll = maxXScroll;
	element.value = 0;
	
	function element:computeShiftableImgButton(diffX, diffY)
		local posX = element.scrollBarNeedle.posX+diffX;
		posX = math.min(posX, element.maxXScroll-element.needleWidth);
		posX = math.max(posX, 0);
		element.scrollBarNeedle:setPosition(posX, element.scrollBarNeedle.posY);
		element.value = posX/(element.maxXScroll-element.needleWidth);
		element.onScrollFunction(element.onScrollCallbackTarget, element.value);
	end;
	
	function element:getValue()
		return element.value;
	end;
	
	function element:setValue(value)
		value = math.max(0, value);
		value = math.min(1, value);
		local posX = (element.maxXScroll-element.needleWidth)*value;
		element.scrollBarNeedle:setPosition(posX, element.scrollBarNeedle.posY);
	end;
	
	element.scrollBarNeedle = self:createShiftableImgButton(baseElement, 0, 0, needleWidth, height, filename, element.computeShiftableImgButton, element, absolutePath);

	return element;
end;

function RafftnixGUI:createRenderOverlay(baseElement, posX, posY, width, height, cameraId, resolutionX, resolutionY, aspectRatio)
	-- attention: resolution needs to be 2^n
	local element = self:createElement(baseElement, posX, posY, width, height);
	element.overlayId = createRenderOverlay(cameraId, aspectRatio, resolutionX, resolutionY);
		
	function element:update(dt)
		updateRenderOverlay(element.overlayId);
	end;
		
	table.insert(self.updateNeededElements, element);
	return element;
end;

function RafftnixGUI:mouseEvent(posX, posY, isDown, isUp, button)
	if self.baseElement.visibility then
		self:checkElementOnMouseEvent(self.baseElement, posX, posY, 0, 0, 1, 1, isDown, isUp, button);
	end;	
	self.mousePosXBackup = posX;
	self.mousePosYBackup = posY;
end;

function RafftnixGUI:keyEvent(unicode, sym, modifier, isDown)
	if self.baseElement.visibility then
		self:checkElementOnKeyEvent(self.baseElement, unicode, sym, modifier, isDown);
	end;
end;

function RafftnixGUI:checkElementOnKeyEvent(element, unicode, sym, modifier, isDown)
	if element.textInput ~= nil and element.textInput and element.visibility and isDown then
		if element.maxCharacters ~= nil and element.maxCharacters > string.len(element.text) then
			if (unicode > 96 and unicode < 123) or (unicode > 64 and unicode < 91) or unicode == 32 or unicode == 94 or unicode == 93 or unicode == 91 or unicode == 123 or unicode == 125 or unicode == 92 or unicode == 223 or unicode == 63 or unicode == 33 or unicode == 34 or (unicode > 48 and unicode < 60) or (unicode > 39 and unicode < 48) or unicode == 124 then 
				element:setText(element.text .. string.char(unicode));
			end;
		end;
		
		if unicode == 8 then
			local length = string.len(element.text);
			element:setText(string.sub(element.text, 1, length-1));
		end;
	end;

	if element.visibility then
		for a=1, table.getn(element.elements) do
			self:checkElementOnKeyEvent(element.elements[a], unicode, sym, modifier, isDown);
		end;
	end;
end;

function RafftnixGUI:checkElementOnMouseEvent(element, mouseX, mouseY, xParent, yParent, scaleX, scaleY, isDown, isUp, button)
	local scaleX = scaleX * element.scaleX;
	local scaleY = scaleY * element.scaleY;
	element.mouseOver = false;
	if element.visibility and not element.isDisabled then
		if mouseX > ((element.posX*scaleX)+xParent) and mouseX < (((element.posX*scaleX)+xParent) + (element.width*scaleX)) then 
			if mouseY > ((element.posY*scaleY)+yParent) and mouseY < (((element.posY*scaleY)+yParent) + (element.height*scaleY)) then
				if (element.onClickFunction ~= nil or (element.isList ~= nil and element.isList)) and isDown and button == 1 then
					if element.isShiftable then
						element.isShifting = true;
					elseif element.onClickFunction ~= nil then
						element.onClickFunction(element.onClickCallbackTarget);
					end;
					if element.isList then
						local y = ((element.posY*scaleX)+yParent) + (element.height*scaleY);
						
						local start = element.highestVisibleEntry;
						local stop = math.min(element.highestVisibleEntry + element.maxVisibleEntrys - 1, table.getn(element.elements));

						for a=start, stop do
							local entry = element.elements[a];
							if mouseY < y and mouseY > (y - (element.heightPerEntry*scaleY)) then
								element.elements[element.selectedEntry].isSelected = false;
								element.selectEntryCallbackFunction(element.selectEntryCallbackTarget, a);
								element.selectedEntry = a;
								element.elements[element.selectedEntry].isSelected = true;
								break;
							end;
							
							y = y - (element.heightPerEntry*scaleY);
						end;
					end;
				end;
				
				element.mouseOver = true;
				
				if element.isList ~= nil and element.isList and isDown then
					if button == 4 then
						element.scrollUp();
					end;
					if button == 5 then
						element.scrollDown();
					end;
				end;
			end;
		end;
		if not isDown and button == 1 and element.isShifting then
			element.isShifting = false;
		end;
	end;
	
	if element.isShifting then
		local diffX = mouseX - self.mousePosXBackup;
		local diffY = mouseY - self.mousePosYBackup;
		--element.setPosition(element.posX+diffX, element.posY+diffY);
		element.onClickFunction(element.onClickCallbackTarget, diffX, diffY, element.posX, element.posY);
	end;

	if element.visibility then
		for a=1, table.getn(element.elements) do
			self:checkElementOnMouseEvent(element.elements[a], mouseX, mouseY, element.posX*scaleX+xParent, element.posY*scaleY+yParent, scaleX, scaleY, isDown, isUp, button);
		end;
	end;
end;

function RafftnixGUI:update(dt)
	for a=1, table.getn(self.updateNeededElements) do
		local element = self.updateNeededElements[a];
		if element.visibility then
			element:update(dt);
		end;
	end;
end;

function RafftnixGUI:draw()
	setTextBold(false);
	if not self.hideGUI then
		self:renderElement(self.baseElement, 0, 0, 1, 1, false);
	end;
end;

function RafftnixGUI:renderElement(element, x, y, scaleX, scaleY, parentElementSelected)
	scaleX = scaleX * element.scaleX;
	scaleY = scaleY * element.scaleY;

	local textScale = math.min(scaleX, scaleY);

	if element.visibility then
		x = x + element.posX*scaleX;
		y = y + element.posY*scaleY;

		if element.text ~= nil then 
			if element.markSelectedItem and (element.isSelected or parentElementSelected) then
				setTextColor(0,1,0,element.textColor[4]);
			else
				setTextColor(unpack(element.textColor));
			end;
			setTextAlignment(RenderText.ALIGN_LEFT);
			
			-- find correckt text position (focus)
			local textX = 0;
			local textY = 0;
			
			if element.textFocusX == "left" then
				textX = x;
			elseif element.textFocusX == "middle" then
				textX = x + 0.5*(element.width*scaleX) - 0.5*getTextWidth(element.textSize*textScale, element.text);
			elseif element.textFocusX == "right" then
				textX = x + (element.width*scaleX) - getTextWidth(element.textSize*textScale, element.text);
			end;
			
			if element.textFocusY == "top" then
				textY = y + element.height*scaleY - getTextHeight(element.textSize*textScale, element.text);
			elseif element.textFocusY == "center" then
				textY = y + 0.5*(element.height*scaleY) - 0.5*getTextHeight(element.textSize*textScale, element.text);
			elseif element.textFocusY == "bottom" then
				textY = y;
			end;
			
			renderText(textX, textY, element.textSize*textScale, element.text);
			setTextColor(1, 1, 1, 1);
		end;
		if element.overlayId ~= nil then 
			renderOverlay(element.overlayId, x, y, element.width*scaleX, element.height*scaleY);
		end;
		
		if element.isList ~= nil then -- list
			local start = element.highestVisibleEntry;
			local stop = math.min(element.highestVisibleEntry + element.maxVisibleEntrys - 1, table.getn(element.elements));
			y = element.posY + element.height;
			
			for a=start, stop do
				if element.elements[a] ~= nil then
					y = y - element.heightPerEntry;
					
					local parentElementSelected = false;
					if element.selectedEntry == a then
						parentElementSelected = true;
					end;
					self:renderElement(element.elements[a], x, y, scaleX, scaleY, parentElementSelected);
				end;
			end;
		else
			for a=1, table.getn(element.elements) do
				self:renderElement(element.elements[a], x, y, scaleX, scaleY, parentElementSelected);
			end;
		end;
		if element.mouseOverOverlayId ~= nil and element.mouseOver then
			renderOverlay(element.mouseOverOverlayId, x, y, element.width*scaleX, element.height*scaleY);
		end;
	end;
end;