--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| 	Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]


local PLUGIN = PLUGIN

function PLUGIN:GetHookCallPriority(hook)
	if (hook == "HandlePlayerDeath") then
		return 1100
	end
end

function PLUGIN:PlayerLoadedCharacter(client, character)
    -- Datafile
    local bHasDatafile = character:GetHasDatafile()

    if (!bHasDatafile) then
        PLUGIN:CreateDatafile(client)
    end

    -- Teams
    if (client:IsCombine()) then
        self:LeaveTeam(client)

        net.Start("ixPTSync")
            net.WriteBool(true)
            net.WriteTable(self.teams)
        net.Send(client)
    else
        if (client:GetNetVar("ProtectionTeam")) then
            self:LeaveTeam(client)
        end

        net.Start("ixPTSync")
            net.WriteBool(false)
        net.Send(client)
    end
end

function PLUGIN:PostCalculatePlayerDamage(victim, damage, hitgroup)
    if (victim.beingAmputated and victim.beingAmputatedEnd > CurTime() and damage:GetAttacker() == victim.beingAmputated) then
        damage:ScaleDamage(hitgroup == HITGROUP_HEAD and 10 or 5)
    end
end

function PLUGIN:HandlePlayerDeath(victim, damage)
    if (victim.beingAmputated and victim.beingAmputatedEnd > CurTime() and damage:GetAttacker() == victim.beingAmputated) then
        for _, v in ipairs(player.GetAll()) do
            if (CAMI.PlayerHasAccess(v, "Helix - Ban Character")) then
                v:ChatNotify(victim.beingAmputated:Name().." a amputé "..victim:Name().." pour : "..(victim.beingAmputatedReason or "aucune raison donnée"))
            end
        end
        victim.beingAmputated:Notify("Verdict délivré !")

        victim.beingAmputated = nil
        victim.beingAmputatedEnd = nil
        victim.beingAmputatedReason = nil

        return true --Do not let medical plugin interfere and put player in bleedout
    end
end

-- Entity related functions
function PLUGIN:PlayerButtonDown( client, key )
    if (IsFirstTimePredicted() and key == KEY_LALT) then
        local entity = client:GetEyeTraceNoCursor().Entity

        if !IsValid(entity) then
            return false
        end

        if !entity.canUse then
            return false
        end

        if (client:GetShootPos():Distance(entity:GetPos()) > 100) then
            return false
        end

        if entity:GetNWInt("owner") and client:GetCharacter():GetID() != entity:GetNWInt("owner") then
            return false
        end

        if entity.hasDiskInserted then client:NotifyLocalized("Un disque est inséré dans cet ordinateur !") return false end

        if (entity:GetClass() == "ix_medical_computer" or entity:GetClass() == "ix_computer") then
            local getUsers = entity.users
            local getNotes = entity.notes

            local activeClass = "med_computer"

            if entity:GetClass() == "ix_computer" then
                activeClass = "cit_computer"
            end

            local pos = entity:GetPos()

            ix.item.Spawn(activeClass, pos + Vector( 0, 0, 2 ), function(item, entityCreated) item:SetData("users", getUsers) item:SetData("notes", getNotes) end, entity:GetAngles())

            entity:Remove()
        end
    end
end

function PLUGIN:RegisterSaveEnts()
	ix.saveEnts:RegisterEntity("ix_medical_computer", true, true, true, {
		OnSave = function(entity, data) --OnSave
            data.motion = false
            data.users = entity.users
            data.notes = entity.notes
            data.owner = entity:GetNWInt("owner")
		end,
        OnRestore = function(entity, data)
            entity.users = data.users
            entity.notes = data.notes
            entity:SetNWInt("owner", data.owner)
        end
	})

    ix.saveEnts:RegisterEntity("ix_computer", true, true, true, {
		OnSave = function(entity, data) --OnSave
            data.motion = false
            data.users = entity.users
            data.notes = entity.notes
            data.owner = entity:GetNWInt("owner")
		end,
        OnRestore = function(entity, data)
            entity.users = data.users
            entity.notes = data.notes
            entity:SetNWInt("owner", data.owner)
        end
	})

    ix.saveEnts:RegisterEntity("ix_terminal", true, true, true, {
		OnSave = function(entity, data) --OnSave
            return {pos = data.pos, angles = data.angles, motion = false}
		end,
	})

    ix.saveEnts:RegisterEntity("ix_console", true, true, true, {
		OnSave = function(entity, data) --OnSave
            return {pos = data.pos, angles = data.angles, motion = false}
		end,
	})

    ix.saveEnts:RegisterEntity("npc_combine_camera", true, true, true, {
		OnSave = function(entity, data) --OnSave
            return {pos = data.pos, angles = data.angles, motion = false}
		end,
        OnRestore = function(entity, data)
            entity:SetOwner(nil)
        end,
	})

    ix.saveEnts:RegisterEntity("ix_workshiftterminal", true, true, true, {
		OnSave = function(entity, data) --OnSave
            return {pos = data.pos, angles = data.angles, motion = false}
		end,
	})
end

function PLUGIN:CanInsertDisk(client, item)
    if !client then return false end
    if !client:Alive() then return false end

    local targetEnt = client:GetEyeTraceNoCursor().Entity
    if !targetEnt then client:Notify("Vous n'êtes pas devant un ordinateur !") return false end
    if !IsValid(targetEnt) then client:Notify("Vous n'êtes pas devant un ordinateur !") return false end
    local entClass = targetEnt:GetClass()
    if (entClass != "ix_medical_computer" and entClass != "ix_computer") then client:Notify("Vous n'êtes pas devant un ordinateur !") return false end
    if targetEnt.hasDiskInserted then client:Notify("Il y a déjà un disque dans cet ordinateur !") return false end

    return targetEnt
end

function PLUGIN:InsertDisk(client, item)
    local computerEnt = self:CanInsertDisk(client, item)
    if !computerEnt or (computerEnt and !IsValid(computerEnt)) then return false end

    local bSuccess, error = item:Transfer(nil, nil, nil, item.player)
    if (!bSuccess and isstring(error)) then
        client:Notify("Impossible de déposer le disque et de l'insérer.")
        return
    end

    local disk = bSuccess
	local rotation = Vector(0, 0, 0)
    local angle = computerEnt:GetAngles()
	angle:RotateAroundAxis(angle:Up(), rotation.x)

    disk:SetAngles(angle)
    disk:SetPos(computerEnt:GetPos() + computerEnt:GetUp() * -1.5 + computerEnt:GetForward() * -15)
    constraint.Weld( disk, computerEnt, 0, 0, 0, true, false )
    local physObj = disk:GetPhysicsObject()
    if !physObj then return end
    physObj:EnableMotion(false)

    computerEnt.hasDiskInserted = item:GetID()
    item.computer = computerEnt
end

netstream.Hook("RequestFloppyDiskData", function(client, computerEnt, password)
    if !computerEnt then return false end
    if !IsValid(computerEnt) then return false end
    if !IsEntity(computerEnt) then return false end
    local diskItem = ix.item.instances[computerEnt.hasDiskInserted]
    if !diskItem then return false end

    local diskPassword = diskItem:GetData("password", false)
    local diskData = diskItem:GetData("content", false)

    if !diskPassword then
        netstream.Start(client, "ReplyFloppyDiskData", true, false, diskData)
        return
    end

    if (diskPassword and !password) or !password then
        netstream.Start(client, "ReplyFloppyDiskData", false)
        return
    end

    if (diskPassword != password) then
        netstream.Start(client, "ReplyFloppyDiskData", false, true)
        return
    end

    if (diskPassword == password) then
        netstream.Start(client, "ReplyFloppyDiskData", true, false, diskData)
        return
    end
end)

netstream.Hook("FloppyDiskSetPassword", function(client, newPass, computerEnt)
    if !computerEnt then return false end
    if !IsValid(computerEnt) then return false end
    if !IsEntity(computerEnt) then return false end
    local diskItem = ix.item.instances[computerEnt.hasDiskInserted]
    if !diskItem then return false end

    diskItem:SetData("password", newPass)
end)

netstream.Hook("FloppyDiskSetData", function(client, newContent, computerEnt)
    if !computerEnt then return false end
    if !IsValid(computerEnt) then return false end
    if !IsEntity(computerEnt) then return false end
    local diskItem = ix.item.instances[computerEnt.hasDiskInserted]
    if !diskItem then return false end

    diskItem:SetData("content", newContent)
end)

netstream.Hook("SetFloppyDiskName", function(client, text, itemID, password)
    local character = client:GetCharacter()
    if !character then return false end

    local inventory = character:GetInventory()
    if !inventory then return false end

    local item = ix.item.instances[itemID]
    if !item then return end

    local password2 = item:GetData("password", false)
    if password2 and !password then return end

    if password2 != password then
        client:NotifyLocalized("Mauvais mot de passe.")
        return
    end

    if !inventory.GetItemByID then return false end
    if !inventory:GetItemByID(itemID) then
        local targetEnt = client:GetEyeTraceNoCursor().Entity
        if !IsValid(targetEnt) then return false end
        if targetEnt:GetClass() != "ix_item" then return false end
        if (targetEnt.ixItemID and targetEnt.ixItemID != itemID) then return false end
    end

    item:SetData("customName", Schema:FirstToUpper(text))
    client:NotifyLocalized("Définissez le nom du disque sur "..Schema:FirstToUpper(text))
end)

netstream.Hook("FindLetterRecepient", function(client, text, itemID)
    local character = client:GetCharacter()
    if !character then return end

    local inventory = character:GetInventory()
    if !inventory then return end

    if !inventory:GetItemByID(itemID) then return end
    if inventory:GetItemByID(itemID).name != "Papier" then return end

    if string.len(text) == 5 and isnumber(tonumber(text)) then
        text = Schema:ZeroNumber(tonumber(text), 5)
    end

    local id = false
    local name = false
    local cid = false
    for _, v in pairs(ix.char.loaded) do
        if !isnumber(text) and string.len(tostring(text)) != 5 then
            if (ix.util.StringMatches(v:GetName(), text)) then
                id = v:GetID()
                name = v:GetName()
                cid = v:GetCid()
                break
            end
        else
            if ix.util.StringMatches(tostring(v:GetCid()), tostring(text)) then
                id = v:GetID()
                name = v:GetName()
                cid = v:GetCid()
                break
            end
        end
    end

    if !id or !name or !cid then
        local receiverQuery = mysql:Select("ix_characters")
        receiverQuery:Select("id")
        receiverQuery:Select("name")
        receiverQuery:Select("cid")

        if !isnumber(text) and string.len(tostring(text)) != 5 then
            receiverQuery:WhereLike("name", text)
        else
            receiverQuery:WhereLike("cid", text)
        end

        receiverQuery:Callback(function(result)
            if (!result or !istable(result) or #result == 0) then
                return
            end

            local first = result[1]
            id = first.id
            name = first.name
            cid = first.cid
        end)

        receiverQuery:Execute()
    end

    timer.Simple(3, function()
        if !id or !name or !cid then client:Notify("Impossible de trouver la personne à qui vous vouliez envoyer une lettre.") return end
        cid = cid or "00000"
        netstream.Start(client, "ReplyLetterRecepient", id, name, cid, itemID)
    end)
end)

function PLUGIN:AddLogToDatafile(client, toCharID, fromCID, fromName)
    local whomData = {
        fromGenericData = false,
        fromLogsTable = false,
        toGenericData = false,
        toLogsTable = false
    }

    for _, v in pairs(ix.char.loaded) do
        if ix.util.StringMatches(tostring(v:GetCid()), tostring(fromCID)) then
            whomData.fromGenericData = v:GetGenericdata()
            whomData.fromLogsTable = v:GetDatafilelogs()
            break
        end
    end

    if ix.char.loaded[tonumber(toCharID)] then
        whomData.toGenericData = ix.char.loaded[tonumber(toCharID)]:GetGenericdata()
        whomData.toLogsTable = ix.char.loaded[tonumber(toCharID)]:GetDatafilelogs()
    end

    local addLogsTo = {["to"] = toCharID, ["from"] = fromCID}
    if whomData.fromGenericData and whomData.fromLogsTable then addLogsTo["from"] = nil end
    if whomData.toGenericData and whomData.toLogsTable then addLogsTo["to"] = nil end

    if !whomData.fromGenericData or !whomData.fromLogsTable or !whomData.toGenericData or !whomData.toLogsTable then
        for whom, id in pairs(addLogsTo) do
            local query = mysql:Select("ix_characters")
            query:Select("name")
            query:Select("faction")
            query:Select("id")
            query:Select("cid")

            if whom != "to" then
                query:Where("cid", id)
            else
                query:Where("id", id)
            end

            query:Where("schema", Schema and Schema.folder or "helix")
            query:Callback(function(result)
                if (!istable(result) or #result == 0) then
                    return
                end

                local dataSelect = mysql:Select("ix_characters_data")
                dataSelect:Where("id", result[1].id)
                dataSelect:WhereIn("key", {"genericdata", "datafilelogs"})
                dataSelect:Callback(function(dataSelectResult)
                    if (!istable(dataSelectResult) or #dataSelectResult == 0) then
                        return
                    end

                    for _, v in ipairs(dataSelectResult) do
                        if whom == "to" then
                            if v.key == "genericdata" then
                                whomData.toGenericData = util.JSONToTable(v.data or "")
                            elseif v.key == "datafilelogs" then
                                whomData.toLogsTable = util.JSONToTable(v.data or "")
                            end
                        else
                            if v.key == "genericdata" then
                                whomData.fromGenericData = util.JSONToTable(v.data or "")
                            elseif v.key == "datafilelogs" then
                                whomData.fromLogsTable = util.JSONToTable(v.data or "")
                            end
                        end
                    end
                end)
                dataSelect:Execute()

            end)
            query:Execute()
        end
    end

    timer.Simple(4, function()
        if whomData.fromGenericData and whomData.fromLogsTable and whomData.toGenericData and whomData.toLogsTable then
            local combineutilities = ix.plugin.list["combineutilities"]
            if combineutilities and combineutilities.AddLog then
                for i = 1, 2 do
                    local logsT = (i == 1 and whomData.fromLogsTable or whomData.toLogsTable)
                    local genericT = (i == 1 and whomData.fromGenericData or whomData.toGenericData)
                    local fromName1 = whomData.fromGenericData.name or ""
                    local fromCID1 = whomData.fromGenericData.cid or ""
                    local toName = whomData.toGenericData.name or ""
                    local toCID = whomData.toGenericData.cid or ""

                    if (i == 1) then -- Only need the notification once
                        ix.combineNotify:AddNotification("LOG:// Sujet '" .. fromName1 .. "' a envoyé une lettre à '" .. toName .. "'", nil, client)
                    end

                    combineutilities:AddLog(client, logsT, genericT, "TERMINAL", nil, "LETTRE ENVOYÉE / REÇUE - "..fromName1.." | "..fromCID1.." -> "..toName.." | "..toCID, true, true)
                end
            end
        end
    end)
end

netstream.Hook("SendLetterToID", function(client, charID, itemID, fromCID, fromName)
    local character = client:GetCharacter()
    if !character then return end

    local inventory = character:GetInventory()
    if !inventory then return end

    if !inventory:GetItemByID(itemID) then return end
    if inventory:GetItemByID(itemID).name != "Papier" then return end

    local paperInstance = ix.item.instances[tonumber(itemID)]
    if !paperInstance then return end

    local paperWritingID = paperInstance:GetData("writingID")
    if !paperWritingID then return end

    local paperTitle = paperInstance:GetData("title", "Papier")
    local currentOwner = paperInstance:GetData("owner", false)

    if ix.char.loaded[tonumber(charID)] then
        ix.char.loaded[tonumber(charID)]:SetPurchasedItems("letter_"..itemID, {title = paperTitle, fromCID = fromCID, fromName = fromName, writingID = paperWritingID, owner = currentOwner})
        client:Notify("La lettre a été envoyée avec succès.")
        PLUGIN:AddLogToDatafile(client, charID, fromCID, fromName)
        ix.item.instances[tonumber(itemID)]:Remove()
    else
        local dataSelect = mysql:Select("ix_characters_data")
        dataSelect:Where("id", tonumber(charID))
        dataSelect:WhereIn("key", "purchasedItems")
        dataSelect:Callback(function(dataSelectResult)
            if (!istable(dataSelectResult) or #dataSelectResult == 0) then
                return
            end

            local purchasedItems = util.JSONToTable(dataSelectResult[1].data)
            if !purchasedItems then return end

            purchasedItems["letter_"..itemID] = {title = paperTitle, fromCID = fromCID, fromName = fromName, writingID = paperWritingID, owner = currentOwner}

            local updateQuery = mysql:Update("ix_characters_data")
            updateQuery:Update("data", util.TableToJSON(purchasedItems))
            updateQuery:Where("id", tonumber(charID))
            updateQuery:Where("key", "purchasedItems")
            updateQuery:Execute()

            ix.item.instances[tonumber(itemID)]:Remove()

            PLUGIN:AddLogToDatafile(client, charID, fromCID, fromName)
        end)
        dataSelect:Execute()

        client:Notify("La lettre a été envoyée avec succès.")
    end
end)
