class 'CellsOrganizerBase' (Node)

CellsOrganizerBase.uniqueAnchorId = 0

-------------------------------------------------------------------------------
-- STATIC METHODS -------------------------------------------------------------
-------------------------------------------------------------------------------

function CellsOrganizerBase.staticGetUniqueAnchorId()
	local id = CellsOrganizerBase.uniqueAnchorId
	CellsOrganizerBase.uniqueAnchorId = CellsOrganizerBase.uniqueAnchorId + 1
	return id	
end

-------------------------------------------------------------------------------
-- INITS ----------------------------------------------------------------------
-------------------------------------------------------------------------------

function CellsOrganizerBase:__init(inventory, order)
	Node.__init(self, inventory:getMainEntity(), 'CellsOrganizer', order)

	self.CELL_INDEX_STATE		= 'InvCellIndex_%s'
	self.CELL_FREE_INDEX_STATE	= 'InvCellFreeIndex'

	self.slotWidth	  = 68
	self.slotsCount	  = 7
	self.startPos	  = Vector2f(308, 729)
	self.scissorBox	  = Rectanglef(276, 690, 466, 78)
	self.shift		  = 0
	self.shiftTime	  = 0.6
	self.bShifting	  = false

	self.pInventory	= inventory	
	self.cells		= {}
	self.anchors	= {}
	self.rootAnchor	= nil
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:postInit()
	self:createRootAnchor()
	self:setupScissorBox()
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:setupScissorBox()
	local entity = self:getMainEntity()
	local LTPos	 = self.scissorBox:getLeftTop()
	local RBPos	 = self.scissorBox:getRightBottom()

	entity:setScissorBox(LTPos.x, LTPos.y, RBPos.x, RBPos.y)
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:open(time)
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:close(time)
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:shiftAt(deltaShift, time)
	local shift = self.shift + deltaShift
	self:shiftTo(shift, time)
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:shiftTo(targetShift, time)
	if self.shift == targetShift then
		return
	end

	local pos = self:shiftToPos( targetShift )
	time = time or self.shiftTime

	self.shift = targetShift
	self.rootAnchor:destroyEffectors()

	if time == 0 then
		self.bShifting = false
		self.rootAnchor:setPosition( pos )
	else
		local momentumX	= 5
		local dx		= pos.x - self.rootAnchor:getX()
		local dir		= (dx ~= 0) and dx/math.abs(dx) or 0

		local effector = CreateAnimationEffector()
		effector:setDestroysOnEnd( true )
		effector:attachTo( self.rootAnchor )
		effector:setKey(_TRACK_X, 0,		self.rootAnchor:getX())
		effector:setKey(_TRACK_X, time*0.5,	pos.x)
		effector:setKey(_TRACK_X, time*0.7,	pos.x + momentumX*dir)
		effector:setKey(_TRACK_X, time,		pos.x)
		effector:play()

		self.bShifting = true
		effector.onFinish:add(function()
								self.bShifting = false
							  end)
	end

	self:checkCellsVisible(time * 0.5)
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:isShifting()
	return self.bShifting
end
-------------------------------------------------------------------------------


-------------------------------------------------------------------------------
-- CELLS ----------------------------------------------------------------------
-------------------------------------------------------------------------------

function CellsOrganizerBase:createBlankCells()
	for i=1, self.slotsCount do
		self:createCell(i)
	end
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:createCell(index)
	local cell = Cell(self)
	self:attachCell(cell, index)
	return cell
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:removeCell(index)
	local cell		= self:getCell(index)
	local effector	= self:createRemoveCellEffector(cell)

	effector.onFinish:add(	function()
								cell:destroy()
							end )

	self:detachCell(index)
	self:slideCellsHome(index)

	if self:getCellsCount() < self.slotsCount then
		self:createCell( self:getFreeIndex() )
	end

	local minShift = self.slotsCount - self:getCellsCount()

	if self.shift < minShift then
		self:shiftAt(1)
	else
		self:checkCellsVisible(self.shiftTime * 0.5)
	end
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:attachCell(cell, index)
	assert(not self.cells[index])

	local cellEntity = cell:getMainEntity()
	local anchor	 = self:getParentAnchor(index)
	local pos		 = self:indexToPos(index)

	pos.x = pos.x + self.slotWidth * #self.anchors

	cellEntity:attachTo( anchor )
	cellEntity:setPosition( pos )
	self.cells[index] = cell
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:detachCell(index)
	local cell		= self:getCell(index)
	local toolInfo	= cell:getToolInfo()

	table.remove(self.cells, index)
	
	local state = string.format(self.CELL_INDEX_STATE, toolInfo:getName())

	if HasGameState(state) then
		RemoveGameState(state)
	end
	
	for i = index, self:getCellsCount() do
		local theCell	  = self:getCell(i)
		local theToolInfo = theCell:getToolInfo()

		if theToolInfo then
			state = string.format(self.CELL_INDEX_STATE, theToolInfo:getName())
			SetGameState(state, tostring(i))
		end
	end
	
	if HasGameState(self.CELL_FREE_INDEX_STATE) then
		local maxIndex = tonumber(GetGameState(self.CELL_FREE_INDEX_STATE):get(0))
		SetGameState(self.CELL_FREE_INDEX_STATE, tostring(maxIndex - 1))
	end
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:getCellsCount()
	return #self.cells   
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:getCell(index)
	return self.cells[index] 
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:findCell(predicate)
	for index, cell in pairs(self.cells) do
		if predicate(cell) then
			return cell, index
		end
	end

	return nil
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:checkCellsVisible(time)
	time = time or 0
	
	for index, cell in pairs(self.cells) do
		local bVisible = self:isVisibleSlot(index)

		cell:setVisible(bVisible, time)
	end
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:createRemoveCellEffector(cell)
	local cellEntity = cell:getMainEntity()
	cellEntity:attachInPlace(self.main_entity)

	local time		= self.shiftTime
	local effector	= CreateAnimationEffector()

	effector:setDestroysOnEnd( true )
	effector:attachTo( cellEntity )
	effector:setKey(_TRACK_Y, 0,		cellEntity:getY())
	effector:setKey(_TRACK_Y, time / 2,	cellEntity:getY() + 100)
	effector:setKey(_TRACK_O, 0,		cellEntity:getOpacity())
	effector:setKey(_TRACK_O, time,		0)
	effector:play()

	return effector
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:slideCellsHome(fromIndex)
	local anchor = self:createAnchor(fromIndex)

	self:moveAnchorIndexsLeft( fromIndex )
	self:addAnchor			 ( anchor )
	self:attachTailToAnchor	 ( anchor )

	local effector = anchor:Move(-self.slotWidth, 0, self.shiftTime)

	effector.onFinish:add(	function()
								self:removeAnchor( anchor )
							end )
end
-------------------------------------------------------------------------------


-------------------------------------------------------------------------------
-- ANCHORS --------------------------------------------------------------------
-------------------------------------------------------------------------------

function CellsOrganizerBase:createRootAnchor()
	local pos = self:shiftToPos( self.shift )

	self.rootAnchor = CreateRenderedObject()
	self.rootAnchor:setName		( 'InventoryMainSlider' )
	self.rootAnchor:attachTo	( self:getMainEntity() )
	self.rootAnchor:setPosition	( pos )
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:createAnchor(cellIndex)
	local uniqueId	 = CellsOrganizerBase.staticGetUniqueAnchorId()
	local anchorName = string.format('cellAnchor_%d', uniqueId)
	local parent	 = self:getParentAnchor(cellIndex)
	local anchor	 = CreateRenderedObject()

	anchor:setName	( anchorName )
	anchor:attachTo	( parent )

	anchor.properties		= {}
	anchor.properties.id	= uniqueId
	anchor.properties.index	= cellIndex
	anchor.properties.bCellAnchor = true

	return anchor
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:removeAnchor(anchor)
	local predicate = function(theAnchor)
		return theAnchor.properties.id == anchor.properties.id
	end

	local _, index = self:findAnchor(predicate)
	table.remove(self.anchors, index)

	local childs = anchor:getChilds()
	local parent = anchor:getParent()

	for i = 0, childs:size() - 1 do
		local child = childs:get(i)
		child:attachInPlace(parent)

		if child.properties and child.properties.bCellAnchor then
			local absX		= math.abs(child:getX())
			local doneShift	= absX / self.slotWidth
			local needShift	= math.max(2, math.ceil(doneShift))

			local targetX	= -self.slotWidth * needShift
			local time		= (needShift - doneShift) * self.shiftTime

			child:destroyEffectors()
			local eff = child:MoveTo(targetX, child:getY(), time)
			eff.onFinish:add(	function()
									self:removeAnchor( child )
								end )
		else
			--DOTO: correct cell position, if needed
		end
	end

	anchor:destroy()
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:addAnchor(anchor)
	local predicate = function(theAnchor)
		return theAnchor.properties.index >= anchor.properties.index
	end

	local _, index = self:findAnchor(predicate)

	index = index or (#self.anchors + 1)
	table.insert(self.anchors, index, anchor)
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:findAnchor(predicate, bReverseSearch)
	local startIndex = bReverseSearch and #self.anchors or 1
	local endIndex	 = bReverseSearch and  1			or #self.anchors
	local step		 = bReverseSearch and -1			or 1

	for i=startIndex, endIndex, step do
		local anchor = self.anchors[i]
		assert(anchor)

		if predicate(anchor) then
			return anchor, i
		end
	end

	return nil
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:anchorFilter(predicate)
	local anchors = {}

	for i=1, #self.anchors do
		local anchor = self.anchors[i]

		if predicate(anchor) then
			table.insert(anchors, anchor)
		end
	end

	return anchors
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:getParentAnchor(cellIndex)
	local predicate = function(theAnchor)
		return theAnchor and theAnchor.properties.index <= cellIndex
	end

	return self:findAnchor(predicate, true) or self.rootAnchor
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:moveAnchorIndexsLeft(fromIndex)
	for _, anchor in ipairs(self.anchors) do
		local index = anchor.properties.index

		if index >= fromIndex then
			anchor.properties.index = index - 1
		end
	end
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:attachTailToAnchor(anchor)
	local index = anchor.properties.index

	for i=index, self:getCellsCount() do
		local predicate = function(theAnchor)
			return	theAnchor.properties.id ~= anchor.properties.id and
					theAnchor.properties.index == i
		end

		local bAnchorAttached = false
		local anchors = self:anchorFilter(predicate)

		for i=1, #anchors do
			local theAnchor = anchors[i]

			if theAnchor.id ~= anchor.properties.id then
				theAnchor:attachInPlace(anchor)
				bAnchorAttached = true
				break
			end
		end

		if bAnchorAttached then
			break
		else
			self:getCell(i):getMainEntity():attachInPlace( anchor )
		end
	end
end
-------------------------------------------------------------------------------


-------------------------------------------------------------------------------
-- HELPERS --------------------------------------------------------------------
-------------------------------------------------------------------------------

function CellsOrganizerBase:getNewCellIndex(toolInfo)
	local index
	
	if toolInfo then
		local state = string.format(self.CELL_INDEX_STATE, toolInfo:getName())

		if HasGameState(state) then
			return tonumber(GetGameState(state):get(0))
		else
			local collection = toolInfo:getCollection()

			if collection then
				for _, toolName in pairs(collection:get_tools()) do
					state = string.format(self.CELL_INDEX_STATE, toolName)

					if HasGameState(state) then
						return tonumber(GetGameState(state):get(0))
					end
				end
			end
		
			if HasGameState(self.CELL_FREE_INDEX_STATE) then
				index = tonumber(GetGameState(self.CELL_FREE_INDEX_STATE):get(0))
			else
				index = 1
			end
	
			SetGameState(self.CELL_FREE_INDEX_STATE, tostring(index + 1))
			SetGameState(string.format(self.CELL_INDEX_STATE, toolInfo:getName()), tostring(index))
		end
	else
		index = self:getFreeIndex()
	end

	return index
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:getFreeIndex()
	local freeIndex = 1

	for index, cell in pairs(self.cells) do
		if index >= freeIndex then
			freeIndex = index + 1
		end
	end

	return freeIndex
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:indexToPos(index)
	return Vector2f(self.slotWidth * (index - 1), 0)
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:shiftToPos(shift)
	return self.startPos + Vector2f(self.slotWidth * shift, 0)
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:getPosByShiftAndCellIndex(shift, cellIndex)
	local shiftPos = self:shiftToPos(shift)
	local indexPos = self:indexToPos(cellIndex)

	return shiftPos + indexPos
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:getShift()
	return self.shift
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:getSlotsCount()
	return self.slotsCount
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:getHintTextParent()
	return self.pInventory:getMainEntity()
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:canShiftForward()
	local count		= self:getCellsCount()
	local lastCell	= self:getCell(count)

	if lastCell:isWaitingTool() then
		count = count - 1
	end

	return count > self.slotsCount and self.shift > (self.slotsCount - count)
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:canShiftBack()
	local count		= self:getCellsCount()
	local lastCell	= self:getCell(count)

	if lastCell:isWaitingTool() then
		count = count - 1
	end

	return count > self.slotsCount and self.shift < 0
end
-------------------------------------------------------------------------------

function CellsOrganizerBase:isVisibleSlot(index)
	local slotShift = self.shift + index
	return slotShift >= 1 and slotShift <= self.slotsCount
end
-------------------------------------------------------------------------------