include("shared.lua")

ENT.BoardRT = nil
ENT.BoardMat = nil
ENT.Elements = {}
ENT.ImageCache = {}

function ENT:Initialize()
    -- Создаем уникальный холст (RenderTarget) 1024x512 для рисования маркером
    self.BoardRT = GetRenderTarget("SchoolBoardRT_" .. self:EntIndex(), 1024, 512)
    self.BoardMat = CreateMaterial("SchoolBoardMat_" .. self:EntIndex(), "UnlitGeneric", {
        ["$basetexture"] = self.BoardRT:GetName(),
        ["$translucent"] = 1
    })

    self.Elements = {}
    self.ImageCache = {}
    self.PositionOffset = Vector(3713.2, 4595.6, -200) -- Инициализируем смещение позиции
    self.RotationOffset = Angle(0, -180, 90) -- Инициализируем смещение поворота

    -- Заливаем доску цветом белый
    render.PushRenderTarget(self.BoardRT)
    render.Clear(255, 255, 255, 255) 
    render.PopRenderTarget()

    -- Запрашиваем актуальный макет элементов (картинки, текст)
    net.Start("SchoolBoard_RequestLayout")
    net.WriteEntity(self)
    net.SendToServer()
end

local function GetBoardImage(ent, url)
    if not url or url == "" then return nil end
    if ent.ImageCache[url] then return ent.ImageCache[url] end
    
    ent.ImageCache[url] = Material("error") -- плейсхолдер пока грузится
    
    local fetchUrl = url
    if string.match(fetchUrl, "^https?://imgur%.com/([^/]+)$") then
        local id = string.match(fetchUrl, "^https?://imgur%.com/([^/]+)$")
        fetchUrl = "https://i.imgur.com/" .. id .. ".png"
    end

    http.Fetch(fetchUrl, function(body, length, headers, code)
        if code == 200 then
            local contentType = headers["Content-Type"] or headers["content-type"] or ""
            if not string.find(string.lower(contentType), "image") then
                chat.AddText(Color(255, 50, 50), "[Доска] Ссылка не является картинкой: ", url)
                return
            end

            local filename = "schoolboard_img_" .. util.CRC(url) .. ".png"
            file.Write(filename, body)
            ent.ImageCache[url] = Material("data/" .. filename, "noclamp smooth")
        end
    end)
    return ent.ImageCache[url]
end

net.Receive("SchoolBoard_SyncLayout", function()
    local ent = net.ReadEntity()
    local size = net.ReadUInt(32)
    local compressed = net.ReadData(size)
    
    if IsValid(ent) and ent:GetClass() == "school_board" then
        local json = util.Decompress(compressed) or "[]"
        ent.Elements = util.JSONToTable(json) or {}
    end
end)

net.Receive("SchoolBoard_SyncPosition", function()
    local ent = net.ReadEntity()
    local offsetX = net.ReadFloat()
    local offsetY = net.ReadFloat()
    local offsetZ = net.ReadFloat()
    
    if IsValid(ent) and ent:GetClass() == "school_board" then
        ent.PositionOffset = Vector(offsetX, offsetY, offsetZ)
    end
end)

net.Receive("SchoolBoard_SyncRotation", function()
    local ent = net.ReadEntity()
    local pitch = net.ReadFloat()
    local yaw = net.ReadFloat()
    local roll = net.ReadFloat()
    
    if IsValid(ent) and ent:GetClass() == "school_board" then
        ent.RotationOffset = Angle(pitch, yaw, roll)
    end
end)

function ENT:GetBoardMetrics()
    -- Фиксированный масштаб, который гарантированно влезает на доску
    local scale = 0.088
    
    local width = 93
    local height = 47
    
    local realW = 1024 * scale
    local realH = 512 * scale
    
    -- Центрируем холст 1024x512 относительно ширины 93 и высоты 47
    local offsetY = (width - realW) / 2
    local offsetZ = (height - realH) / 2
    
    -- Исходные крайние точки из модели: Y = -46.5, Z = 23.5
    local startY = -46.5 + offsetY
    local startZ = 23.5 - offsetZ
    local frontX = 1.25 -- Чуть дальше 1.2, чтобы избежать z-fighting
    
    -- Смещение позиции применяем в локальных координатах пропа
    local posOffset = self.PositionOffset or Vector(0, 0, 0)
    local rotOffset = self.RotationOffset or Angle(0, 0, 270)
    
    -- Базовая позиция + смещение в локальных координатах пропа
    local localPos = Vector(frontX + posOffset.x * 0.01, startY + posOffset.y * 0.01, startZ + posOffset.z * 0.01)
    local pos = self:LocalToWorld(localPos)
    local ang = self:LocalToWorldAngles(rotOffset)
    
    return pos, ang, scale, startY, startZ, frontX
end

function ENT:Draw()
    self:DrawModel()

    local pos, ang, scale, startY, startZ, frontX = self:GetBoardMetrics()
    
    self.BoardStartY = startY
    self.BoardStartZ = startZ
    self.BoardScale = scale
    self.BoardFrontX = frontX

    cam.Start3D2D(pos, ang, scale)
        -- Отрисовка слоя с маркерами (RenderTarget)
        surface.SetMaterial(self.BoardMat)
        surface.SetDrawColor(255, 255, 255, 255)
        surface.DrawTexturedRect(0, 0, 1024, 512)

        -- Отрисовка картинок и текста (native, четко)
        for _, el in ipairs(self.Elements or {}) do
            if el.type == "image" then
                local mat = GetBoardImage(self, el.url)
                if mat and mat:GetName() ~= "error" then
                    surface.SetMaterial(mat)
                    surface.SetDrawColor(255, 255, 255, 255)
                    surface.DrawTexturedRect(el.x, el.y, el.w, el.h)
                end
            elseif el.type == "text" then
                draw.DrawText(el.text, "DermaLarge", el.x, el.y, el.color or color_white, TEXT_ALIGN_LEFT)
            end
        end
    cam.End3D2D()
end

-- ==========================================
-- MENU / EDITOR
-- ==========================================
net.Receive("SchoolBoard_OpenMenu", function()
    local ent = net.ReadEntity()
    if not IsValid(ent) then return end

    local local_elements = table.Copy(ent.Elements or {})
    local selected_idx = nil

    local frame = vgui.Create("DFrame")
    -- Делаем окно адаптивным под разрешение игрока
    local fw, fh = ScrW() * 0.9, ScrH() * 0.9
    frame:SetSize(fw, fh)
    frame:Center()
    frame:SetTitle("Редактор Интерактивной Доски")
    frame:MakePopup()

    -- ЛЕВАЯ ПАНЕЛЬ: Инструменты
    local leftPanel = vgui.Create("DPanel", frame)
    leftPanel:Dock(LEFT)
    leftPanel:SetWide(math.max(200, fw * 0.15))
    leftPanel:DockMargin(5, 5, 5, 5)

    -- ПРАВАЯ ПАНЕЛЬ: Свойства выбранного элемента
    local rightPanel = vgui.Create("DPanel", frame)
    rightPanel:Dock(RIGHT)
    rightPanel:SetWide(math.max(250, fw * 0.2))
    rightPanel:DockMargin(5, 5, 5, 5)

    -- ЦЕНТР: Холст
    local centerPanel = vgui.Create("DPanel", frame)
    centerPanel:Dock(FILL)
    centerPanel:DockMargin(5, 5, 5, 5)
    centerPanel.Paint = function(self, w, h)
        surface.SetDrawColor(50, 50, 50, 255)
        surface.DrawRect(0, 0, w, h)
    end

    local titleHint = vgui.Create("DLabel", centerPanel)
    titleHint:Dock(TOP)
    titleHint:DockMargin(10, 10, 10, 0)
    titleHint:SetFont("DermaDefaultBold")
    titleHint:SetText("Холст 1024x512. Перетаскивайте элементы мышью.")
    titleHint:SetTextColor(color_white)
    titleHint:SetContentAlignment(5)

    local canvasCanvas = vgui.Create("DPanel", centerPanel)
    canvasCanvas:Dock(FILL)
    canvasCanvas:DockMargin(20, 10, 20, 20)
    canvasCanvas.Paint = function() end -- transparent wrapper

    -- Сам холст
    local canvasPanel = vgui.Create("DPanel", canvasCanvas)
    canvasPanel:SetMouseInputEnabled(true)
    
    -- Вычисляем размеры холста, чтобы сохранить пропорции 2:1
    canvasCanvas.PerformLayout = function(self, w, h)
        local ratio = 1024 / 512
        local cw, ch = w, w / ratio
        if ch > h then
            ch = h
            cw = h * ratio
        end
        canvasPanel:SetSize(cw, ch)
        canvasPanel:SetPos((w - cw) / 2, (h - ch) / 2)
    end

    -- Отрисовка холста (с маппингом из 1024x512 в текущее разрешение cw x ch)
    canvasPanel.Paint = function(self, w, h)
        local scaleX = w / 1024
        local scaleY = h / 512

        surface.SetDrawColor(255, 255, 255, 255)
        surface.DrawRect(0, 0, w, h)

        for i, el in ipairs(local_elements) do
            local drawX = el.x * scaleX
            local drawY = el.y * scaleY
            local drawW = (el.w or 100) * scaleX
            local drawH = (el.h or 30) * scaleY

            if el.type == "image" then
                local mat = GetBoardImage(ent, el.url)
                if mat and mat:GetName() ~= "error" then
                    surface.SetMaterial(mat)
                    surface.SetDrawColor(255, 255, 255, 255)
                    surface.DrawTexturedRect(drawX, drawY, drawW, drawH)
                else
                    surface.SetDrawColor(200, 200, 200, 255)
                    surface.DrawRect(drawX, drawY, drawW, drawH)
                    draw.SimpleText("Image", "DermaDefault", drawX + drawW/2, drawY + drawH/2, color_black, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
                end
            elseif el.type == "text" then
                local m = Matrix()
                m:Translate(Vector(drawX, drawY, 0))
                m:Scale(Vector(scaleX, scaleY, 1))
                
                cam.PushModelMatrix(m)
                draw.DrawText(el.text, "DermaLarge", 0, 0, el.color or color_white, TEXT_ALIGN_LEFT)
                cam.PopModelMatrix()
            end
            
            if i == selected_idx then
                surface.SetDrawColor(255, 0, 0, 255)
                surface.DrawOutlinedRect(drawX, drawY, drawW, drawH, 2)
            end
        end
    end

    local dragging = false
    local dragOffset = {x=0,y=0}
    
    canvasPanel.OnMousePressed = function(self, key)
        if key == MOUSE_LEFT then
            local mx, my = self:CursorPos()
            local w, h = self:GetSize()
            local scaleX = 1024 / w
            local scaleY = 512 / h
            
            local realX = mx * scaleX
            local realY = my * scaleY

            selected_idx = nil
            for i = #local_elements, 1, -1 do
                local el = local_elements[i]
                local ew = el.w or 100
                local eh = el.h or 30
                if el.type == "text" then
                    surface.SetFont("DermaLarge")
                    ew, eh = surface.GetTextSize(el.text)
                    el.w = ew
                    el.h = eh
                end
                
                if realX >= el.x and realX <= el.x + ew and realY >= el.y and realY <= el.y + eh then
                    selected_idx = i
                    dragging = true
                    dragOffset.x = realX - el.x
                    dragOffset.y = realY - el.y
                    break
                end
            end
            UpdatePropertiesPanel()
        end
    end
    canvasPanel.OnMouseReleased = function(self, key)
        if key == MOUSE_LEFT then dragging = false end
    end
    canvasPanel.Think = function(self)
        if dragging and selected_idx and local_elements[selected_idx] then
            local mx, my = self:CursorPos()
            local w, h = self:GetSize()
            local scaleX = 1024 / w
            local scaleY = 512 / h
            
            local realX = mx * scaleX
            local realY = my * scaleY

            local_elements[selected_idx].x = realX - dragOffset.x
            local_elements[selected_idx].y = realY - dragOffset.y
        end
    end

    local toolsPanel = vgui.Create("DScrollPanel", leftPanel)
    toolsPanel:Dock(FILL)

    local lblTools = toolsPanel:Add("DLabel")
    lblTools:Dock(TOP)
    lblTools:DockMargin(5,5,5,10)
    lblTools:SetText("Инструменты")
    lblTools:SetFont("DermaDefaultBold")
    lblTools:SetDark(true)

    local btnAddImg = toolsPanel:Add("DButton")
    btnAddImg:Dock(TOP)
    btnAddImg:DockMargin(5,0,5,5)
    btnAddImg:SetText("Добавить Картинку")
    btnAddImg.DoClick = function()
        Derma_StringRequest("URL Картинки", "Введите прямую ссылку (.png/.jpg):", "", function(text)
            table.insert(local_elements, {type="image", url=text, x=512-150, y=256-150, w=300, h=300})
            selected_idx = #local_elements
            UpdatePropertiesPanel()
        end)
    end

    local btnAddText = toolsPanel:Add("DButton")
    btnAddText:Dock(TOP)
    btnAddText:DockMargin(5,0,5,15)
    btnAddText:SetText("Добавить Текст")
    btnAddText.DoClick = function()
        table.insert(local_elements, {type="text", text="Новый текст", x=512-50, y=256-15, color=Color(255,255,255)})
        selected_idx = #local_elements
        UpdatePropertiesPanel()
    end

    local lblControl = toolsPanel:Add("DLabel")
    lblControl:Dock(TOP)
    lblControl:DockMargin(5,10,5,5)
    lblControl:SetText("Управление доской")
    lblControl:SetFont("DermaDefaultBold")
    lblControl:SetDark(true)

    local btnSave = toolsPanel:Add("DButton")
    btnSave:Dock(TOP)
    btnSave:DockMargin(5,0,5,5)
    btnSave:SetText("Сохранить на Доску")
    btnSave:SetTall(35)
    btnSave.DoClick = function()
        -- Сохраняем элементы (текст и картинки)
        local json = util.TableToJSON(local_elements)
        local compressed = util.Compress(json)
        net.Start("SchoolBoard_SaveLayout")
        net.WriteEntity(ent)
        net.WriteUInt(string.len(compressed), 32)
        net.WriteData(compressed, string.len(compressed))
        net.SendToServer()
        
        chat.AddText(Color(0,255,0), "[Доска] Успешно сохранено!")
        frame:Close()
    end
    
    local btnClearDraw = toolsPanel:Add("DButton")
    btnClearDraw:Dock(TOP)
    btnClearDraw:DockMargin(5,5,5,15)
    btnClearDraw:SetText("Стереть 3D-маркер")
    btnClearDraw.DoClick = function()
        net.Start("SchoolBoard_Clear_Req")
        net.WriteEntity(ent)
        net.SendToServer()
    end
    
    local chalkLabel = toolsPanel:Add("DLabel")
    chalkLabel:Dock(TOP)
    chalkLabel:DockMargin(5,10,5,5)
    chalkLabel:SetText("Цвет 3D-маркера:")
    chalkLabel:SetDark(true)
    
    local chalkMixer = toolsPanel:Add("DColorMixer")
    chalkMixer:Dock(TOP)
    chalkMixer:SetTall(100)
    chalkMixer:SetPalette(true)
    chalkMixer:SetAlphaBar(false)
    chalkMixer:SetWangs(true)
    chalkMixer:SetColor(LocalPlayer().BoardChalkColor or Color(255,255,255))
    chalkMixer.ValueChanged = function(s, c)
        LocalPlayer().BoardChalkColor = c
    end

    local propsScroll = vgui.Create("DScrollPanel", rightPanel)
    propsScroll:Dock(FILL)

    function UpdatePropertiesPanel()
        propsScroll:Clear()
        
        local lblProps = propsScroll:Add("DLabel")
        lblProps:Dock(TOP)
        lblProps:DockMargin(5,5,5,10)
        lblProps:SetText("Свойства элемента")
        lblProps:SetFont("DermaDefaultBold")
        lblProps:SetDark(true)

        if selected_idx and local_elements[selected_idx] then
            local el = local_elements[selected_idx]
            
            local btnDel = propsScroll:Add("DButton")
            btnDel:Dock(TOP)
            btnDel:DockMargin(5,0,5,10)
            btnDel:SetText("Удалить элемент")
            btnDel:SetTextColor(Color(200, 50, 50))
            btnDel.DoClick = function()
                table.remove(local_elements, selected_idx)
                selected_idx = nil
                UpdatePropertiesPanel()
            end

            if el.type == "image" then
                local wSlider = propsScroll:Add("DNumSlider")
                wSlider:Dock(TOP)
                wSlider:DockMargin(5,0,5,0)
                wSlider:SetText("Ширина")
                wSlider:SetMinMax(10, 1024)
                wSlider:SetDecimals(0)
                wSlider:SetValue(el.w)
                wSlider.OnValueChanged = function(s, val) el.w = val end

                local hSlider = propsScroll:Add("DNumSlider")
                hSlider:Dock(TOP)
                hSlider:DockMargin(5,0,5,10)
                hSlider:SetText("Высота")
                hSlider:SetMinMax(10, 1024)
                hSlider:SetDecimals(0)
                hSlider:SetValue(el.h)
                hSlider.OnValueChanged = function(s, val) el.h = val end
                
            elseif el.type == "text" then
                local tEntry = propsScroll:Add("DTextEntry")
                tEntry:Dock(TOP)
                tEntry:DockMargin(5,0,5,10)
                tEntry:SetValue(el.text)
                tEntry.OnChange = function(s) el.text = s:GetValue() end
                
                local lblC = propsScroll:Add("DLabel")
                lblC:Dock(TOP)
                lblC:DockMargin(5,5,5,0)
                lblC:SetText("Цвет текста:")
                lblC:SetDark(true)

                local colMixer = propsScroll:Add("DColorMixer")
                colMixer:Dock(TOP)
                colMixer:SetTall(120)
                colMixer:SetAlphaBar(false)
                colMixer:DockMargin(5,0,5,10)
                colMixer:SetColor(el.color or color_white)
                colMixer.ValueChanged = function(s, c) el.color = c end
            end
            
            local btnUp = propsScroll:Add("DButton")
            btnUp:Dock(TOP)
            btnUp:DockMargin(5,5,5,5)
            btnUp:SetText("На передний план")
            btnUp.DoClick = function()
                local temp = table.remove(local_elements, selected_idx)
                table.insert(local_elements, temp)
                selected_idx = #local_elements
                UpdatePropertiesPanel()
            end
            
            local btnDown = propsScroll:Add("DButton")
            btnDown:Dock(TOP)
            btnDown:DockMargin(5,0,5,5)
            btnDown:SetText("На задний план")
            btnDown.DoClick = function()
                local temp = table.remove(local_elements, selected_idx)
                table.insert(local_elements, 1, temp)
                selected_idx = 1
                UpdatePropertiesPanel()
            end
        else
            local hint = propsScroll:Add("DLabel")
            hint:Dock(TOP)
            hint:DockMargin(5,0,5,0)
            hint:SetText("Выберите элемент на холсте,\nчтобы изменить его свойства.")
            hint:SetDark(true)
            hint:SizeToContents()
        end
    end
    UpdatePropertiesPanel()
end)

-- ==========================================
-- CHALK DRAWING LOGIC (MARKER)
-- ==========================================
net.Receive("SchoolBoard_Clear_Do", function()
    local ent = net.ReadEntity()
    
    if IsValid(ent) and ent.BoardRT then
        render.PushRenderTarget(ent.BoardRT)
        render.Clear(180, 140, 100, 255)
        render.PopRenderTarget()
    end
end)

net.Receive("SchoolBoard_DrawLine", function()
    local ent = net.ReadEntity()
    local x1 = net.ReadFloat()
    local y1 = net.ReadFloat()
    local x2 = net.ReadFloat()
    local y2 = net.ReadFloat()
    local r = net.ReadUInt(8)
    local g = net.ReadUInt(8)
    local b = net.ReadUInt(8)

    if IsValid(ent) and ent.BoardRT then
        render.PushRenderTarget(ent.BoardRT)
        cam.Start2D()
            surface.SetDrawColor(r, g, b, 255)
            surface.DrawLine(x1, y1, x2, y2)
            surface.DrawLine(x1 + 1, y1, x2 + 1, y2)
            surface.DrawLine(x1, y1 + 1, x2, y2 + 1)
        cam.End2D()
        render.PopRenderTarget()
    end
end)

local lastDrawX, lastDrawY = nil, nil

hook.Add("Think", "SchoolBoard_DrawLogic", function()
    local ply = LocalPlayer()
    
    if not input.IsMouseDown(MOUSE_LEFT) then 
        lastDrawX = nil
        lastDrawY = nil
        return 
    end

    local tr = ply:GetEyeTrace()
    local ent = tr.Entity

    if IsValid(ent) and ent:GetClass() == "school_board" and ent.BoardRT then
        if ply:GetPos():DistToSqr(ent:GetPos()) > 40000 then return end
        if not ent.BoardScale then return end

        local obbPos = ent:WorldToLocal(tr.HitPos)
        
        -- Проверка: не даем рисовать с обратной стороны доски
        if obbPos.x < ent.BoardFrontX - 2 then return end

        local drawX = (obbPos.y - ent.BoardStartY) / ent.BoardScale
        local drawY = (ent.BoardStartZ - obbPos.z) / ent.BoardScale
        
        if drawX < 0 or drawX > 1024 or drawY < 0 or drawY > 512 then
            lastDrawX = nil
            lastDrawY = nil
            return
        end
        
        local chalkColor = ply.BoardChalkColor or Color(255, 255, 255)

        if lastDrawX and lastDrawY then
            render.PushRenderTarget(ent.BoardRT)
            cam.Start2D()
                surface.SetDrawColor(chalkColor.r, chalkColor.g, chalkColor.b, 255)
                surface.DrawLine(lastDrawX, lastDrawY, drawX, drawY)
                surface.DrawLine(lastDrawX + 1, lastDrawY, drawX + 1, drawY)
                surface.DrawLine(lastDrawX, lastDrawY + 1, drawX, drawY + 1)
            cam.End2D()
            render.PopRenderTarget()

            net.Start("SchoolBoard_DrawLine", true)
            net.WriteEntity(ent)
            net.WriteFloat(lastDrawX)
            net.WriteFloat(lastDrawY)
            net.WriteFloat(drawX)
            net.WriteFloat(drawY)
            net.WriteUInt(chalkColor.r, 8)
            net.WriteUInt(chalkColor.g, 8)
            net.WriteUInt(chalkColor.b, 8)
            net.SendToServer()
        end

        lastDrawX = drawX
        lastDrawY = drawY
    else
        lastDrawX = nil
        lastDrawY = nil
    end
end)
