---
--- Date         : 2020-08-10 15:33:27
--- Author       : LICHAO7
--- LastEditors  : LICHAO7
--- LastEditTime : 2020-08-18 20:44:35
--- Description  : Base actor assemble
---

---
--- Base Assemble class
---
--- @class LBaseAssemble
---
LBaseAssemble = LBaseAssemble or class("LBaseAssemble");

function LBaseAssemble:ctor()
    self.m_strName        = "";
    self.m_strType        = "";
    self.m_strParams      = "";
    self.m_strPath        = "";
    self.m_strAsset       = "";
    self.m_strSocket      = "";
    self.m_bIsRoot        = false;
    self.m_actor          = nil;
    self.m_player         = nil;
    self.m_tbChildren     = {};
    self.m_tbLogicSockets = {};
    self.m_tbRevise       = {};
    self.m_tbSocketName   = {};
    self.m_tbProperty     = {};
end

function LBaseAssemble:dtor()
    self.m_strName        = nil;
    self.m_strType        = nil;
    self.m_strParams      = nil;
    self.m_strPath        = nil;
    self.m_strAsset       = nil;
    self.m_strSocket      = nil;
    self.m_bIsRoot        = nil;
    self.m_actor          = nil;
    self.m_player         = nil;
    self.m_tbChildren     = nil;
    self.m_tbLogicSockets = nil;
    self.m_tbSocketName   = nil;
    self.m_tbProperty     = nil;
end

---
--- Get children count.
---
--- @return number
---
function LBaseAssemble:GetSize()
    return #self.m_tbChildren;
end

---
--- Add child assemble to children.
---
--- @param assemble LBaseAssemble
--- @return number
---
function LBaseAssemble:AddChild(assemble)
    return table.insert(self.m_tbChildren, assemble);
end

---
--- Remove child assemble to children.
---
--- @param assemble LBaseAssemble
--- @return LBaseAssemble
---
function LBaseAssemble:RemoveChild(assemble)
    local key   = 1;
    local child = nil;
    while true do
        if self.m_tbChildren[key] == assemble then
            child = table.remove(self.m_tbChildren, assemble);
            break;
        end
        key = key + 1;
    end
    return child or false;
end

---
--- Get actor that created.
---
--- @return LActor
---
function LBaseAssemble:GetActor()
    return self.m_actor;
end

---
--- Create actor.
---
--- !!! Unimplemented base method.
---
function LBaseAssemble:CreateActor()
    assert(false, "Unimplemented base method.");
end

---
--- Destroy actor.
---
--- !!! Unimplemented base method.
---
function LBaseAssemble:DestroyActor()
    assert(false, "Unimplemented base method.");
end

---
--- Get player that binded.
---
--- @return LActor
---
function LBaseAssemble:GetPlayer()
    return self.m_player;
end

---
--- Set player for bind.
---
---@param player LLocalPlayer
---
function LBaseAssemble:SetPlayer(player)
    if not player then
        self.m_player:DetachFromParent();
    end
    self.m_player = player;
end

---
--- Draw Property panel
--- !!! Unimplemented base method.
---
function LBaseAssemble:DrawPropertyWidget() end

---
--- Draw Driving control panel
--- !!! Unimplemented base method.
---
function LBaseAssemble:DrawControlTipWidget() end

---
--- Find logic socket by name.
---
--- @param name string
--- @return table
---
function LBaseAssemble:FindLogicSocketByName(name)
    local findItem = nil;
    for key, socket in pairs(self.m_tbLogicSockets) do
        if key == name then
            findItem = socket;
            break;
        end
    end
    return findItem or false;
end

---
--- Construct edit property
---
function LBaseAssemble:ConstructPropertyFromLogicSockets() end

---
--- Dump edit property to logic socket
---
function LBaseAssemble:DumpPropertyToLogicSockets() end

---
--- Save to `.assemble` file.
---
--- @param path string
--- @return boolean
---
function LBaseAssemble:ToFile(path)
    local struct_assemble_file = function()
        return {
            Type         = "",
            Components   = "",
            Params       = "",
            LogicSockets = {},
        };
    end
    local struct_component = function()
        return {
            -- ChildComponents = {},
            Type   = "",
            Asset  = "",
            Socket = "",
            Root   = nil,
            Revise = {
                0, 0, 0, 0,
                0, 0, 0, 0,
                0, 0, 0, 0,
                0, 0, 0, 0,
            },
        };
    end
    self:DumpPropertyToLogicSockets();
    local asfile        = struct_assemble_file();
    asfile.Type         = self.m_strType;
    asfile.Params       = self.m_strParams;
    asfile.Components   = {};
    asfile.LogicSockets = self.m_tbLogicSockets; -- TODO:需要根据Components中的Socket做默认处理
    local i, size = 1, self:GetSize();
    local oComp, nComp;
    while i <= size do
        -- nComp.ChildComponents = oComp.m_tbChildren or {};
        nComp = struct_component();
        oComp = self.m_tbChildren[i];
        nComp.Type   = oComp.m_strType or "";
        nComp.Socket = oComp.m_strSocket or "";
        nComp.Revise = oComp.m_tbRevise or nComp.Revise;
        nComp.Asset  = string.replace(oComp.m_strAsset, FS.Path.Join(FS.GetCwd(), FS.Path.SEP), "") or "";
        if oComp.m_bIsRoot then
            nComp.Root = 1;
        end
        asfile.Components[tostring(i - 1)] = nComp;
        i = i + 1;
    end
    return JSON.Dumpf(asfile, path);
end

---
--- Init from params.
---
--- @param params table
--- @param params.Name string
--- @param params.Type string
--- @param params.Params string
--- @param params.Path string
--- @param params.Asset string
--- @param params.Socket string
--- @param params.Root boolean
--- @param params.LogicSockets table
--- @param params.Revise table
--- @return boolean
---
function LBaseAssemble:InitFromParams(params)
    self.m_strName        = params.Name or "";
    self.m_strType        = params.Type or "";
    self.m_strParams      = params.Params or "";
    self.m_strPath        = params.Path or "";
    self.m_strAsset       = params.Asset or "";
    self.m_strSocket      = params.Socket or "";
    self.m_bIsRoot        = params.Root or false;
    self.m_tbLogicSockets = params.LogicSockets or {};
    self.m_tbRevise       = params.Revise or {};
    return true;
end

---
--- Init self from file.
---
--- @param strPath string
--- @return LBaseAssemble
---
function LBaseAssemble:InitFromFile(strPath)
    local content  = JSON.Loadf(strPath, nil);
    if not content then
        return false;
    end

    self.m_strName        = util.a2u8(string.replace(FS.Path.SplitFile(strPath), FS.Path.SplitExt(strPath), ""));
    self.m_strType        = content.Type;
    self.m_strParams      = content.Params;
    self.m_strPath        = strPath;
    self.m_tbLogicSockets = content.LogicSockets or {};
    self:ConstructPropertyFromLogicSockets();

    local key   = 0;
    local value = nil;
    while true do
        value = content.Components[tostring(key)];
        if value then
            local component = LBaseAssemble:new();
            if value.Root then
                value.Socket = "Root";
            end
            value.Name = util.a2u8(string.replace(
                FS.Path.SplitFile(value.Asset or ""),
                FS.Path.SplitExt(value.Asset or ""), ""
            ));
            component:InitFromParams(value);
            self:AddChild(component);
        else break end
        key = key + 1;
    end
    content = nil;
    return true;
end
