using log4net;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace VIZ.Framework.Core
{
    /// <summary>
    /// USB设备模型
    /// </summary>
    public class GPIOModel : ModelBase
    {
        /// <summary>
        /// 日志
        /// </summary>
        private readonly static ILog log = LogManager.GetLogger(typeof(GPIOModel));

        /// <summary>
        /// 引脚掩码
        /// </summary>
        private readonly static uint PIN_MASK = GPIOPin.PIN_0_TO_15;

        /// <summary>
        /// 读取引脚值延时（单位：毫秒）
        /// </summary>
        private readonly static int READ_PIN_VALUE_DELAY = 200;

        /// <summary>
        /// 重新打开设备延时
        /// </summary>
        private readonly static int REOPEN_DEVICE_DELAY = 1000;

        /// <summary>
        /// USB设备模型
        /// </summary>
        /// <param name="deviceInfo">USB设备信息</param>
        public GPIOModel(USBDeviceInfo deviceInfo)
        {
            this.Update(deviceInfo);
        }

        #region Handle -- 句柄

        private int handle;
        /// <summary>
        /// 句柄
        /// </summary>
        public int Handle
        {
            get { return handle; }
            set { handle = value; this.RaisePropertyChanged(nameof(Handle)); }
        }

        #endregion

        #region USBDeviceInfo -- 设备信息

        private USBDeviceInfo deviceInfo;
        /// <summary>
        /// 设备信息
        /// </summary>
        public USBDeviceInfo DeviceInfo
        {
            get { return deviceInfo; }
            private set { deviceInfo = value; this.RaisePropertyChanged(nameof(DeviceInfo)); }
        }

        #endregion

        #region PIN_0 -- 引脚0是否接通

        private bool pin_0;
        /// <summary>
        /// 引脚0是否接通
        /// </summary>
        public bool PIN_0
        {
            get { return pin_0; }
            set { pin_0 = value; this.RaisePropertyChanged(nameof(PIN_0)); }
        }

        #endregion

        #region PIN_1 -- 引脚1是否接通

        private bool pin_1;
        /// <summary>
        /// 引脚1是否接通
        /// </summary>
        public bool PIN_1
        {
            get { return pin_1; }
            set { pin_1 = value; this.RaisePropertyChanged(nameof(PIN_1)); }
        }

        #endregion

        #region PIN_2 -- 引脚2是否接通

        private bool pin_2;
        /// <summary>
        /// 引脚2是否接通
        /// </summary>
        public bool PIN_2
        {
            get { return pin_2; }
            set { pin_2 = value; this.RaisePropertyChanged(nameof(PIN_2)); }
        }

        #endregion

        #region PIN_3 -- 引脚3是否接通

        private bool pin_3;
        /// <summary>
        /// 引脚3是否接通
        /// </summary>
        public bool PIN_3
        {
            get { return pin_3; }
            set { pin_3 = value; this.RaisePropertyChanged(nameof(PIN_3)); }
        }

        #endregion

        #region PIN_4 -- 引脚4是否接通

        private bool pin_4;
        /// <summary>
        /// 引脚4是否接通
        /// </summary>
        public bool PIN_4
        {
            get { return pin_4; }
            set { pin_4 = value; this.RaisePropertyChanged(nameof(PIN_4)); }
        }

        #endregion

        #region PIN_5 -- 引脚5是否接通

        private bool pin_5;
        /// <summary>
        /// 引脚5是否接通
        /// </summary>
        public bool PIN_5
        {
            get { return pin_5; }
            set { pin_5 = value; this.RaisePropertyChanged(nameof(PIN_5)); }
        }

        #endregion

        #region PIN_6 -- 引脚6是否接通

        private bool pin_6;
        /// <summary>
        /// 引脚6是否接通
        /// </summary>
        public bool PIN_6
        {
            get { return pin_6; }
            set { pin_6 = value; this.RaisePropertyChanged(nameof(PIN_6)); }
        }

        #endregion

        #region PIN_7 -- 引脚7是否接通

        private bool pin_7;
        /// <summary>
        /// 引脚7是否接通
        /// </summary>
        public bool PIN_7
        {
            get { return pin_7; }
            set { pin_7 = value; this.RaisePropertyChanged(nameof(PIN_7)); }
        }

        #endregion

        #region PIN_8 -- 引脚8是否接通

        private bool pin_8;
        /// <summary>
        /// 引脚8是否接通
        /// </summary>
        public bool PIN_8
        {
            get { return pin_8; }
            set { pin_8 = value; this.RaisePropertyChanged(nameof(PIN_8)); }
        }

        #endregion

        #region PIN_9 -- 引脚9是否接通

        private bool pin_9;
        /// <summary>
        /// 引脚9是否接通
        /// </summary>
        public bool PIN_9
        {
            get { return pin_9; }
            set { pin_9 = value; this.RaisePropertyChanged(nameof(PIN_9)); }
        }

        #endregion

        #region PIN_10 -- 引脚10是否接通

        private bool pin_10;
        /// <summary>
        /// 引脚10是否接通
        /// </summary>
        public bool PIN_10
        {
            get { return pin_10; }
            set { pin_10 = value; this.RaisePropertyChanged(nameof(PIN_10)); }
        }

        #endregion

        #region PIN_11 -- 引脚11是否接通

        private bool pin_11;
        /// <summary>
        /// 引脚11是否接通
        /// </summary>
        public bool PIN_11
        {
            get { return pin_11; }
            set { pin_11 = value; this.RaisePropertyChanged(nameof(PIN_11)); }
        }

        #endregion

        #region PIN_12 -- 引脚12是否接通

        private bool pin_12;
        /// <summary>
        /// 引脚12是否接通
        /// </summary>
        public bool PIN_12
        {
            get { return pin_12; }
            set { pin_12 = value; this.RaisePropertyChanged(nameof(PIN_12)); }
        }

        #endregion

        #region PIN_13 -- 引脚13是否接通

        private bool pin_13;
        /// <summary>
        /// 引脚13是否接通
        /// </summary>
        public bool PIN_13
        {
            get { return pin_13; }
            set { pin_13 = value; this.RaisePropertyChanged(nameof(PIN_13)); }
        }

        #endregion

        #region PIN_14 -- 引脚14是否接通

        private bool pin_14;
        /// <summary>
        /// 引脚14是否接通
        /// </summary>
        public bool PIN_14
        {
            get { return pin_14; }
            set { pin_14 = value; this.RaisePropertyChanged(nameof(PIN_14)); }
        }

        #endregion

        #region PIN_15 -- 引脚15是否接通

        private bool pin_15;
        /// <summary>
        /// 引脚15是否接通
        /// </summary>
        public bool PIN_15
        {
            get { return pin_15; }
            set { pin_15 = value; this.RaisePropertyChanged(nameof(PIN_15)); }
        }

        #endregion

        #region DeviceState -- 设备状态

        private bool deviceState;
        /// <summary>
        /// 设备状态
        /// </summary>
        public bool DeviceState
        {
            get { return deviceState; }
            set { deviceState = value; this.RaisePropertySaveChanged(nameof(DeviceState)); }
        }

        #endregion

        /// <summary>
        /// 引脚值改变时触发
        /// </summary>
        public event EventHandler<GPIOModelPinValueChangedEventArgs> PinValueChanged;

        /// <summary>
        /// 状态读取线程
        /// </summary>
        private Task readTask;

        /// <summary>
        /// 状态读取线程信息
        /// </summary>
        private TaskInfo readTaskInfo;

        /// <summary>
        /// 更新设备信息
        /// </summary>
        /// <param name="deviceInfo">设备信息</param>
        public void Update(USBDeviceInfo deviceInfo)
        {
            this.DeviceInfo = deviceInfo;
            this.Handle = deviceInfo.Handle;
        }

        /// <summary>
        /// 打开设备
        /// </summary>
        /// <returns>是否成功打开</returns>
        public bool Open()
        {
            this.DeviceState = false;

            if (this.DeviceInfo == null)
                return false;

            if (!this.DeviceInfo.Open())
                return false;

            if (!this.DeviceInfo.SetInput(PIN_MASK, GPIOPuPd.NOPULL))
                return false;

            if (this.readTaskInfo != null)
            {
                this.readTaskInfo.IsCancel = true;
            }

            this.DeviceState = true;

            if (this.readTask == null)
            {
                this.readTaskInfo = new TaskInfo();
                this.readTask = Task.Run(this.executeRead);
            }

            return true;
        }

        /// <summary>
        /// 关闭设备
        /// </summary>
        public void Close()
        {
            if (this.readTaskInfo != null)
            {
                this.readTaskInfo.IsCancel = true;
            }
            this.readTaskInfo = null;
            this.readTask = null;
            this.deviceInfo?.Close();
            this.DeviceState = false;
        }

        /// <summary>
        /// 执行读取
        /// </summary>
        private void executeRead()
        {
            TaskInfo taskInfo = this.readTaskInfo;

            while (!taskInfo.IsCancel)
            {
                try
                {
                    // 无设备信息
                    if (this.DeviceInfo == null)
                    {
                        Task.Delay(READ_PIN_VALUE_DELAY).Wait();
                        continue;
                    }

                    // 设备状态不可用时尝试重新初始化设备
                    if (!this.DeviceState && !this.Open())
                    {
                        Task.Delay(REOPEN_DEVICE_DELAY).Wait();
                        continue;
                    }

                    // 每轮尝试5次读取
                    uint pinValue;
                    if (!this.DeviceInfo.Read(PIN_MASK, out pinValue))
                    {
                        pinValue = GPIOPin.PIN_DEFAULT_VALUE;
                    }
                    this.updatePinValue(pinValue);

                    Task.Delay(READ_PIN_VALUE_DELAY).Wait();
                }
                catch (Exception ex)
                {
                    log.Error(ex);
                }
            }
        }

        /// <summary>
        /// 更新引脚值
        /// </summary>
        /// <param name="pinValue">引脚值</param>
        private void updatePinValue(uint pinValue)
        {
            bool[] state = new bool[16];

            state[0] = (GPIOPin.PIN_0 & pinValue) == 0;
            state[1] = (GPIOPin.PIN_1 & pinValue) == 0;
            state[2] = (GPIOPin.PIN_2 & pinValue) == 0;
            state[3] = (GPIOPin.PIN_3 & pinValue) == 0;
            state[4] = (GPIOPin.PIN_4 & pinValue) == 0;
            state[5] = (GPIOPin.PIN_5 & pinValue) == 0;
            state[6] = (GPIOPin.PIN_6 & pinValue) == 0;
            state[7] = (GPIOPin.PIN_7 & pinValue) == 0;
            state[8] = (GPIOPin.PIN_8 & pinValue) == 0;
            state[9] = (GPIOPin.PIN_9 & pinValue) == 0;
            state[10] = (GPIOPin.PIN_10 & pinValue) == 0;
            state[11] = (GPIOPin.PIN_11 & pinValue) == 0;
            state[12] = (GPIOPin.PIN_12 & pinValue) == 0;
            state[13] = (GPIOPin.PIN_13 & pinValue) == 0;
            state[14] = (GPIOPin.PIN_14 & pinValue) == 0;
            state[15] = (GPIOPin.PIN_15 & pinValue) == 0;

            WPFHelper.BeginInvoke(() =>
            {
                // 修改引脚值
                this.PIN_0 = state[0];
                this.PIN_1 = state[1];
                this.PIN_2 = state[2];
                this.PIN_3 = state[3];
                this.PIN_4 = state[4];
                this.PIN_5 = state[5];
                this.PIN_6 = state[6];
                this.PIN_7 = state[7];
                this.PIN_8 = state[8];
                this.PIN_9 = state[9];
                this.PIN_10 = state[10];
                this.PIN_11 = state[11];
                this.PIN_12 = state[12];
                this.PIN_13 = state[13];
                this.PIN_14 = state[14];
                this.PIN_15 = state[15];

                // 触发引脚值改变事件
                if (this.PinValueChanged == null)
                    return;

                GPIOModelPinValueChangedEventArgs args = new GPIOModelPinValueChangedEventArgs();
                args.GPIOModel = this;
                args.PinValue = pinValue;

                this.PinValueChanged.Invoke(this, args);
            });
        }
    }
}
