--***********************************************************
--**                    THE INDIE STONE                    **
--***********************************************************

if isClient() then return end

require "ISBaseObject"

SGlobalObjectSystem = ISBaseObject:derive("SGlobalObjectSystem")

function SGlobalObjectSystem:noise(message)
	if self.wantNoise then print(self.systemName..': '..message) end
end

function SGlobalObjectSystem:new(name)
	-- Create the GlobalObjectSystem called NAME and load gos_NAME.bin if it exists.
	local system = SGlobalObjects.registerSystem(name)
	-- NOTE: The table for this Lua object is the same one the SGlobalObjectSystem
	-- Java object created.  The Java class calls some of this Lua object's methods.
	-- At this point, system:getModData() has already been read from disk if the
	-- gos_name.bin file existed.
	local o = system:getModData()
	setmetatable(o, self)
	self.__index = self
	o.system = system
	o.systemName = name
	o.wantNoise = getDebug()
	o:initSystem()
	o:initLuaObjects()
	o:noise('#objects='..system:getObjectCount())
	return o
end

function SGlobalObjectSystem:initSystem()
end

function SGlobalObjectSystem:getInitialStateForClient()
	-- Return a Lua table that is used to initialize the client-side system.
	-- This is called when a client connects in multiplayer, and after
	-- server-side systems are created in singleplayer.
	return nil
end

function SGlobalObjectSystem:getLuaObjectCount()
	return self.system:getObjectCount()
end

function SGlobalObjectSystem:getLuaObjectByIndex(index)
	return self.system:getObjectByIndex(index-1):getModData()
end

function SGlobalObjectSystem:initLuaObjects()
	for i=1,self.system:getObjectCount() do
		local globalObject = self.system:getObjectByIndex(i-1)
		local luaObject = self:newLuaObject(globalObject)
		self:noise('added luaObject '..luaObject.x..','..luaObject.y..','..luaObject.z)
	end
end

function SGlobalObjectSystem:isValidIsoObject(isoObject)
	error "override this method"
end

function SGlobalObjectSystem:getIsoObjectOnSquare(square)
	if not square then return nil end
	for i=1,square:getObjects():size() do
		local isoObject = square:getObjects():get(i-1)
		if self:isValidIsoObject(isoObject) then
			return isoObject
		end
	end
	return nil
end

function SGlobalObjectSystem:getIsoObjectAt(x, y, z)
	local square = getCell():getGridSquare(x, y, z)
	return self:getIsoObjectOnSquare(square)
end

function SGlobalObjectSystem:newLuaObject(globalObject)
	-- Return an object derived from SGlobalObject
	error "override this method"
end

function SGlobalObjectSystem:newLuaObjectAt(x, y, z)
	local globalObject = self.system:newObject(x, y, z)
	local luaObject = self:newLuaObject(globalObject)
	self:newLuaObjectOnClient(luaObject)
	return luaObject
end

function SGlobalObjectSystem:newLuaObjectOnSquare(square)
	return self:newLuaObjectAt(square:getX(), square:getY(), square:getZ())
end

function SGlobalObjectSystem:removeLuaObject(luaObject)
	if not luaObject or (luaObject.luaSystem ~= self) then return end
	self:noise('removing luaObject '..luaObject.x..','..luaObject.y..','..luaObject.z)
	luaObject:aboutToRemoveFromSystem()
	self:removeLuaObjectOnClient(luaObject)
	self.system:removeObject(luaObject.globalObject)
	self:noise('#objects='..self.system:getObjectCount())
end

function SGlobalObjectSystem:removeLuaObjectAt(x, y, z)
	local luaObject = self:getLuaObjectAt(x, y, z)
	self:removeLuaObject(luaObject)
end

function SGlobalObjectSystem:removeLuaObjectOnSquare(square)
	local luaObject = self:getLuaObjectOnSquare(square)
	self:removeLuaObject(luaObject)
end

function SGlobalObjectSystem:newLuaObjectOnClient(luaObject)
	self.system:addGlobalObjectOnClient(luaObject.globalObject)
end

function SGlobalObjectSystem:removeLuaObjectOnClient(luaObject)
	self.system:removeGlobalObjectOnClient(luaObject.globalObject)
end

function SGlobalObjectSystem:getLuaObjectAt(x, y, z)
	local globalObject = self.system:getObjectAt(x, y, z)
	return globalObject and globalObject:getModData() or nil
end

function SGlobalObjectSystem:getLuaObjectOnSquare(square)
	if not square then return nil end
	return self:getLuaObjectAt(square:getX(), square:getY(), square:getZ())
end

function SGlobalObjectSystem:loadIsoObject(isoObject)
	if not isoObject or not isoObject:getSquare() then return end
	if not self:isValidIsoObject(isoObject) then return end
	local square = isoObject:getSquare()
	local luaObject = self:getLuaObjectOnSquare(square)
	if luaObject then
		self:noise('found isoObject with a luaObject '..luaObject.x..','..luaObject.y..','..luaObject.z)
		luaObject:stateToIsoObject(isoObject)
	else
		self:noise('found isoObject without a luaObject '..square:getX()..','..square:getY()..','..square:getZ())
		local globalObject = self.system:newObject(square:getX(), square:getY(), square:getZ())
		local luaObject = self:newLuaObject(globalObject)
		luaObject:stateFromIsoObject(isoObject)
		self:noise('#objects='..self.system:getObjectCount())
		self:newLuaObjectOnClient(luaObject)
	end
end

function SGlobalObjectSystem:sendCommand(command, args)
	self.system:sendCommand(command, args)
end

function SGlobalObjectSystem:OnClientCommand(command, playerObj, args)
	-- CGlobalObjectSystem:sendCommand() arguments are routed to this method
	-- in both singleplayer *and* multiplayer.
end

function SGlobalObjectSystem:OnDestroyIsoThumpable(isoObject, playerObj)
	self:OnObjectAboutToBeRemoved(isoObject)
end

function SGlobalObjectSystem:OnObjectAdded(isoObject)
	if not self:isValidIsoObject(isoObject) then return end
	self:loadIsoObject(isoObject)
end

function SGlobalObjectSystem:OnObjectAboutToBeRemoved(isoObject)
	if not self:isValidIsoObject(isoObject) then return end
	local luaObject = self:getLuaObjectOnSquare(isoObject:getSquare())
	if not luaObject then return end
	self:removeLuaObject(luaObject)
end

function SGlobalObjectSystem:OnIsoObjectChangedItself(isoObject)
	-- A Java object changed it's state. Sync the global object.
	-- For example, after a generator runs out of fuel and shuts itself off.
	if not isoObject or not isoObject:getSquare() then return end
	self:noise('OnIsoObjectChangedItself')
	if not self:isValidIsoObject(isoObject) then return end
	local square = isoObject:getSquare()
	local luaObject = self:getLuaObjectOnSquare(square)
	if luaObject then
		luaObject:OnIsoObjectChangedItself(isoObject)
--		self.system:updateGlobalObjectOnClient(luaObject.globalObject)
	end
end

-- Java calls this method when a chunk with GlobalObjects managed by this system is loaded.
-- This is how GlobalObjects with a missing IsoObject are removed.
-- Instead of using the LoadGridSquare event and checking every location,
-- this event is triggered only for chunks that have GlobalObjects belonging
-- to this particular system.
function SGlobalObjectSystem:OnChunkLoaded(wx, wy)
	local globalObjects = self.system:getObjectsInChunk(wx, wy)
	for i=1,globalObjects:size() do
		local globalObject = globalObjects:get(i-1)
		local square = getCell():getGridSquare(globalObject:getX(), globalObject:getY(), globalObject:getZ())
		local isoObject = self:getIsoObjectOnSquare(square)
		if not isoObject then
			self:noise('found luaObject without an isoObject')
			self:removeLuaObject(globalObject:getModData())
		end
	end
	-- This returns the ArrayList to a pool for reuse.  There's no harm if
	-- you forget to call it.
	self.system:finishedWithList(globalObjects)
end



local function OnDestroyIsoThumpable(luaClass, isoObject, playerObj)
	luaClass.instance:OnDestroyIsoThumpable(isoObject, playerObj)
end

local function OnObjectAdded(luaClass, isoObject)
	luaClass.instance:OnObjectAdded(isoObject)
end

local function OnObjectAboutToBeRemoved(luaClass, isoObject)
	luaClass.instance:OnObjectAboutToBeRemoved(isoObject)
end

--[[
local function OnGameBoot(luaClass)
	if not isServerSoftReset() then return end
	luaClass.instance = luaClass:new()
end
--]]

local function OnSGlobalObjectSystemInit(luaClass)
	luaClass.instance = luaClass:new()
end

local function OnClientCommand(module, command, player, args)
	if module ~= 'SFarmingSystem' then return end
	if Commands[command] then
		local argStr = ''
		for k,v in pairs(args) do argStr = argStr..' '..k..'='..v end
		noise('OnClientCommand '..module..' '..command..argStr)
		SFarmingSystem.instance:receiveCommand(player, command, args)
	end
end

Events.OnClientCommand.Add(OnClientCommand)

function SGlobalObjectSystem.RegisterSystemClass(luaClass)
	if luaClass == SGlobalObjectSystem then error "replace : with . before RegisterSystemClass" end

	-- This is to support reloading a derived class file in the Lua debugger.
	for i=1,SGlobalObjects.getSystemCount() do
		local system = SGlobalObjects.getSystemByIndex(i-1)
		if system:getModData().Type == luaClass.Type then
			luaClass.instance = system:getModData()
			return
		end
	end
	
	Events.OnDestroyIsoThumpable.Add(function(isoObject, playerObj) OnDestroyIsoThumpable(luaClass, isoObject, playerObj) end)
	Events.OnObjectAdded.Add(function(isoObject) OnObjectAdded(luaClass, isoObject) end)
	Events.OnObjectAboutToBeRemoved.Add(function(isoObject) OnObjectAboutToBeRemoved(luaClass, isoObject) end)
--	Events.OnGameBoot.Add(function() OnGameBoot(luaClass) end)
	Events.OnSGlobalObjectSystemInit.Add(function() OnSGlobalObjectSystemInit(luaClass) end)
end

