local function onlocktype(self, locktype, old_locktype)
    if not self.isstuck then
        if old_locktype ~= nil then
            self.inst:RemoveTag(old_locktype.."_lock")
        end
        if locktype ~= nil then
            self.inst:AddTag(locktype.."_lock")
        end
    end
end

local function onunlockable(self)
    if self.key ~= nil and self.islocked and not self.isstuck then
        self.inst:AddTag("unlockable")
    else
        self.inst:RemoveTag("unlockable")
    end
end

local function onisstuck(self, isstuck)
    if self.locktype ~= nil then
        if isstuck then
            self.inst:RemoveTag(self.locktype.."_lock")
        else
            self.inst:AddTag(self.locktype.."_lock")
        end
    end
    onunlockable(self)
end

local Lock = Class(function(self, inst)
    self.inst = inst
    self.onlocked = nil
    self.onunlocked = nil
    self.unlocktest = nil
    self.islocked = true
    self.isstuck = false
    self.key = nil
    self.locktype = LOCKTYPE.DOOR
end,
nil,
{
    locktype = onlocktype,
    islocked = onunlockable,
    key = onunlockable,
    isstuck = onisstuck,
})

function Lock:OnRemoveFromEntity()
    if self.locktype ~= nil then
        self.inst:RemoveTag(self.locktype.."_lock")
    end
    self.inst:RemoveTag("unlockable")
end

function Lock:GetDebugString()
    return string.format("type:%s, locked:%s, isstuck:%s, key:%s", self.locktype, tostring(self.islocked), tostring(self.isstuck), tostring(self.key) )
end

function Lock:SetOnUnlockedFn(fn)
    self.onunlocked = fn
end

function Lock:SetOnLockedFn(fn)
    self.onlocked = fn
end

function Lock:CompatableKey(keytype)
    return not self:IsStuck() and keytype == self.locktype
end

function Lock:IsStuck()
    return self.isstuck
end

function Lock:IsLocked()
    return self.islocked
end

function Lock:Unlock(key, doer)
    if not self:IsStuck() and self.islocked then
        self.islocked = false
        if self.onunlocked ~= nil then
            self.onunlocked(self.inst, key, doer)
        end
        if key ~= nil then
            key.components.key:OnUsed(self.inst)
            if key.components.stackable ~= nil and key.components.stackable:IsStack() then
                key = key.components.stackable:Get()
            else
                key.components.inventoryitem:RemoveFromOwner()
            end
            self:SetKey(key)
        end
    end
end

function Lock:Lock(doer)
    if not self:IsStuck() and not self.islocked then
        self.islocked = true
        if self.onlocked then
            self.onlocked(self.inst, doer)
        end
        if self.key then
            self.key.components.key:OnRemoved(self.inst, doer)
            if doer.components.inventory then
                doer.components.inventory:GiveItem(self.key, nil, self.inst:GetPosition())
            end
        end
        self:SetKey(nil)
    end
end

function Lock:SetKey(key)
    if self.key then
        self.inst:RemoveChild(self.key)
        self.key:ReturnToScene()
    end
    if key then
        self.inst:AddChild(key)
        key:RemoveFromScene()
    end
    self.key = key
end

function Lock:TestForUnlock(key)
    if self:IsStuck() then return false end
    if self.unlocktest then --manually do unlock in testforunlockfn
        self.unlocktest(key, self.inst)
    else
        self:Unlock(key)
    end
end

function Lock:SetLocked(locked)
    if locked ~= self.islocked then
        if locked then
            if self.onlocked ~= nil then
                self.onlocked(self.inst)
            end
        elseif self.onunlocked ~= nil then
            self.onunlocked(self.inst)
        end
        self.islocked = locked
    end
end

function Lock:OnSave()
    return
        {
            locked = self.islocked,
            isstuck = self.isstuck,
            key = self.key ~= nil and self.key.GUID or nil,
        },
        self.key ~= nil and { self.key.GUID } or nil
end

function Lock:OnLoad(data)
    if data ~= nil then
        self.islocked = nil
        self:SetLocked(data.locked)
        self.isstuck = data.isstuck
    end
end

function Lock:LoadPostPass(newents, data)
    if data ~= nil and data.key ~= nil then
        local key = newents[data.key]
        if key ~= nil then
            self:SetKey(key.entity)
        end
    end
end

return Lock
