ISTrap = ISBaseObject:derive("ISTrap")

function ISTrap:new(x,y,z)
    local o = {};
    setmetatable(o, self);
    self.__index = self;
    o.x = x;
    o.y = y;
    o.z = z;
    o.animal = {};
    return o;
end

function ISTrap:getSquare()
	return getCell():getGridSquare(self.x, self.y, self.z)
end

function ISTrap:calculTrap(square)
    self:setDef();
    if self.bait then
        self:checkForAnimal(square);
    end
    -- already something inside this trap
    if not self.animal.type then
        self:checkDestroy(square);
    end
end

function ISTrap:addSound(square)
    -- add sound to attract the surrounding zombies
    if self.animal.type and square then
        if ZombRand(4) == 0 then
            addSound(self:getTrapObject(), square:getX(),square:getY(),square:getZ(), 40, 10);
        end
        if self.animal.type == "bird" then
--            getSoundManager():PlayWorldSoundWav("PZ_BidTrapped_wood_0" .. ZombRand(3)+1, square, 0, 15, 2, true);
            square:playSound("PZ_BidTrapped_wood", true);
        else
            if self.type == "Base.TrapCrate" or self.type == "Base.TrapBox" or self.type == "Base.TrapStick" then
--                getSoundManager():PlayWorldSoundWav("PZ_AnimalTrapped_wood_0" .. ZombRand(4)+1, square, 0, 15, 2, true);
                square:playSound("PZ_AnimalTrapped_wood", true);
            elseif self.type =="Base.TrapCage" then
--                getSoundManager():PlayWorldSoundWav("PZ_AnimalTrappedMetal_0" .. ZombRand(3)+1, square, 0, 15, 2, true);
                square:playSound("PZ_AnimalTrappedMetal", true);
            end
        end
    end
end

-- check if an animal stole your bait/destroy the trap (every hour)
function ISTrap:checkDestroy(square)
    if self.bait and ZombRand(self.def.trapStrength + 10) == 0 then
        self:removeBait();
    end
    -- slight chance of trap being destroying (by bigger animal for example)
--    if ZombRand(40) == 0 and ZombRand(self.def.trapStrength) == 0 then
--        self:removeTrap();
--    end
end

function ISTrap:removeBait(character)
    if self.bait then
--        print("remove bait");
        if character then -- give the player his item
            local bait = InventoryItemFactory.CreateItem(self.bait)
            bait:setAge(self.baitDay);
            bait:multiplyFoodValues(-0.05 / bait:getBaseHunger())
            if isServer() then
                character:sendObjectChange('addItem', { item = bait })
            else
                character:getInventory():AddItem(bait)
            end
        end
        self.bait = nil;
        self.baitDay = 0;
        self:toModData(self:getSquare(), true);
    end
end

function ISTrap:removeObject()
    local square = self:getSquare()
    local object = self:getTrapObject()
    if square and object then
        square:transmitRemoveItemFromSquare(object)
        self:reinitModData(square)
    end
end

function ISTrap:removeAnimal(character)
    if character then
--        print("remove animal");
        local item = InventoryItemFactory.CreateItem(self.animal.item)
        -- Randomize the hunger reduction of the animal
        local number = ZombRand(self.animal.minSize, self.animal.maxSize);
        local xp = number / 3;
        if xp < 3 then
            xp = 3;
        end
        -- give xp only if you are the one who placed this trap);
        if self.player == character:getUsername() then
            if isServer() then
                character:sendObjectChange('addXp', { perk = Perks.Trapping:index(), xp = xp })
            else
                character:getXp():AddXP(Perks.Trapping, xp);
            end
        end
        number = number / 100;
        number = number - (number * 2);
        item:setBaseHunger(number);
        item:setHungChange(item:getBaseHunger());
        if isServer() then
            character:sendObjectChange('addItem', { item = item })
        else
            character:getInventory():AddItem(item)
        end
    end
    self.animalHour = 0;
    self.animal = {};
    -- change the sprite of the item
    local trapObject = self:getTrapObject();
    if trapObject then
        trapObject:setSprite(self.openSprite);
        trapObject:transmitUpdatedSpriteToClients()
    end
    self:toModData(self:getSquare(), true)
end

-- check if an animal is caught (every hour)
function ISTrap:checkForAnimal(square)
    -- you won't find an animal if a player is near the trap, so we check the trap only if it's streamed
    if square then return; end
    -- first, get which animal we'll attract
    local animalsList = {};
    for i,v in ipairs(Animals) do
        -- check if at this hour we can get this animal
        local timesOk = self:checkTime(v);
--        local timesOk = true;
        if v.traps[self.type] and v.baits[self.bait] and ZombRand(100) < (v.traps[self.type] + v.baits[self.bait] + (self.skill * 1.5)) and timesOk and v.zone[self.zone] and ZombRand(100) < (v.zone[self.zone] + (self.skill * 1.5)) then -- this animal can be caught by this trap and we have the correct bait for it
            -- now check if the bait is still fresh
            if self:checkBaitFreshness() then
--                print("can catch " .. v.type);
                table.insert(animalsList, v);
            end
        end
    end
    -- random an animal
    if #animalsList > 0 then
        local int = ZombRand(#animalsList) + 1;
        local testAnimal = animalsList[int];
        if testAnimal then
--            print("get animal : " .. testAnimal.type .. " in zone " .. self.zone);
            TrapSystem.noise('trapped '..testAnimal.type..' '..self.x..','..self.y..','..self.z)
            self.animal = testAnimal;
            self.animalHour = 0;
            self:removeBait();
            self:toModData(square, true);
            -- change the sprite of the item
            local trapObject = self:getTrapObject();
            if trapObject then
                trapObject:setSprite(self.closedSprite);
                trapObject:transmitUpdatedSpriteToClients()
            end
        end
    end
end

function ISTrap:getTrapObject()
    return TrapSystem.findTrapObject(self:getSquare())
end

function ISTrap:addBait(bait, age, player)
    self.bait = bait
    self.baitDay = age
    self.player = player:getUsername();
    local square = self:getSquare()
    self:toModData(square, true)
end

function ISTrap:setDef()
    if self.def then return; end
    for i,v in ipairs(Traps) do
        if v.type == self.type then
            self.def = v;
            break;
        end
    end
end

function ISTrap:fromModData(square)
--    print("from mod data");
    self.square = square;
    self.x = square:getX();
    self.y = square:getY();
    self.z = square:getZ();
    self:fromModData2(square:getModData())
    self:setDef();
    -- change the sprite of the item
    local trapObject = self:getTrapObject();
    if trapObject then
        if self.animal.type then
            trapObject:setSprite(self.closedSprite);
        else
            trapObject:setSprite(self.openSprite);
        end
    end
end

function ISTrap:toModData(square, transmitData)
    if square then
        self.square = square;
        self:toModData2(square:getModData())
        if transmitData then
            square:transmitModdata();
        end
    end
    if isClient() then return end
    for i=1,#TrapSystem.modData.trapping.traps do
        local data = TrapSystem.modData.trapping.traps[i]
        if data.x == self.x and data.y == self.y and data.z == self.z then
            self:toModData2(data)
            break
        end
    end
end

function ISTrap:fromModData2(modData)
    self.type = modData["trapType"];
    if modData["trapBait"] == "" then
        self.bait = nil;
    else
        self.bait = modData["trapBait"];
    end
    self.baitDay = modData["trapBaitDay"];
    self.animal = {}
    local animalType = modData["animal"];
    for i,v in ipairs(Animals) do
        if v.type == animalType then
            self.animal = v;
        end
    end
    self.animalHour = modData["animalHour"];
    self.openSprite = modData["openSprite"];
    self.closedSprite = modData["closedSprite"];
    self.zone = modData["zone"];
    self.player = modData["player"];
    self.skill = modData["trappingSkill"];
end

function ISTrap:toModData2(modData)
    modData["trapType"] = self.type;
    modData["trapBait"] = self.bait or "";
    modData["trapBaitDay"] = self.baitDay;
    modData["animal"] = self.animal.type;
    modData["animalHour"] = self.animalHour;
    modData["openSprite"] = self.openSprite;
    modData["closedSprite"] = self.closedSprite;
    modData["zone"] = self.zone;
    modData["player"] = self.player;
    modData["trappingSkill"] = self.skill;
end

function ISTrap:createInfo(square, info, north, player)
    self.type = info.trapDef.type;
    self.def = info.trapDef;
    self.square = info.sq;
    self.closedSprite = info.trapDef.closedSprite;
    self.openSprite = info.trapDef.sprite;
    if north and info.trapDef.northSprite then
        self.closedSprite = info.trapDef.northClosedSprite;
        self.openSprite = info.trapDef.northSprite;
    end
    local zones = getZones(self.x, self.y, self.z);
    local zone = nil;
    -- get the zone without name, the one with name are custom ones (for fishing, plant scavenging..)
    if zones then
        for i=0,zones:size()-1 do
            if not zones:get(i):getName() or zones:get(i):getName() == "" then
                zone = zones:get(i);
                break;
            end
        end
    end
    if zone then
        self.zone = zone:getType();
--        print("find zone");
    else
        self.zone = "TownZone";
--        print("can't find zone");
    end
--    print("ZONE : " .. self.zone);
    self.player = player:getUsername();
    self.skill = player:getPerkLevel(Perks.Trapping);
end

function ISTrap:checkBaitFreshness()
    local baitItem = InventoryItemFactory.CreateItem(self.bait);
    baitItem:setAge(self.baitDay);
    return baitItem:isFresh();
end

function ISTrap:checkTime(animal)
    if animal.minHour == animal.maxHour then return true; end -- if min = max then it's 24/7
    local current = getGameTime():getHour()
    if animal.minHour < animal.maxHour then
        return animal.minHour <= current and animal.maxHour >= current
    else
        return animal.minHour <= current or animal.maxHour >= current
    end
end

function ISTrap:reinitModData(square)
    square:getModData()["trapType"] = nil;
    square:getModData()["trapBait"] = nil;
    square:getModData()["trapBaitDay"] = nil;
    square:getModData()["animal"] = nil;
    square:getModData()["animalHour"] = nil;
    square:getModData()["openSprite"] = nil;
    square:getModData()["closedSprite"] = nil;
    square:getModData()["zone"] = nil;
    square:getModData()["player"] = nil;
    square:getModData()["trappingSkill"] = nil;
    square:transmitModdata()
end
