local class = import("class", "local")
local html = import("html.core", "local")
local events = import("events", "local")
local gui = import("gui", "local")
local network = import("network", "local")
local lfs = import("lfs", "local")
local enums = import("enums", "local")

local windows = {}
local window = class("html_window")
local gc = {}

local function unshow(el)
    if el:has_class("show") then
        el:set_class("show", false)
    end
    for i = 1, #el.children do
        unshow(el.children[i])
    end
end

function window:__init(data, x, y, w, h)
    windows[#windows + 1] = self
    self.__x = x or 0
    self.__y = y or 0
    self.__w = w
    self.__h = h
    self.__appends = {}
    local attr = lfs.attributes(data)
    if attr and attr.mode == "file" then
        self.file = data -- data is a file path
        self.reload = function()
            self.__dirty = true
            if self.html then
                gc[#gc+1] = self.html
            end
            self:load(io.open(self.file):read("*a"))
        end
        local watcher = require("watcher")
        watcher.watch(self.file, self.reload)
        self.reload()
    else
        self.__dirty = true
        self.data = data
        self:load(data)
    end
end

function window:close()
    for i = 1, #windows do
        if windows[i] == self then
            table.remove(windows, i)
            break
        end
    end
end

function window:load(data)
    local str = data
    for i = 1, #self.__appends do
        str = str .. self.__appends[i].data
    end
    self.html = html.load(str)
    self:clean()
    unshow(self.root)
end

function window:append(key, data)
    self.__appends[#self.__appends + 1] = { key = key, data = data }
    if self.file then
        self.reload()
    else
        self:load(self.data)
    end
end

function window:remove(key)
    for i = 1, #self.__appends do
        if self.__appends[i].key == key then
            table.remove(self.__appends, i)
            break
        end
    end
    if self.file then
        self.reload()
    else
        self:load(self.data)
    end
end

function window.__properties.root:get()
    if self.html then
        return self.html:root()
    end
end

function window.__properties.x:get()
    return self.__x
end

function window.__properties.x:set(value)
    self.__x = value
    if not self.__w then
        self.__dirty = true
    end
end

function window.__properties.y:get()
    return self.__y
end

function window.__properties.y:set(value)
    self.__y = value
    if not self.__h then
        self.__dirty = true
    end
end

function window.__properties.w:get()
    return self.__w or (gui.width - self.x)
end

function window.__properties.w:set(value)
    self.__w = value
    self.__dirty = true
end

function window.__properties.h:get()
    return self.__h or (gui.height - self.y)
end

function window.__properties.h:set(value)
    self.__h = value
    self.__dirty = true
end

function window:clean()
    if self.__dirty then
        self.html:render(self.w, self.h)
        self.__dirty = false
    end
end

events.wnd_proc += function(mouse, msg, wparam, lparam)
    for i = 1, #windows do
        local w = windows[i]
        if w.html then
            w:clean()
            if not w.html:wnd_proc(mouse, msg, w.x, w.y) and msg == enums.WM.LButtonDown then
                unshow(w.root)
            end
        end
    end
end

events.window_size_changed += function(w, h)
    for i = 1, #windows do
        local w = windows[i]
        if w.html then
            w:clean()
            w.html:render(w.w, w.h)
        end
    end
end

events.render += function(layer)
    gc = {}
    for i = 1, #windows do
        local w = windows[i]
        if w.html and (not w.layer or w.layer == layer) then
            w:clean()
            w.html:draw(w.x, w.y, w.w, w.h)
        end
    end
end

events.html += function(evt, ...)
    if evt == "import_css" then
        local url = select(1, ...)
        local base = select(2, ...)
        if url:find("://") then
            return network.get(url)
        else
            local root = select(3, ...)
            for i = 1, #windows do
                if not windows[i].root or windows[i].root == root then
                    local win = windows[i]
                    if not win.reload then
                        win.reload = function()
                            win.__dirty = true
                            if win.html then
                                gc[#gc+1] = win.html
                            end
                            if win.file then
                                win.html = html.load(io.open(win.file):read("*a"))
                            elseif win.data then
                                win.html = html.load(win.data)
                            end
                            win:clean()
                            unshow(win.root)
                        end
                    end
                    require("watcher").watch(base .. url, win.reload)
                end
            end
            return io.open(base .. url):read("*a")
        end
    elseif evt == "anchor" then
        local el = select(1, ...)
        unshow(el:root())
        if el:get_attr("role") == "button" then
            if el:get_attr("data-bs-toggle") == "dropdown" then
                local children = el.parent.children
                for i = 1, #children do
                    local child = children[i]
                    if child:has_class("dropdown-menu") then
                        child:set_class("show", not child:has_class("show"))
                    end
                end
            end
        end
    elseif evt == "click" then
        local el = select(1, ...)
        unshow(el:root())
        if el:has_class("nav-item") then
            if el:has_class("dropdown") then
                local children = el.children
                for i = 1, #children do
                    local child = children[i]
                    if child:has_class("dropdown-menu") then
                        child:set_class("show", not child:has_class("show"))
                    end
                end
            end
        elseif el:has_class("form-check-label") then
            local target = el.parent:select("#" .. el:get_attr("for"))
            if target then
                if target:has_class("form-check-input") then
                    if target:get_attr("checked") then
                        target:set_attr("checked", nil)
                        events.html("check", target, nil)
                    else
                        target:set_attr("checked", "")
                        events.html("check", target, "checked")
                    end
                end
            end
        elseif el:has_class("form-check-input") then
            if el:get_attr("checked") then
                el:set_attr("checked", nil)
                events.html("check", el, nil)
            else
                el:set_attr("checked", "")
                events.html("check", el, "checked")
            end
        elseif el:has_class("form-check") then
            local children = el.children
            for i = 1, #children do
                local child = children[i]
                if child:has_class("form-check-input") then
                    if child:get_attr("checked") then
                        child:set_attr("checked", nil)
                        events.html("check", child, nil)
                    else
                        child:set_attr("checked", "")
                        events.html("check", child, "checked")
                    end
                end
            end
        end
    end
end

return window