--***********************************************************
--**                    ROBERT JOHNSON                     **
--***********************************************************

require "TimedActions/ISBaseTimedAction"

ISCraftAction = ISBaseTimedAction:derive("ISCraftAction");

function ISCraftAction:isValid()
	return RecipeManager.IsRecipeValid(self.recipe, self.character, self.item, self.containers) and not self.character:isDriving();
end

function ISCraftAction:update()
	self.item:setJobDelta(self:getJobDelta());
	--    if self.recipe:getSound() and (not self.craftSound or not self.craftSound:isPlaying()) then
	--        self.craftSound = getSoundManager():PlayWorldSoundWav(self.recipe:getSound(), self.character:getCurrentSquare(), 0, 2, 1, true);
	--    end
	self.character:setMetabolicTarget(Metabolics.UsingTools);
end

function ISCraftAction:start()
	if self.recipe:getSound() then
		self.craftSound = self.character:playSound(self.recipe:getSound());
	end
	self.item:setJobType(self.recipe:getName());
	self.item:setJobDelta(0.0);
	if self.recipe:getProp1() or self.recipe:getProp2() then
		self:setOverrideHandModels(self:getPropItemOrModel(self.recipe:getProp1()), self:getPropItemOrModel(self.recipe:getProp2()))
	end
	if self.recipe:getAnimNode() then
		self:setActionAnim(self.recipe:getAnimNode());
	else
		self:setActionAnim(CharacterActionAnims.Craft);
	end

	--	self.character:reportEvent("EventCrafting");
end

function ISCraftAction:stop()
	if self.craftSound and self.character:getEmitter():isPlaying(self.craftSound) then
		self.character:stopOrTriggerSound(self.craftSound);
	end
	self.item:setJobDelta(0.0);
	ISBaseTimedAction.stop(self);
end

function ISCraftAction:perform()
	if self.craftSound and self.character:getEmitter():isPlaying(self.craftSound) then
		self.character:stopOrTriggerSound(self.craftSound);
	end
	if self.container:getType() == "floor" then
		self.fromFloor = true;
	else
		self.fromFloor = false;
	end
	self.container:setDrawDirty(true);
	self.item:setJobDelta(0.0);
	local resultItemCreated = RecipeManager.PerformMakeItem(self.recipe, self.item, self.character, self.containers);
	if resultItemCreated and instanceof(resultItemCreated, "DrainableComboItem") and self.recipe:getResult():getDrainableCount() > 0 then
		resultItemCreated:setUsedDelta(resultItemCreated:getUseDelta() * self.recipe:getResult():getDrainableCount());
	end
	if resultItemCreated and instanceof(resultItemCreated, "Food") and instanceof(self.item, "Food") then
		-- TODO: this could be improved by checking/averaging all the items involved
		resultItemCreated:setHeat(self.item:getHeat());
		resultItemCreated:setFreezingTime(self.item:getFreezingTime());
		resultItemCreated:setFrozen(self.item:isFrozen());
	end
	if resultItemCreated and self.recipe:getResult():getCount() > 1 then
		-- FIXME: this does not call the recipe's OnCreate lua function
		local itemsAdded = self.container:AddItems(resultItemCreated:getFullType(), self.recipe:getResult():getCount());
		-- now we modify the variables of the item created, for example if you create a nailed baseball bat, it'll have the condition of the used baseball bat
		if itemsAdded and instanceof(resultItemCreated, "Food") then
			for i=0, itemsAdded:size()-1 do
				local newItem = itemsAdded:get(i);
				if resultItemCreated:isCustomName() then
					newItem:setName(resultItemCreated:getDisplayName());
					newItem:setCustomName(true);
				end
				newItem:setCooked(resultItemCreated:isCooked());
				newItem:setRotten(resultItemCreated:isRotten());
				newItem:setBurnt(resultItemCreated:isBurnt());
				newItem:setAge(resultItemCreated:getAge());
				newItem:setHungChange(resultItemCreated:getHungChange());
				newItem:setBaseHunger(resultItemCreated:getBaseHunger());
				newItem:setThirstChange(resultItemCreated:getThirstChangeUnmodified());
				newItem:setPoisonDetectionLevel(resultItemCreated:getPoisonDetectionLevel());
				newItem:setPoisonPower(resultItemCreated:getPoisonPower());
				newItem:setCarbohydrates(resultItemCreated:getCarbohydrates());
				newItem:setLipids(resultItemCreated:getLipids());
				newItem:setProteins(resultItemCreated:getProteins());
				newItem:setCalories(resultItemCreated:getCalories());
				newItem:setTaintedWater(resultItemCreated:isTaintedWater());
				newItem:setActualWeight(resultItemCreated:getActualWeight());
				newItem:setWeight(resultItemCreated:getWeight());
				newItem:setCustomWeight(resultItemCreated:isCustomWeight());
				--set the new items heat/freezing/frozen
				newItem:setHeat(resultItemCreated:getHeat());
				newItem:setFreezingTime(resultItemCreated:getFreezingTime());
				newItem:setFrozen(resultItemCreated:isFrozen());
				newItem:setBoredomChange(resultItemCreated:getBoredomChangeUnmodified());
				newItem:setUnhappyChange(resultItemCreated:getUnhappyChangeUnmodified());
			end
		end
		if itemsAdded and instanceof(resultItemCreated, "HandWeapon") then
			for i=0, itemsAdded:size()-1 do
				local newItem = itemsAdded:get(i);
				newItem:setCondition(resultItemCreated:getCondition());
			end
		end
		if itemsAdded and self.fromFloor then
			for i=1,itemsAdded:size() do
				self.character:getCurrentSquare():AddWorldInventoryItem(itemsAdded:get(i-1),
					(self.character:getX() - math.floor(self.character:getX())) + ZombRandFloat(0.1,0.5),
					(self.character:getY() - math.floor(self.character:getY())) + ZombRandFloat(0.1,0.5),
					self.character:getZ() - math.floor(self.character:getZ()))
				-- NOTE: AddWorldInventoryItem() sets the item's container to null
				itemsAdded:get(i-1):setContainer(self.container)
			end
		end
		if itemsAdded and not self.fromFloor then
			for i=1,itemsAdded:size() do
				self:addOrDropItem(itemsAdded:get(i-1))
			end
		end
	elseif resultItemCreated then
		if self.fromFloor then
			self.character:getCurrentSquare():AddWorldInventoryItem(resultItemCreated,
				self.character:getX() - math.floor(self.character:getX()) + ZombRandFloat(0.1,0.5),
				self.character:getY() - math.floor(self.character:getY()) + ZombRandFloat(0.1,0.5),
				self.character:getZ() - math.floor(self.character:getZ()))
			self.container:AddItem(resultItemCreated)
		else
			self:addOrDropItem(resultItemCreated)
		end
	end

	ISInventoryPage.dirtyUI()

	if self.onCompleteFunc then
		local args = self.onCompleteArgs
		self.onCompleteFunc(args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8])
	end

	-- needed to remove from queue / start next.
	ISBaseTimedAction.perform(self);
end

function ISCraftAction:getPropItemOrModel(propStr)
	if not propStr then return nil end
	if propStr == "" then return nil end
	if not propStr:contains("Source=") then return propStr end
	local sourceIndex = tonumber(propStr:sub(propStr:find("=") + 1))
	if not sourceIndex or (sourceIndex < 1) or (sourceIndex > self.recipe:getSource():size()) then return nil end
	local items = RecipeManager.getSourceItemsNeeded(self.recipe, sourceIndex - 1, self.character, self.containers, self.item, nil, nil)
	if items:isEmpty() then return nil end
	-- It would be best to use the item instead of the model, so any blood/etc appears as expected.
	-- But things like flashlights have animation masks which break the "Dismantle Flashlight" animation, for example.
	-- So return the model name instead of the item.  Returning the item does work, though.
	return items:get(0):getStaticModel()
end

function ISCraftAction:addOrDropItem(item)
	local inv = self.character:getInventory()
	if not inv:contains(item) then
		inv:AddItem(item)
	end
	if inv:getCapacityWeight() > inv:getEffectiveCapacity(self.character) then
		if inv:contains(item) then
			inv:Remove(item)
		end
		self.character:getCurrentSquare():AddWorldInventoryItem(item,
			self.character:getX() - math.floor(self.character:getX()),
			self.character:getY() - math.floor(self.character:getY()),
			self.character:getZ() - math.floor(self.character:getZ()))
	end
end

function ISCraftAction:setOnComplete(func, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
	self.onCompleteFunc = func
	self.onCompleteArgs = { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 }
end

function ISCraftAction:new(character, item, time, recipe, container, containers)
	local o = {}
	setmetatable(o, self)
	self.__index = self
	o.character = character;
	o.item = item;
	o.recipe = recipe;
	o.container = container;
	-- If the recipe can be done from the floor, then we can use items from nearby containers.
	-- If the recipe cannot be done from the floor, then all the items must already be in the player's inventory.
	o.containers = recipe:isCanBeDoneFromFloor() and containers or nil;
	o.stopOnWalk = recipe:isStopOnWalk();
	o.stopOnRun = recipe:isStopOnRun();
	o.maxTime = time;
	if character:isTimedActionInstant() then
		o.maxTime = 1;
	end
	o.jobType = recipe:getName();
	o.forceProgressBar = true;
	return o;
end
