using log4net;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Web.UI.WebControls;
using System.Xml.Linq;
using VIZ.Package.Domain;
using VIZ.Package.Storage;

namespace VIZ.Package.Service
{
    /// <summary>
    /// VIZ命令ControlObject服务
    /// </summary>
    public class VizCommandControlObjectService
    {
        /// <summary>
        /// 日志
        /// </summary>
        private readonly static ILog log = LogManager.GetLogger(typeof(VizCommandControlObjectService));

        /// <summary>
        /// 不启用编辑类型
        /// </summary>
        /// <remarks>
        /// 不启用编辑则可以直接在GridControl的单元格中直径进行值修改
        /// </remarks>
        private static readonly List<VizControlFieldType> ALLOW_EDITING_FALSE_TYPES = new List<VizControlFieldType>
        {
             VizControlFieldType.none,
             VizControlFieldType.boolean,
             VizControlFieldType.image,
             VizControlFieldType.duplet,
             VizControlFieldType.triplet,
             VizControlFieldType.font
        };

        // =======================================================================
        // public Function
        // =======================================================================

        /// <summary>
        /// 获取控制对象List列定义
        /// </summary>
        /// <param name="schema">定义信息</param>
        /// <returns>列定义</returns>
        public List<GridColumnControlFieldDefinition> GetControlObjectListColumn(string schema)
        {
            List<GridColumnControlFieldDefinition> columns = new List<GridColumnControlFieldDefinition>();

            using (System.IO.MemoryStream ms = new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(schema)))
            {
                XElement element = XElement.Load(ms);
                XElement elementSchema = element.Element("schema");
                if (elementSchema == null)
                    return columns;

                ControlObject_Schema_Node schemaNode = new ControlObject_Schema_Node();
                schemaNode.FromXmlElement(elementSchema);

                foreach (ControlObject_Field_node field in schemaNode.Fields)
                {
                    GridColumnControlFieldDefinition column = new GridColumnControlFieldDefinition();
                    column.Header = (field.Name == field.Description) ? field.Name : $"({field.Description}){field.Name}";
                    column.FieldName = field.Name;
                    column.Type = this.GetControlFieldType(field.Type);
                    column.AllowEditing = !ALLOW_EDITING_FALSE_TYPES.Contains(column.Type);

                    columns.Add(column);
                }
            }

            return columns;
        }

        /// <summary>
        /// 获取控制对象List数据
        /// </summary>
        /// <param name="xml">数据</param>
        /// <returns>数据</returns>
        public List<ExpandoObject> GetControlObjectListData(string xml)
        {
            List<ExpandoObject> items = new List<ExpandoObject>();
            if (string.IsNullOrWhiteSpace(xml))
                return items;

            try
            {
                using (System.IO.MemoryStream ms = new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(xml)))
                {
                    XElement element = XElement.Load(ms);

                    ControlObject_Entry_Node root = new ControlObject_Entry_Node();
                    root.FromXmlElement(element);

                    foreach (var row in root.Elements)
                    {
                        IDictionary<string, object> obj = new ExpandoObject();
                        ControlObject_Entry_Node data = row.Entrys.FirstOrDefault(p => p.Name == "data");
                        if (data == null)
                            continue;

                        foreach (var cell in data.Entrys)
                        {
                            obj[cell.Name] = cell.Value;
                        }

                        items.Add(obj as ExpandoObject);
                    }
                }
            }
            catch (Exception ex)
            {
                log.Error(ex);
            }

            return items;
        }

        /// <summary>
        /// 获取控制对象XML数据
        /// </summary>
        /// <param name="items">控制对象数据</param>
        /// <returns>XML数据</returns>
        public string GetControlObjectXml(List<ExpandoObject> items)
        {
            ControlObject_Entry_Node root = new ControlObject_Entry_Node();
            foreach (IDictionary<string, object> row in items)
            {
                ControlObject_Element_Node element = new ControlObject_Element_Node();
                ControlObject_Entry_Node data = new ControlObject_Entry_Node();
                data.Name = "data";
                foreach (var kv in row)
                {
                    ControlObject_Entry_Node cell = new ControlObject_Entry_Node();
                    cell.Name = kv.Key;
                    cell.Value = kv.Value == null ? string.Empty : kv.Value.ToString();
                    data.Entrys.Add(cell);
                }
                element.Entrys.Add(data);
                root.Elements.Add(element);
            }

            XElement root_element = root.ToXmlElement();

            return $"<?xml version=\"1.0\"?>{root_element.ToString(SaveOptions.DisableFormatting)}";
        }

        /// <summary>
        /// 获取场景树节点信息
        /// </summary>
        /// <param name="conn">连接</param>
        /// <returns>场景树节点信息</returns>
        public List<VizTreeNodeInfo> GetTreeNodeList(ConnModel conn)
        {
            List<VizTreeNodeInfo> list = new List<VizTreeNodeInfo>();

            string cmd = $"MAIN_SCENE*TREE GET";
            string result = conn.EndpointManager.Request(cmd);
            List<string> nodes = result.Split('{').Select(p => p.Replace("}", "")).ToList();

            foreach (string node in nodes)
            {
                if (string.IsNullOrWhiteSpace(node))
                    continue;

                // 1 29073 object 1
                string[] pars = node.Split(' ');

                VizTreeNodeInfo info = new VizTreeNodeInfo();
                info.Path = pars[0];
                info.Num = pars[1];
                info.Name = pars[2];
                // pars[3] 有的节点有，有的节点没有

                list.Add(info);
            }

            return list;
        }

        /// <summary>
        /// 获取控制对象ID集合
        /// </summary>
        /// <param name="conn">连接</param>
        /// <returns>控制对象ID集合</returns>
        public List<string> GetControlObjectIds(ConnModel conn)
        {
            string cmd = $"MAIN_SCENE*TREE SEARCH_FOR_CONTAINER_WITH_PROPERTY BUILT_IN*FUNCTION BUILT_IN*FUNCTION*ControlObject";
            string result = conn.EndpointManager.Request(cmd);
            string[] source = result.Trim().Split(' ');

            return source.ToList();
        }

        /// <summary>
        /// 获取控制对象信息
        /// </summary>
        /// <param name="conn">连接</param>
        /// <returns>控制对象列表</returns>
        public ControlObjectModel GetControlObject(ConnModel conn)
        {
            ControlObjectModel obj = new ControlObjectModel();

            // Step 1. 获取场景树信息
            List<VizTreeNodeInfo> treeNodeList = this.GetTreeNodeList(conn);
            Dictionary<string, VizTreeNodeInfo> treeNodeDic = treeNodeList.ToDictionary(p => $"#{p.Num}", p => p);

            // Step 2. 获取控制对象ID集合
            List<string> ids = this.GetControlObjectIds(conn);

            // Step 3. 获取控制对象信息, 只获取第一个控制对象
            string id = ids.FirstOrDefault();
            if (string.IsNullOrWhiteSpace(id))
                return obj;

            if (!treeNodeDic.TryGetValue(id, out VizTreeNodeInfo objTreeNodeInfo))
                return obj;

            // Step 4. 获取第一个控制对象的信息
            obj.TreeNodePath = objTreeNodeInfo.Path;
            obj.TreeNodeName = objTreeNodeInfo.Name;
            obj.Description = this.GetControlObjectParameter(conn, obj.TreeNodePath, VizControlObjectParameters.description);
            obj.UseAllDirectors = this.GetControlObjectParameter(conn, obj.TreeNodePath, VizControlObjectParameters.use_all_directors) == "1";

            // Step 5. 获取第一个控制对象的字段描述
            string fieldDetails = this.GetControlObjectResult(conn, obj.TreeNodePath);
            obj.FieldDetails = fieldDetails ?? string.Empty;

            // Step 6. 更新控制对象字段
            this.UpdateControlObjectField(conn, obj, obj.FieldDetails, true);

            return obj;
        }

        /// <summary>
        /// 更新控制对象模型
        /// </summary>
        /// <param name="model">模型</param>
        /// <param name="entity">实体</param>
        public void UpdateControlObjectModel(ControlObjectModel model, ControlObjectEntity entity)
        {
            // Step 1. 获取第一个控制对象的信息
            model.TreeNodePath = entity.TreeNodePath;
            model.TreeNodeName = entity.TreeNodeName;
            model.Description = entity.Description;
            model.UseAllDirectors = entity.UseAllDirectors;
            model.FieldDetails = entity.FieldDetails ?? string.Empty;

            this.UpdateControlObjectField(null, model, model.FieldDetails, false);
        }

        /// <summary>
        /// 获取控制对象参数
        /// </summary>
        /// <param name="conn">连接</param>
        /// <param name="treeNodePath">场景数路径</param>
        /// <param name="parameter">参数</param>
        /// <returns>描述</returns>
        public string GetControlObjectParameter(ConnModel conn, string treeNodePath, VizControlObjectParameters parameter)
        {
            string cmd = $"MAIN_SCENE*TREE*{treeNodePath}*FUNCTION*ControlObject*{parameter} GET";
            string result = conn.EndpointManager.Request(cmd);

            return result;
        }

        /// <summary>
        /// 获取控制对象返回值
        /// </summary>
        /// <remarks>
        /// 返回结果按照“:”进行分割
        /// FieldIdentifier:VizID_SetGetfunction:Type:Min:Max:MaxChar:Description:AuxField:
        /// </remarks>
        /// <param name="conn">连接</param>
        /// <param name="treeNodePath">场景树路径</param>
        /// <returns>控制对象返回值</returns>
        public string GetControlObjectResult(ConnModel conn, string treeNodePath)
        {
            conn.EndpointManager.Send($"MAIN_SCENE*TREE*{treeNodePath}*FUNCTION*ControlObject*in SET LIST");
            string result = conn.EndpointManager.Request($"MAIN_SCENE*TREE*{treeNodePath}*FUNCTION*ControlObject*result GET");

            return result;
        }

        /// <summary>
        /// 获取控制对象字段值
        /// </summary>
        /// <param name="conn">连接</param>
        /// <param name="treeNodePath">场景树路径</param>
        /// <param name="fieldIdentifier">字段</param>
        /// <returns>字段值</returns>
        public string GetControlFieldValue(ConnModel conn, string treeNodePath, string fieldIdentifier)
        {
            conn.EndpointManager.Send($"MAIN_SCENE*TREE*{treeNodePath}*FUNCTION*ControlObject*in SET ON {fieldIdentifier} GET");
            string result = conn.EndpointManager.Request($"MAIN_SCENE*TREE*{treeNodePath}*FUNCTION*ControlObject*result GET");

            return result;
        }

        /// <summary>
        /// 设置控制对象值
        /// </summary>
        /// <param name="conn">连接</param>
        /// <param name="treeNodePath">场景节点路径</param>
        /// <param name="fieldIdentifier">字段</param>
        /// <param name="value">值</param>
        public void SetControlObjectValue(ConnModel conn, string treeNodePath, string fieldIdentifier, string value)
        {
            conn.EndpointManager.Send($"MAIN_SCENE*TREE*{treeNodePath}*FUNCTION*ControlObject*in SET ON {fieldIdentifier} SET {value}");
        }

        /// <summary>
        /// 设置控制对象值
        /// </summary>
        /// <param name="conn">连接</param>
        /// <param name="obj">控制对象</param>
        public void SetControlObject(ConnModel conn, ControlObjectModel obj)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append($"MAIN_SCENE*TREE*{obj.TreeNodePath}*FUNCTION*ControlObject*in SET ON ");
            foreach (ControlFieldNodeModel field in obj.AllFiledNodes)
            {
                sb.Append($"{field.FieldIdentifier} SET {field.Value}\\0");
            }

            conn.EndpointManager.Send(sb.ToString());
        }

        /// <summary>
        /// 设置列表控制对象的值
        /// </summary>
        /// <param name="conn">连接</param>
        /// <param name="treeNodePath">场景节点路径</param>
        /// <param name="listName">列表名称</param>
        /// <param name="listLine">列表值位序</param>
        /// <param name="fieldIdentifier">字段</param>
        /// <param name="value">值</param>
        public void SetControlObjectListValue(ConnModel conn, string treeNodePath, string listName, int listLine, string fieldIdentifier, string value)
        {
            conn.EndpointManager.Send($"MAIN_SCENE*TREE*{treeNodePath}*FUNCTION*ControlObject*in SET WITH {listName} INDEX {listLine} ON {fieldIdentifier} SET {value}");
        }

        /// <summary>
        /// 设置控制字段聚焦
        /// </summary>
        /// <param name="conn">连接</param>
        /// <param name="obj">控制对象</param>
        /// <param name="field">控制字段</param>
        public void ShowFocus(ConnModel conn, ControlObjectModel obj, ControlFieldNodeModel field)
        {
            if (conn == null)
                throw new ArgumentNullException(nameof(conn));

            VizConfigEntity config = ApplicationDomainEx.VizConfig;

            if (config.EngineFullType == EngineFullType.VIZ_Eng3)
            {
                conn.EndpointManager.Send($"RENDERER*TREE*{obj.TreeNodePath}*FUNCTION*ControlObject*in SET FOCUS {field.FieldIdentifier}");
            }
            else if (config.EngineFullType == EngineFullType.VIZ_Eng4)
            {
                conn.EndpointManager.Send($"{ApplicationDomainEx.VizPreviewRenderer}*TREE*{obj.TreeNodePath}*FUNCTION*ControlObject*in SET FOCUS {field.FieldIdentifier}");
            }
        }

        /// <summary>
        /// 设置控制字段聚焦
        /// </summary>
        /// <param name="conn">连接</param>
        /// <param name="obj">控制对象</param>
        /// <param name="field">控制字段</param>
        /// <param name="listLine">行数</param>
        /// <param name="fieldIdentifier">字段名</param>
        public void ShowFocus(ConnModel conn, ControlObjectModel obj, ControlFieldNodeModel field, int listLine, string fieldIdentifier)
        {
            if (conn == null)
                throw new ArgumentNullException(nameof(conn));

            VizConfigEntity config = ApplicationDomainEx.VizConfig;

            if (config.EngineFullType == EngineFullType.VIZ_Eng3)
            {
                conn.EndpointManager.Send($"RENDERER*TREE*{obj.TreeNodePath}*FUNCTION*ControlObject*in SET WITH {field.FieldIdentifier} INDEX {listLine} FOCUS {fieldIdentifier}");
            }
            else if (config.EngineFullType == EngineFullType.VIZ_Eng4)
            {
                conn.EndpointManager.Send($"{ApplicationDomainEx.VizPreviewRenderer}*TREE*{obj.TreeNodePath}*FUNCTION*ControlObject*in SET WITH {field.FieldIdentifier} INDEX {listLine} FOCUS {fieldIdentifier}");
            }
        }

        // =======================================================================
        // Private Function
        // =======================================================================

        /// <summary>
        /// 获取控制字段类型
        /// </summary>
        /// <param name="type">字段类型</param>
        /// <returns>控制对象字段类型</returns>
        private VizControlFieldType GetControlFieldType(string type)
        {
            if (type == "text")
                return VizControlFieldType.text;

            if (type == "bool")
                return VizControlFieldType.boolean;

            if (type == "image")
                return VizControlFieldType.image;

            if (type == "richtext")
                return VizControlFieldType.richtext;

            if (type == "integer")
                return VizControlFieldType.integer;

            if (type == "float")
                return VizControlFieldType.@float;

            if (type == "duplet")
                return VizControlFieldType.duplet;

            if (type == "triplet")
                return VizControlFieldType.triplet;

            if (type == "font")
                return VizControlFieldType.font;

            if (type.StartsWith("<?xml"))
                return VizControlFieldType.list;

            return VizControlFieldType.none;
        }

        /// <summary>
        /// 更新控制对象字段
        /// </summary>
        /// <param name="conn">连接</param>
        /// <param name="model">控制字段模型</param>
        /// <param name="fieldDetails">字段描述</param>
        /// <param name="isGetValueFromViz">是否从Viz获取字段值</param>
        private void UpdateControlObjectField(ConnModel conn, ControlObjectModel model, string fieldDetails, bool isGetValueFromViz)
        {
            foreach (string fieldDetail in fieldDetails.Split('\n'))
            {
                // 返回示例： BTNAME:#10245*GEOM*TEXT:text:::-1:BTNAME:single_line, location_id=#10245, location=2/3/5

                if (string.IsNullOrWhiteSpace(fieldDetail))
                    continue;

                string[] pars = fieldDetail.Split(':');
                string num = pars[1].Split('*').FirstOrDefault();

                ControlFieldNodeModel node = new ControlFieldNodeModel();

                node.FieldIdentifier = pars[0];
                node.Route = node.FieldIdentifier.Split('.').ToList();
                node.Num = num;
                node.TypeSchema = pars[2];
                node.Type = this.GetControlFieldType(node.TypeSchema);
                if (isGetValueFromViz)
                {
                    node.Value = this.GetControlFieldValue(conn, model.TreeNodePath, node.FieldIdentifier);
                }

                if (pars.Length >= 7)
                {
                    node.Description = pars[6];
                }

                model.AllFiledNodes.Add(node);
            }

            // Step 6. 根据字段FieldIdentifier构建树形结构
            int count = 1;
            List<ControlFieldNodeModel> fieldNodes = model.AllFiledNodes.Where(p => p.Route.Count == count).ToList();
            List<ControlFieldNodeModel> parents = fieldNodes;

            while (true)
            {
                ++count;

                List<ControlFieldNodeModel> items = model.AllFiledNodes.Where(p => p.Route.Count == count).ToList();
                if (items.Count == 0)
                    break;

                foreach (ControlFieldNodeModel item in items)
                {
                    string parentFieldIdentifier = string.Join(".", item.Route.Take(count - 1));
                    ControlFieldNodeModel parent = parents.FirstOrDefault(p => p.FieldIdentifier == parentFieldIdentifier);
                    if (parent == null)
                        continue;

                    parent.Items.Add(item);
                }

                parents = items;
            }

            model.FieldNodes = fieldNodes;
        }
    }
}
