using DbCommon.BusinessCore.BaseCore;
using DbCommon.Enties.DbModels;
using DbCommon.Extenions;
using Microsoft.CSharp.RuntimeBinder;
using MiniExcelLibs;
using MiniExcelLibs.Attributes;
using ProjectManagementSystem.Common.Log;
using System;
using System.Collections.Generic;
using System.Data;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace ProjectManagementSystem.Common.Config
{
    public class ExcelConfig
    {
        private static ExcelConfig m_instance;
        private static readonly object m_locker = new object();
        public static ExcelConfig Instance
        {
            get
            {
                if (m_instance == null)
                {
                    lock (m_locker)
                    {
                        if (m_instance == null)
                        {
                            m_instance = new ExcelConfig();
                            if (!m_instance.Load())
                            {
                                m_instance = null;
                            }
                        }
                    }
                }
                return m_instance;
            }
        }

        private Dictionary<int, RouteConfig> m_routeConfigDict;
        private Dictionary<string, string> m_locationAddressDict;

        public List<RouteConfig> RouteConfigList { get; private set; }
        public List<MaterialDetail> MaterialDetailList { get; private set; }
        public List<PlcBehavior> PlcBehaviorList { get; private set; }
        public List<TaskRely> TaskRelyList { get; private set; }
        public IEnumerable<IDictionary<string, object>> RouteConfigDicts { get; private set; }

        private ExcelConfig()
        {
            m_routeConfigDict = new Dictionary<int, RouteConfig>();
            m_locationAddressDict = new Dictionary<string, string>();
            MaterialDetailList = new List<MaterialDetail>();
        }

        public RouteConfig GetRouteConfig(int route)
        {
            if (m_routeConfigDict.ContainsKey(route))
            {
                return m_routeConfigDict[route];
            }
            return null;
        }

        public RouteConfig GetRouteConfig(string location)
        {
            return RouteConfigList.Find(d => d.LocationCode == location);
        }

        /// <summary>
        /// 根据库位号和列名，获取文本
        /// </summary>
        /// <param name="location">库位号</param>
        /// <param name="member">列名</param>
        /// <returns></returns>
        private string GetLocationMember(string location, string member)
        {
            var item = RouteConfigDicts.FirstOrDefault(d => d.ContainsKey("库位号") && d["库位号"].ToString() == location);
            if (item != null
                && item.ContainsKey(member))
            {
                return item[member]?.ToString();
            }

            return null;
        }

        public string GetLocationMemberCache(string location, string member)
        {
            string key = $"{location}-{member}";
            if (m_locationAddressDict.ContainsKey(key))
            {
                return m_locationAddressDict[key];
            }
            else
            {
                string realAddr = GetLocationMember(location, member);
                m_locationAddressDict.Add(key, realAddr);
                return realAddr;
            }
        }

        public object GetProperty(object o, string member)
        {
            if (o == null) throw new ArgumentNullException("o");
            if (member == null) throw new ArgumentNullException("member");
            Type scope = o.GetType();
            IDynamicMetaObjectProvider provider = o as IDynamicMetaObjectProvider;
            if (provider != null)
            {
                ParameterExpression param = Expression.Parameter(typeof(object));
                DynamicMetaObject mobj = provider.GetMetaObject(param);
                GetMemberBinder binder = (GetMemberBinder)Microsoft.CSharp.RuntimeBinder.Binder.GetMember(0, member, scope, new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(0, null) });
                DynamicMetaObject ret = mobj.BindGetMember(binder);
                BlockExpression final = Expression.Block(
                    Expression.Label(CallSiteBinder.UpdateLabel),
                    ret.Expression
                );
                LambdaExpression lambda = Expression.Lambda(final, param);
                Delegate del = lambda.Compile();
                return del.DynamicInvoke(o);
            }
            else
            {
                return o.GetType().GetProperty(member, BindingFlags.Public | BindingFlags.Instance).GetValue(o, null);
            }
        }

        private bool Load()
        {
            try
            {
                string sLocation = System.Reflection.Assembly.GetExecutingAssembly().Location;
                FileInfo finfo = new FileInfo(sLocation);

                string currPath = finfo.DirectoryName;
                //string dataPath = Path.Combine(currPath, "data");
                string dataFile = Path.Combine(currPath, "基础配置表.xlsx");

                /* 如果没有数据路径，则创建该路径 */
                //if (false == Directory.Exists(dataPath))
                //{
                //    Directory.CreateDirectory(dataPath);
                //}

                RouteConfigDicts = MiniExcel.Query(dataFile, true, sheetName: "线路配置").Cast<IDictionary<string, object>>();

                RouteConfigList = MiniExcel.Query<RouteConfig>(dataFile, sheetName: "线路配置").ToList();
                PlcBehaviorList = MiniExcel.Query<PlcBehavior>(dataFile, sheetName: "PLC行为码").ToList();
                TaskRelyList = MiniExcel.Query<TaskRely>(dataFile, sheetName: "任务依赖").ToList();

                foreach (var item in RouteConfigList)
                {
                    if (!m_routeConfigDict.ContainsKey(item.Route))
                    {
                        m_routeConfigDict.Add(item.Route, item);
                    }
                }

                foreach (var item in PlcBehaviorList)
                {
                    item.DbAddressRead = item.DbAddressReadStr.ToStringArray();
                    item.DbAddressWrite = item.DbAddressWriteStr.ToStringArray();
                    item.DbAddressFinalWrite = item.DbAddressFinalWriteStr.ToStringArray();
                    item.DbValueRead = item.DbValueReadStr.ToBoolArray();
                    item.DbValueWrite = item.DbValueWriteStr.ToBoolArray();
                    item.DbValueFinalWrite = item.DbValueFinalWriteStr.ToBoolArray();
                }

                foreach (var item in TaskRelyList)
                {
                    item.EndWareHouse = item.EndWareHouseStr.ToStringArray();
                }

                MaterialDetailManager materialDetailManager = new MaterialDetailManager();
                var materialDetailList = materialDetailManager.GetList();

                for (int i = 0; i < materialDetailList.Count; i++)
                {
                    var mData = materialDetailList[i];
                    string[] materialIdArray = mData.MaterialId.Split(new char[] { '|' });
                    string[] materialNameArray = mData.MaterialName.Split(new char[] { '|' });
                    string[] materialNumberArray = mData.MaterialNumber.Split(new char[] { '|' });

                    for (int index = 0; index < materialIdArray.Length; index++)
                    {
                        MaterialDetail materialDetail = new MaterialDetail();
                        materialDetail.WareHouseCode = mData.WareHouseCode;
                        materialDetail.LocationCode = mData.LocationCode;
                        materialDetail.MaterialId = materialIdArray[index];
                        materialDetail.MaterialName = materialNameArray[index];
                        materialDetail.MaterialNumber = materialNumberArray[index];
                        MaterialDetailList.Add(materialDetail);
                    }
                }

                return true;
            }
            catch (Exception ex)
            {
                CLog.Instance.GetLog("System").WriteException("Exception", ex);
            }
            return false;
        }
    }


    public class RouteConfig
    {
        [ExcelColumnName("线路")]
        public int Route { get; set; }
        [ExcelColumnName("仓库号")]
        public string WareHouseCode { get; set; }
        [ExcelColumnName("库位号")]
        public string LocationCode { get; set; }
        [ExcelColumnName("备注1")]
        public string Remark { get; set; }
        [ExcelColumnName("备注2")]
        public string Remark2 { get; set; }
        [ExcelColumnName("备注3")]
        public string Remark3 { get; set; }
        [ExcelColumnName("叫料状态")]
        public int CallLineStauts { get; set; }
        [ExcelColumnName("取消叫料状态")]
        public int CancelLineStauts { get; set; }

        [ExcelColumnName("IP地址(PLC)")]
        public string PlcIpAddr { get; set; }
        [ExcelColumnName("站号")]
        public int Station { get; set; }
        [ExcelColumnName("心跳信号(PLC)")]
        public string PlcHeartbearAddr { get; set; }
        [ExcelColumnName("心跳信号(AGV)")]
        public string PlcHeartbearAddr_AGV { get; set; }
    }

    public class PlcBehavior
    {
        [ExcelColumnName("行为码")]
        public int Behavior { get; set; }
        [ExcelColumnName("DB地址写入")]
        public string DbAddressWriteStr { get; set; }
        [ExcelColumnName("DB地址读取")]
        public string DbAddressReadStr { get; set; }
        [ExcelColumnName("DB地址最终写入")]
        public string DbAddressFinalWriteStr { get; set; }
        [ExcelColumnName("写入值")]
        public string DbValueWriteStr { get; set; }
        [ExcelColumnName("读取值")]
        public string DbValueReadStr { get; set; }
        [ExcelColumnName("最终写入值")]
        public string DbValueFinalWriteStr { get; set; }

        public string[] DbAddressWrite { get; set; }
        public string[] DbAddressRead { get; set; }
        public string[] DbAddressFinalWrite { get; set; }
        public bool[] DbValueWrite { get; set; }
        public bool[] DbValueRead { get; set; }
        public bool[] DbValueFinalWrite { get; set; }

        [ExcelColumnName("备注")]
        public string Remark { get; set; }
    }

    public class TaskRely
    {
        [ExcelColumnName("新任务开始仓库")]
        public string StartWareHouse { get; set; }
        [ExcelColumnName("旧任务结束仓库")]
        public string EndWareHouseStr { get; set; }
        public string[] EndWareHouse { get; set; }
    }
}
