-- Garry's Mod Binder - Main Client Logic
include("binder/sh_lang.lua")
include("binder/core/sh_config.lua")
include("binder/vgui/cl_frame.lua")
include("binder/vgui/cl_profiles_tab.lua")
include("binder/vgui/cl_keybind_tab.lua")
include("binder/vgui/cl_radial_tab.lua")
include("binder/vgui/cl_radial_hud.lua")
include("binder/vgui/cl_settings_tab.lua")

Binder = Binder or {}
Binder.Profiles = Binder.Profiles or {}
Binder.F6Pressed = false

surface.CreateFont("Binder_Key", {
	font = "Roboto",
	size = 12,
	weight = 700,
	extended = true,
})

CreateClientConVar("binder_show_feedback", "1", true, false)
CreateClientConVar("binder_dark_theme", "1", true, false)
CreateClientConVar("binder_radial_key", tostring(Binder.Config.DefaultRadialKey), true, false)

-- Networking
net.Receive("Binder_SyncProfiles", function()
    local length = net.ReadUInt(32)
    local compressed = net.ReadData(length)
    local data = util.Decompress(compressed)
    
    if data then
        Binder.Profiles = Binder.SanitizeProfiles(util.JSONToTable(data) or {})
        if IsValid(Binder.Frame) then
             Binder.Frame:RefreshActiveTab()
        end
    end
end)

function Binder.SaveToServer()
    local data = util.TableToJSON(Binder.Profiles)
    local compressed = util.Compress(data)
    
    net.Start("Binder_UpdateProfile")
        net.WriteUInt(#compressed, 32)
        net.WriteData(compressed, #compressed)
    net.SendToServer()
end

-- Input Hook for Binds (Client-side Think for Singleplayer support)
local PrevBindState = {}

hook.Add("Think", "Binder_CheckBindsThink", function()
    local activeProfile
    for _, p in ipairs(Binder.Profiles or {}) do
        if p.Active then activeProfile = p break end
    end
    
    if not activeProfile or not activeProfile.Binds then return end

    -- Don't trigger new presses if in console, ESC menu, typing in chat, or focused on a VGUI text entry
    local blockNewInput = gui.IsConsoleVisible() or gui.IsGameUIVisible() or (vgui.GetKeyboardFocus() ~= nil) or LocalPlayer():IsTyping()

    for key, commandStr in pairs(activeProfile.Binds) do
        local isDown = input.IsButtonDown(key)
        local wasDown = PrevBindState[key] or false
        
        if isDown and not wasDown then
            if not blockNewInput then
                PrevBindState[key] = true
                if string.sub(commandStr, 1, 1) == "/" then
                    LocalPlayer():ConCommand("say " .. commandStr)
                else
                    LocalPlayer():ConCommand(commandStr)
                end
            end
        elseif not isDown and wasDown then
            PrevBindState[key] = false
            
            -- Automatically release + commands
            local cmd = string.Explode(" ", commandStr)[1]
            if cmd and string.sub(cmd, 1, 1) == "+" then
                local minusCmd = "-" .. string.sub(cmd, 2)
                LocalPlayer():ConCommand(minusCmd)
            end
        end
    end
end)


concommand.Add("binder_menu", function()
    if Binder.Frame and IsValid(Binder.Frame) then
        Binder.Frame:Remove()
    end
    
    Binder.Frame = vgui.Create("Binder_Frame")
end)

-- Open menu on F6
hook.Add("Think", "Binder_F6Check", function()
    if input.IsKeyDown(KEY_F6) and not Binder.F6Pressed then
        Binder.F6Pressed = true
        RunConsoleCommand("binder_menu")
    elseif not input.IsKeyDown(KEY_F6) then
        Binder.F6Pressed = false
    end
end)
