class 'WayPoint'

NodeState = {
	PENDING     = 0,
	DISABLED	= 0,
	ENABLED	 	= 1,
	TRIGGERED   = 2,
	TRIPPED 	= 2
}

TargetVisibility = {
	INVISIBLE 	= 0,
	VISIBLE		= 1
}

function WayPoint:__init(type, info, target_name, state, value)
	assert(type and info and target_name and 'Incomplete Node constructor parameters')

	if state then
		self.isConditionsSatisfied = function(self) return HasGameState(state, value) end
	end

	self.state = self:isConditionsSatisfied() and NodeState.ENABLED or NodeState.PENDING
	self.connected_nodes = {}
	self.state_string	= value and state..'@'..value or ''
	self.target_area	= target_name
	self.target_is_visible  = TargetVisibility.VISIBLE
	self.type = type
	self.info = info
end

function WayPoint:isConditionsSatisfied()
	return true
end

function WayPoint:trigger()
	self:setState(NodeState.TRIGGERED)
end

function WayPoint:isAccessible(use_saved_visibility)
	if not self.state == NodeState.TRIGGERED or not use_saved_visibility and current_scene:getPlayField():hasEntityNamed(self.target_area) then
		return current_scene:getPlayField():getHint():IsAccessible(current_scene:getPlayField():getEntityNamed(self.target_area))
	else
		return self.target_is_visible ~= TargetVisibility.INVISIBLE
	end
end

function WayPoint:setTargetArea(area)
	self.target_area = area
end

function WayPoint:setState(state)
	Console:pushText(self:__tostring()..' set state  '..(state or 'Ignored'))
	self.state = state
end

function WayPoint:addCondition(state, value)
	-- Check if current state is already present in condition
	for _, current in ipairs(self.state_string:split(":")) do
		if current:find("@") then
			local state_value = current:split('@')
			if state == state_value[1] and value == state_value[2] then return end
		end
	end
	self.state_string = self.state_string..':'..state..'@'..value

	self.isConditionsSatisfied = function(self) 
		for _, current in ipairs(self.state_string:split(":")) do
			if current:find("@") then
				local state_value = current:split('@')
				if not HasGameState(state_value[1], state_value[2]) then
					return false
				end
			end
		end
		return true
	end
	self.target_is_visible  = TargetVisibility.NOT_DEPENDS
	self:setState(self:isConditionsSatisfied() and NodeState.ENABLED or NodeState.PENDING)
end

-- address helper function
local function getAddress(obj)
	local mt = getmetatable(obj) or {}
	local _tos = mt.__tostring
	mt.__tostring = nil
	
	local key = tostring(obj)
	
	mt.__tostring = _tos
	return key
end

function WayPoint:isRootOfDisabledTree()
	if self.state == NodeState.TRIGGERED and self.type ~= NodeType.SCENE then
		for node, _ in pairs(self.connected_nodes) do
	if not node:isRootOfDisabledTree() then
		return false
	end
		end
		return true
	end

	return false
end

function WayPoint:getGameStateString()
	local function ToString(val)
		local function KeyValToString(key, value)
	return "[ " .. ToString(key) .. " ] = " .. ToString(value)
		end
		
		local function GetString(key1, value1, ...)
	if key1 then
		return KeyValToString(key1, value1) .. ", " .. GetString(...)
	end
	 
	 return ""
		end
		
		local function UnpackTable(t)
	local function nextKey(key, value)
		if key then
			return key, value, nextKey(next(t, key))
		end
	end
	 
	return nextKey(next(t))
		end
		
		------------------
		local t = type(val)
		
		if t == "table" then
	return "{" .. GetString(UnpackTable(val)) .. "}"
		elseif t == "userdata" then
	return ToString(getAddress(val))
		elseif t == "string" then
	return "'" .. val .. "'"
		else
	 return tostring(val)
		end
	end
	
	local function FormConditionCallback(state)
		local buffer = ''

		for _, current in ipairs(self.state_string:split(":")) do
	if current:find("@") then
		local state_value = current:split('@')
		buffer = buffer.."and HasGameState('"..state_value[1].."', '"..state_value[2].."') "
	end
		end
		

		return buffer == '' and 'true' or buffer:sub(4)
	end

	local str_restore_f = [[function(GetNode)
								local type = ]] .. ToString(self.type) .. [[;
								local info = ]] .. ToString(self.info) ..[[;
								local target_name = ]] .. ToString(self.target_area) .. [[;

								local obj = WayPoint(type, info, target_name);
								obj.target_is_visible = ]] .. ToString(self.target_is_visible) .. [[;
								obj.state = ]] .. ToString(self.state) .. [[;
								obj.state_string = ]] .. ToString(self.state_string) .. [[;
								obj.connected_nodes = ]] .. ToString(self.connected_nodes) .. [[;
								obj.isConditionsSatisfied = function(self) return ]] .. FormConditionCallback() .. [[ end;

								return obj
							end]]
							
							return "'" .. getAddress(self) .. "'", str_restore_f
end

function WayPoint:__tostring()
	local sting_rep   = {'Scene',
			'Link',
			'Zoom',
			'ToolApply',
			'ToolFind',
			'Click',
			'DragNDrop',
			'Note',
			'Task',
			'AdditionalDependency',
	}
	return 'node of '..(sting_rep[self.type] or trostring(self.type))..' to '..self.info
end

function WayPoint:__eq(other)
	return getAddress(self) == getAddress(other)
end
