using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using ONNONLed5KSDKD;
using tool;
namespace UnNomalized_node
{
    public class BroadcastBoardManager : Singleton<BroadcastBoardManager>
    {
        public List<BroadcastBoard> boardList;
        public List<BroadcastSquareBoard> squareBoardList;
        public List<BroadcastFetchBoard> fetchBoardList;

        public static Led5kSDK.bx_5k_area_header header_model;
        public static object screen_lock;
        public static bool isClosing;

        private BroadcastBoardManager()
        {
            screen_lock = new object();

            header_model = new Led5kSDK.bx_5k_area_header();
            header_model.AreaType = 0x06;
            //横向偏移
            header_model.AreaX = 0;
            //纵向偏移
            header_model.AreaY = 0;
            //宽度，需将点数除以8
            header_model.AreaWidth = 32 / 8;
            //高度
            header_model.AreaHeight = 32;
            //0自动循环显示 1完成后停留在最后一页 2超时未完成删除该信息
            header_model.RunMode = 0;
            header_model.Reserved1 = 0;
            header_model.Reserved2 = 0;
            header_model.Reserved3 = 0;
            //1静止显示 2快速打出 3向左移动 4向右移动 5向上移动 6向下移动
            header_model.DisplayMode = 0x01;
            header_model.ExitMode = 0x00;
            //1快-24慢 
            header_model.Speed = 5;
            //多少个0.5s
            header_model.StayTime = 6;
            //1单行 2多行
            header_model.SingleLine = 0x01;
            //1手动换行 2自动
            header_model.NewLine = 0x01;
            header_model.Timeout = 2;
            // 行间距
            header_model.Lines_sizes = 0;

            Led5kSDK.InitSdk(2, 2);
            boardList = new List<BroadcastBoard>();
            squareBoardList = new List<BroadcastSquareBoard>();
            fetchBoardList = new List<BroadcastFetchBoard>();
            isClosing = false;
        }

        /// <summary>
        /// 增加屏幕
        /// </summary>
        /// <param name="id"></param>
        /// <param name="led_ip"></param>
        /// <param name="led_port"></param>
        /// <param name="card_type"></param>
        /// <param name="tmout_sec"></param>
        /// <param name="mode"></param>
        /// <returns></returns>
        public void AddScreen(int id, byte[] led_ip, uint led_port, Led5kSDK.bx_5k_card_type card_type, int tmout_sec, int mode)
        {
            BroadcastBoard bb = new BroadcastBoard(id, led_ip, led_port, card_type, tmout_sec, mode);
            bb.InitBoard();
            boardList.Add(bb);
        }
        public void AddSquareScreen(int id, byte[] led_ip, uint led_port, Led5kSDK.bx_5k_card_type card_type, int tmout_sec, int mode)
        {
            BroadcastSquareBoard bsb = new BroadcastSquareBoard(id, led_ip, led_port, card_type, tmout_sec, mode);
            bsb.InitBoard();
            squareBoardList.Add(bsb);
        }
        public void AddFetchScreen(int id, byte[] led_ip, uint led_port, Led5kSDK.bx_5k_card_type card_type, int tmout_sec, int mode)
        {
            BroadcastFetchBoard bsb = new BroadcastFetchBoard(id, led_ip, led_port, card_type, tmout_sec, mode);
            bsb.InitBoard();
            fetchBoardList.Add(bsb);
        }
        /// <summary>
        /// 关闭所有屏幕连接
        /// </summary>
        public void Close()
        {
            isClosing = true;
            //lock (screen_lock)
            //{
            //    Led5kSDK.ReleaseSdk();
            //}
        }
        /// <summary>
        /// 时间同步
        /// </summary>
        public void UpdateTimeAll()
        {
            foreach (BroadcastBoard bb in boardList)
            {
                Task.Factory.StartNew(() =>
                {
                    uint handle = Led5kSDK.CreateClient(bb.boardParams.ip, bb.boardParams.port, bb.boardParams.card_type, bb.boardParams.timeout_sec, bb.boardParams.mode, null);
                    if (handle == 0)
                    {
                        bb.connection = false;
                        Console.WriteLine(bb.boardParams.ip + ",connection failed.");
                    }
                    else
                    {
                        bb.connection = true;
                    }
                    int err = Led5kSDK.CON_SytemClockCorrect(handle);
                    Led5kSDK.Destroy(handle);
                    if (err != 0)
                    {
                        Console.WriteLine(bb.boardParams.ip + ", clock correction failed.");
                    }
                });
            }
        }
    }

    public class BroadcastBoard
    {
        /// <summary>
        /// 显示最大队列计数
        /// </summary>
        public const int max_queue_size = 1;
        /// <summary>
        /// 最大重复获取状态次数
        /// </summary>
        public const int max_state_count = 50;
        /// <summary>
        /// 本LED屏参数
        /// </summary>
        public BoardParams boardParams;
        /// <summary>
        /// 硬件连接状态，默认已连接，消息发送失败时设置为断线
        /// </summary>
        public bool connection;
        /// <summary>
        /// 各区域默认显示字符串
        /// </summary>
        private List<string> defaultStr;
        /// <summary>
        /// 当前显示状态
        /// </summary>
        public int ledScanState;// 0空点云或有杂物，1位正常，2位前超界，3位后超界，4位左超界，5位右超界，6位超高
        /// <summary>
        /// 已重复获得同样状态的次数
        /// </summary>
        public int lastRepeatCount;
        private string[] arrows = { "\\T001arrU64|\\n", "\\T001arrD64|\\n", "\\T001arrL64|\\n", "\\T001arrR64|\\n" };
        //************************************************ 方法块 **************************************************
        // *************** 公有方法 ***************
        public BroadcastBoard(int id, byte[] ip, uint port, Led5kSDK.bx_5k_card_type card_type, int tmout_sec, int mode)
        {
            connection = true;
            defaultStr = new List<string>();
            defaultStr.Add("象智临光迎欢");
            defaultStr.Add(" ");
            defaultStr.Add(" ");
            boardParams = new BoardParams(id, ip, port, card_type, tmout_sec, mode);
        }

        #region puai_funcs
        /// <summary>
        /// 初始化控制板
        /// 注意!!!!!此函数定义仅适用于普爱内屏
        /// </summary>
        /// <param name="handle"></param>
        /// <returns></returns>
        public virtual bool InitBoard()
        {
            if (boardParams == null)
                return false;

            //第一行128*16显示文字,上下8像素留空
            AreaInfo area1 = new AreaInfo(boardParams.id, 0, BroadcastBoardManager.header_model);
            area1.header.AreaWidth = 2;//16;// 2=16/8
            area1.header.AreaHeight = 96;//16;//96
            area1.header.AreaX = 1;//0 / 8; // 8/8
            area1.header.AreaY = 0;// 8; // 0
            area1.header.SingleLine = 0x02;
            area1.header.NewLine = 0x02;
            area1.header.DisplayMode = 0x02;
            area1.header.StayTime = 2;
            area1.header.RunMode = 1;
            area1.header.Speed = 2;
            area1.header.Timeout = 20;
            area1.header.DynamicAreaLoc = (byte)0;

            boardParams.areas.Add(area1);
            //Thread.Sleep(50);

            //第二、三行64*64显示一个指示标志
            AreaInfo area2 = new AreaInfo(boardParams.id, 1, BroadcastBoardManager.header_model);
            area2.header.AreaWidth = 8;// 8=64/8
            area2.header.AreaHeight = 64;
            area2.header.AreaX = 32 / 8; // 8/8
            area2.header.AreaY = 16;// 32; // 8/8
            area2.header.SingleLine = 0x01;
            area2.header.NewLine = 0x02;
            area2.header.DisplayMode = 0x02;
            area2.header.StayTime = 10;
            area2.header.RunMode = 2;
            area2.header.DynamicAreaLoc = (byte)1;
            //area2.header.AreaX = (32 + 16) / 8;
            //area2.header.AreaY = 16;
            boardParams.areas.Add(area2);

            //Thread.Sleep(50);
            //顶角右偏112像素的8*8块专用于播放声音
            AreaInfo areaSound = new AreaInfo(boardParams.id, 2, BroadcastBoardManager.header_model);
            areaSound.header.AreaWidth = 2;// 2=16/8
            areaSound.header.AreaHeight = 16;
            areaSound.header.SingleLine = 0x02;
            areaSound.header.NewLine = 0x02;
            areaSound.header.DisplayMode = 0x02;
            areaSound.header.StayTime = 1;
            areaSound.header.RunMode = 2;
            areaSound.header.AreaX = 14;// 0;// 32 / 8;
            areaSound.header.AreaY = 0;// 64; //0
            areaSound.header.Speed = 4;
            areaSound.header.DynamicAreaLoc = (byte)2;
            boardParams.areas.Add(areaSound);

            HandlingThread(150);
            return true;
        }
        /// <summary>
        /// 更新第一块区域文字信息
        /// </summary>
        /// <param name="sentence"></param>
        /// <param name="slice"></param>
        public bool UpdateText(string sentence, int waitSec, int runMode = -1, int dispMode = -1, int boardIndex = 0, bool horizontal = false, int slice = 60)
        {
            int i = 0;
            if (boardIndex < 0 || boardIndex > 2)
                return false;
            List<string> sentenceContent = new List<string>();
            // 竖屏
            if (!horizontal)
            {
                // 拆分字符串
                while (i < sentence.Length)
                {
                    List<char> chars = new List<char>();
                    if ((i + slice) % slice == 0)
                    {
                        if (i < sentence.Length - slice + 1)
                        {
                            char[] temp = (sentence.Substring(i, slice)).ToCharArray();
                            for (int j = 0; j < temp.Length; j++)
                            {
                                if (temp[j] == '，')
                                {
                                    chars.Add(' ');
                                    //chars.Add(' ');
                                }
                                else
                                {
                                    chars.Add(temp[j]);
                                }
                            }
                            chars.Reverse();
                            //temp = temp.Reverse().ToArray();
                            sentenceContent.Add(new String(chars.ToArray()));
                            i = i + slice - 1;
                        }
                        else
                        {
                            sentenceContent.Add(new String(sentence.Substring(i).ToCharArray().Reverse().ToArray()));
                            break;
                        }
                    }
                    i++;
                }
                if (sentenceContent.Count <= 0)
                    return false;
                int count = 0;

                //// 播放声音
                AddDisplay(boardParams.areas[2].index, sentence.Replace("2C\\","").Replace("1C\\", ""), waitSec, runMode, dispMode);
                Console.WriteLine("声音： "+ sentence.Replace("2C\\", "").Replace("1C\\", ""));
                if (sentenceContent.Count == 1)
                {
                    AddDisplay(boardParams.areas[boardIndex].index, sentenceContent[0], waitSec, runMode, dispMode);
                }
                else
                {
                    while (waitSec > count && !BroadcastBoardManager.isClosing)
                    {
                        // 显示文字
                        AddDisplay(boardParams.areas[boardIndex].index, sentenceContent[count % sentenceContent.Count], 1, runMode, dispMode);
                        count++;
                    }
                }
            }
            // 横屏
            else
            {
                AddDisplay(boardParams.areas[2].index, sentence, waitSec, runMode, dispMode);
                AddDisplay(boardParams.areas[boardIndex].index, sentence + "\\n", waitSec, runMode, dispMode);
            }
            return true;
        }
        /// <summary>
        /// 更新下方箭头显示
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public bool UpdateArrow(int index, int waitSec = 5, int runMode = -1)
        {
            if (index > 3)
                return false;
            if (boardParams.areas.Count < 2)
                return false;
            if (index < 0)
            {
                AddDisplay(boardParams.areas[1].index, " ", waitSec, runMode);
            }
            else
            {
                AddDisplay(boardParams.areas[1].index, arrows[index], waitSec, runMode);
            }
            return true;
        }
        #endregion

        /// <summary>
        /// 删除某屏幕所有动态区
        /// </summary>
        /// <param name="handle"></param>
        /// <returns></returns>
        public bool RemoveDynamicAreas()
        {
            int err = 0;
            uint handle = Led5kSDK.CreateClient(boardParams.ip, boardParams.port, boardParams.card_type, boardParams.timeout_sec, boardParams.mode, null);
            if (handle == 0)
            {
                connection = false;
                return false;
            }
            else
            {
                connection = true;
            }
            for (int i = 0; i < boardParams.areas.Count; i++)
            {
                err += Led5kSDK.SCREEN_DelDynamicArea(handle, (byte)i);
            }
            Led5kSDK.Destroy(handle);
            return err == 0 ? true : false;
        }

        // *************** 私有方法 ***************
        ///// <summary>
        ///// 增加动态区
        ///// </summary>
        ///// <param name="handle"></param>
        ///// <param name="header"></param>
        //private int AddDynamicArea(uint handle, Led5kSDK.bx_5k_area_header header)
        //{
        //    List<Led5kSDK.bx_5k_area_header> list;
        //    int result = -1;
        //    lock (BroadcastBoardManager.screen_lock)
        //    {
        //        Thread.Sleep(100);
        //        if (!handleDynamicAreasMap.ContainsKey(handle))
        //        {
        //            list = new List<Led5kSDK.bx_5k_area_header>();
        //            list.Add(header);
        //            header.DynamicAreaLoc = 0;
        //            handleDynamicAreasMap.Add(handle, list);
        //            result = 0;
        //        }
        //        else
        //        {
        //            handleDynamicAreasMap.TryGetValue(handle, out list);
        //            if (list != null)
        //            {
        //                int no = list[list.Count - 1].DynamicAreaLoc + 1;
        //                header.DynamicAreaLoc = (byte)(no);
        //                list.Add(header);
        //                result = no;
        //            }
        //        }
        //    }
        //    return result;
        //}
        /// <summary>
        /// 动态区域更新函数
        /// </summary>
        /// <param name="i"></param>
        protected bool UpdateArea(int index, byte[] areaText, int runMode = -1, int waitSec = -1, int dispMode = -1)
        {
            int err = -1;
            lock (BroadcastBoardManager.screen_lock)
            {
                if (BroadcastBoardManager.isClosing)
                    return false;
                //Thread.Sleep(100);
                if (boardParams.areas.Count() <= index)
                    return false;

                boardParams.areas[index].header.DataLen = areaText.Length;
                if (runMode >= 0)
                {
                    boardParams.areas[index].header.RunMode = (byte)runMode;
                }
                if (waitSec >= 0)
                {
                    boardParams.areas[index].header.Timeout = (short)waitSec;
                }
                if (dispMode >= 0)
                {
                    boardParams.areas[index].header.DisplayMode = (byte)dispMode;
                }
                uint handle = Led5kSDK.CreateClient(boardParams.ip, boardParams.port, boardParams.card_type, boardParams.timeout_sec, boardParams.mode, null);
                if (handle == 0)
                {
                    connection = false;
                    return false;
                }
                else
                {
                    connection = true;
                }
                try
                {
                    err = Led5kSDK.SCREEN_SendDynamicArea(handle, boardParams.areas[index].header, (ushort)areaText.Length, areaText);
                }
                catch { Console.WriteLine("UpdateArea " + index + " : " + err); }
                Led5kSDK.Destroy(handle);
            }
            if (err == 0)
                return true;
            else
                return false;

        }
        /// <summary>
        /// 动态区域更新函数
        /// </summary>
        /// <param name="i"></param>
        protected bool UpdateArea(int index, byte[] areaText, bool soundOnly, byte[] soundText, int sound, int runMode = -1, int waitSec = -1, int dispMode = -1)
        {
            int err = -1;
            sound = Math.Min(10, sound);
            sound = Math.Max(1, sound);
            lock (BroadcastBoardManager.screen_lock)
            {
                if (BroadcastBoardManager.isClosing)
                    return false;
                //Thread.Sleep(100);
                if (boardParams.areas.Count() <= index)
                    return false;

                boardParams.areas[index].header.DataLen = areaText.Length;
                if (runMode >= 0)
                {
                    boardParams.areas[index].header.RunMode = (byte)runMode;
                }
                if (waitSec >= 0)
                {
                    boardParams.areas[index].header.Timeout = (short)waitSec;
                }
                if (dispMode >= 0)
                {
                    boardParams.areas[index].header.DisplayMode = (byte)dispMode;
                }
                uint handle = Led5kSDK.CreateClient(boardParams.ip, boardParams.port, boardParams.card_type, boardParams.timeout_sec, boardParams.mode, null);
                if (handle == 0)
                {
                    connection = false;
                    return false;
                }
                else
                {
                    connection = true;
                }
                try
                {
                    if (!soundOnly)
                        err = Led5kSDK.SCREEN_SendSoundDynamicArea(handle, boardParams.areas[index].header, (ushort)areaText.Length, areaText, 2, 0, (byte)sound, 5, soundText.Length, soundText);
                    else
                        err = Led5kSDK.SCREEN_SendSoundDynamicArea(handle, boardParams.areas[index].header, 0, Encoding.Default.GetBytes(""), 2, 0, (byte)sound, 5, soundText.Length, soundText);
                }
                catch { Console.WriteLine("UpdateArea sound " + index + " : " + err); }
                Led5kSDK.Destroy(handle);
            }
            if (err == 0)
                return true;
            else
                return false;
        }
        /// <summary>
        /// 将待显示信息添加到对应区域队列排队显示
        /// </summary>
        /// <param name="index"></param>
        /// <param name="sentence"></param>
        /// <param name="duration"></param>
        /// <param name="runMode"></param>
        /// <param name="dispMode"></param>
        /// <returns></returns>
        protected bool AddDisplay(int index, string sentence, int duration, int runMode = -1, int dispMode = -1)
        {
            if (boardParams.areas.Count <= index)
                return false;
            lock (boardParams.areas[index].display_queue_lock)
            {
                if (boardParams.areas[index].displayInfos == null)
                    return false;
                // 待显示信息过多，直接抛出
                while(boardParams.areas[index].displayInfos.Count > max_queue_size)
                {
                    boardParams.areas[index].displayInfos.Dequeue();
                }
                DisplayInfo di = new DisplayInfo();
                di.display_string = sentence;
                di.display_duration = duration;
                di.runMode = runMode;
                di.dispMode = dispMode;
                boardParams.areas[index].displayInfos.Enqueue(di);
            }
            return true;
        }
        /// <summary>
        /// 根据当前区域状态更新区域显示
        /// </summary>
        /// <param name="area"></param>
        /// <param name="isSound"></param>
        /// <param name="sleep_milliseconds"></param>
        protected void UpdateDisplay(ref AreaInfo area, bool isSound, int sleep_milliseconds)
        {
            // 当前已显示完
            if (area.AreaIdle())
            {
                // 空队列则播放默认
                if (area.displayInfos.Count == 0)
                {
                    if (!area.is_default_display)
                    {
                        //Console.WriteLine("区域"+area.index+"恢复默认");
                        area.is_default_display = true;
                        if (isSound)
                        {
                            UpdateArea(area.index, Encoding.Default.GetBytes(""), true,
                                Encoding.Default.GetBytes(defaultStr[area.index]), 10,
                                0,
                                area.header.Timeout,
                                area.header.DisplayMode);
                        }
                        else
                        {
                            UpdateArea(area.index,
                                Encoding.Default.GetBytes(defaultStr[area.index]),
                                0,
                                area.header.Timeout,
                                area.header.DisplayMode);
                        }
                    }
                }
                // 队列中存在排队元素则显示
                else
                {
                    area.is_default_display = false;
                    area.currentDisplayInfo = area.displayInfos.Dequeue();
                    area.AreaResetDisplayTimeLeft(area.currentDisplayInfo.display_duration);
                    //Console.WriteLine("区域" + area.index + "显示文字："+ area.currentDisplayInfo.display_string);
                    if (isSound)
                    {
                        UpdateArea(area.index, Encoding.Default.GetBytes(""), true,
                            Encoding.Default.GetBytes(area.currentDisplayInfo.display_string), 10,
                            area.currentDisplayInfo.runMode,
                            (int)area.currentDisplayInfo.display_duration,
                            area.currentDisplayInfo.dispMode);
                    }
                    else
                    {
                        UpdateArea(area.index,
                           Encoding.Default.GetBytes(area.currentDisplayInfo.display_string),
                           area.currentDisplayInfo.runMode,
                           (int)area.currentDisplayInfo.display_duration,
                           area.currentDisplayInfo.dispMode);
                    }
                }
            }
            // 当前未显示完，但存在消息排队，超过最短显示时间后更新显示
            else if (area.displayInfos.Count > 0
                    && area.currentDisplayInfo != null
                    && area.currentDisplayInfo.display_duration - area.display_time_left > DisplayInfo.minimum_display_duration)
            {
                area.is_default_display = false;
                area.currentDisplayInfo = area.displayInfos.Dequeue();
                area.AreaResetDisplayTimeLeft(area.currentDisplayInfo.display_duration);
                //Console.WriteLine("区域" + area.index + "排队：" + area.currentDisplayInfo.display_string);
                if (isSound)
                {
                    UpdateArea(area.index, Encoding.Default.GetBytes(""), true,
                        Encoding.Default.GetBytes(area.currentDisplayInfo.display_string), 10,
                        area.currentDisplayInfo.runMode,
                        (int)area.currentDisplayInfo.display_duration,
                        area.currentDisplayInfo.dispMode);
                }
                else
                {
                    UpdateArea(area.index,
                        Encoding.Default.GetBytes(area.currentDisplayInfo.display_string),
                        area.currentDisplayInfo.runMode,
                        (int)area.currentDisplayInfo.display_duration,
                        area.currentDisplayInfo.dispMode);
                }
            }
            // 未显示完，未超过最短显示时间或不存在消息排队
            else
            {
                area.AreaDisplayTimeReduce(sleep_milliseconds / 1000.0);
            }
        }
        /// <summary>
        /// 从队列取出实例，更新显示
        /// </summary>
        /// <returns></returns>
        protected virtual void HandlingThread(int sleep_milliseconds)
        {
            //初始化时启动连接检查线程
            Task.Factory.StartNew(() =>
            {
                while (!BroadcastBoardManager.isClosing)
                {
                    Thread.Sleep(sleep_milliseconds);
                    //Task.Factory.StartNew(() =>
                    //{
                        try
                        {
                            for (int i = 0; i < boardParams.areas.Count; i++)
                            {
                                lock (boardParams.areas[i].display_queue_lock)
                                {
                                    AreaInfo area = boardParams.areas[i];
                                    UpdateDisplay(ref area, boardParams.areas[i].index == 2, sleep_milliseconds);
                                }
                            }
                        }
                        catch(Exception ex) { Console.WriteLine(">>>>>>>>>>"+ex.Message+"\n"+ex.StackTrace); }
                    //});
                }
            });
            //return connected;
        }
    }

    /// <summary>
    /// 该类定义停车终端外部LED方屏显示功能，显示车位编号、状态、suv车位量情况（实时显示内部维护，外部设置状态即可）
    /// </summary>
    public class BroadcastSquareBoard : BroadcastBoard
    {
        public enum Entrance_statu
        {
            ENTRANCE_OCCUPIED   = 1,//占用
            ENTRANCE_LEISURE    = 2,//空闲
            ENTRANCE_NOSIGNAL   = 3,//无信号
            ENTRANCE_FULL=4         //车位已满

        };
        /// <summary>
        /// 状态更新互斥锁
        /// </summary>
        private object statusLock;
        /// <summary>
        /// 状态更新flag
        /// </summary>
        private bool statusUpdated;
        /// <summary>
        /// 是否指令占用
        /// </summary>
        public Entrance_statu occupied;
        /// <summary>
        /// 是否suv车位已满
        /// </summary>
        private bool suv_full;
        // 方屏(楚天停车终端LED外屏提示车位状态)构造
        private string disp_parking_str;
        public BroadcastSquareBoard(int id, byte[] ip, uint port, Led5kSDK.bx_5k_card_type card_type, int tmout_sec, int mode):base(id,ip,port,card_type,tmout_sec,mode)
        {
            occupied = Entrance_statu.ENTRANCE_OCCUPIED;
            suv_full = false;
            statusUpdated = false;
            statusLock = new object();
        }
        /// <summary>
        /// 外部调用更新状态
        /// </summary>
        /// <param name="occupied"></param>
        /// <param name="suv_full"></param>
        public void UpdateStatus(Entrance_statu occupied, bool suv_full)
        {
            if (connection == false)
                return;
            lock (statusLock)
            {
                if(this.occupied!= occupied || this.suv_full != suv_full)
                {
                    statusUpdated = true;
                    this.occupied = occupied;
                    this.suv_full = suv_full;
                    //Console.WriteLine("square status changed...");
                }
            }
        }
        /// <summary>
        /// 初始化led屏
        /// </summary>
        /// <returns></returns>
        public override bool InitBoard()
        {
            if (boardParams == null)
                return false;

            //定义一个80*54区域,上下3像素留空
            AreaInfo area1 = new AreaInfo(boardParams.id, 0, BroadcastBoardManager.header_model);
            area1.header.AreaWidth = 10;//10=80/8
            area1.header.AreaHeight = 54;//16;//96
            area1.header.AreaX = 0;
            area1.header.AreaY = 3;
            area1.header.SingleLine = 0x02;//多行
            area1.header.NewLine = 0x01;//手动换行
            area1.header.DisplayMode = 0x01;//静止显示
            area1.header.StayTime = 2;
            area1.header.RunMode = 0;
            area1.header.Speed = 2;
            area1.header.Timeout = 20;
            area1.header.DynamicAreaLoc = (byte)0;

            boardParams.areas.Add(area1);
            HandlingThread(150);
            return true;
        }
        /// <summary>
        /// 设置方屏第三行显示信息
        /// </summary>
        /// <param name="str"></param>
        public void set_disp_str(string str)
        {
            disp_parking_str = str;
            statusUpdated = true;
        }
        protected override void HandlingThread(int sleep_milliseconds)
        {
            //初始化时启动连接检查线程
            Task.Factory.StartNew(() =>
            {
                while (!BroadcastBoardManager.isClosing)
                {
                    Thread.Sleep(sleep_milliseconds);
                    //Task.Factory.StartNew(() =>
                    //{
                    try
                    {
                        if(boardParams.areas.Count()<1 || boardParams.areas[0] == null)
                        {
                            Console.WriteLine("!!!!!square led param error");
                            continue;
                        }
                        else// 方屏仅一个区域,且仅有本线程更新,不用加显示锁
                        {
                            if(statusUpdated)
                            {
                                lock (statusLock)
                                {
                                    string str = "";
                                    if (occupied == Entrance_statu.ENTRANCE_OCCUPIED)
                                    {
                                        str = "\\C1 内有车  X";
                                    }
                                    else if (occupied == Entrance_statu.ENTRANCE_LEISURE)
                                    {
                                        str = "\\C2 空闲  <-";
                                    }
                                    else if (occupied == Entrance_statu.ENTRANCE_NOSIGNAL)
                                    {
                                        str = "\\C1 无信号  X";
                                    }
                                    else if(occupied == Entrance_statu.ENTRANCE_FULL)
                                    {
                                        str = "\\C1 车位已满 ";
                                    }
                                    statusUpdated = false;
                                    string disp_str = "\\C7 停车入口"+ (boardParams.id+1).ToString() +
                                                    "\\n" + str +
                                                    "\\n\\C3" + disp_parking_str;
                                    bool res = UpdateArea(boardParams.areas[0].index, Encoding.Default.GetBytes(disp_str));
                                    //Console.WriteLine("square area updated.."+res);
                                }
                            } 
                        }
                    }
                    catch (Exception ex) { Console.WriteLine(">>>>>>>>>>" + ex.Message + "\n" + ex.StackTrace); }
                    //});
                }
            });
        }
    }

    /// <summary>
    /// 该类定义停车终端外部LED长屏显示功能，显示剩余车位、欢迎信息等（实时显示内部维护，外部设置状态即可）
    /// </summary>
    public class BroadcastLongBoard : BroadcastBoard
    {
        /// <summary>
        /// 状态更新互斥锁
        /// </summary>
        private object statusLock;
        /// <summary>
        /// 状态更新flag
        /// </summary>
        private bool statusUpdated;
        /// <summary>
        /// 剩余车位数量
        /// </summary>
        private int parkspaceLeft;
        /// <summary>
        /// 可预约车位数量
        /// </summary>
        private int parkspaceReservable;
        // 长屏构造
        public BroadcastLongBoard(int id, byte[] ip, uint port, Led5kSDK.bx_5k_card_type card_type, int tmout_sec, int mode) : base(id, ip, port, card_type, tmout_sec, mode)
        {
            parkspaceLeft = 0;
            parkspaceReservable = 0;
            statusUpdated = false;
            statusLock = new object();
        }
        /// <summary>
        /// 外部调用更新状态
        /// </summary>
        /// <param name="occupied"></param>
        /// <param name="suv_full"></param>
        public void UpdateStatus(int parkspaceLeft, int parkspaceReservable)
        {
            lock (statusLock)
            {
                if (this.parkspaceLeft != parkspaceLeft || this.parkspaceReservable != parkspaceReservable)
                {
                    statusUpdated = true;
                    this.parkspaceLeft = parkspaceLeft;
                    this.parkspaceReservable = parkspaceReservable;
                    Console.WriteLine("long status changed...");
                }
            }
        }
        /// <summary>
        /// 初始化led屏
        /// </summary>
        /// <returns></returns>
        public override bool InitBoard()
        {
            if (boardParams == null)
                return false;

            //定义一个400*20区域,上下2像素留空
            AreaInfo area1 = new AreaInfo(boardParams.id, 0, BroadcastBoardManager.header_model);
            area1.header.AreaWidth = 50;//50=400/8
            area1.header.AreaHeight = 16;
            area1.header.AreaX = 0;
            area1.header.AreaY = 2;
            area1.header.SingleLine = 0x01;//单行
            area1.header.NewLine = 0x01;//手动换行
            area1.header.DisplayMode = 0x01;//静止显示
            area1.header.StayTime = 2;
            area1.header.RunMode = 1;
            area1.header.Speed = 2;
            area1.header.Timeout = 20;
            area1.header.DynamicAreaLoc = (byte)0;

            boardParams.areas?.Add(area1);
            HandlingThread(150);
            return true;
        }
        protected override void HandlingThread(int sleep_milliseconds)
        {
            //初始化时启动连接检查线程
            Task.Factory.StartNew(() =>
            {
                while (!BroadcastBoardManager.isClosing)
                {
                    Thread.Sleep(sleep_milliseconds);
                    //Task.Factory.StartNew(() =>
                    //{
                    try
                    {
                        if (boardParams.areas.Count() < 1 || boardParams.areas[0] == null)
                        {
                            Console.WriteLine("!!!!!long led param error");
                            continue;
                        }
                        else// 长屏仅一个区域,且仅有本线程更新,不用加显示锁
                        {
                            if (statusUpdated)
                            {
                                lock (statusLock)
                                {
                                    statusUpdated = false;
                                    string disp_str = "\\C1 剩余车位  \\C2" + parkspaceLeft +
                                                    "   \\C3欢迎光临智象停车   \\C1可预约车位 \\C2" + parkspaceReservable;
                                    bool res = UpdateArea(boardParams.areas[0].index, Encoding.Default.GetBytes(disp_str));
                                    Console.WriteLine("long area updated.." + res);
                                }
                            }
                        }
                    }
                    catch (Exception ex) { Console.WriteLine(">>>>>>>>>>" + ex.Message + "\n" + ex.StackTrace); }
                    //});
                }
            });
        }
    }

    /// <summary>
    /// 该类定义停车终端外部LED取车屏显示功能，共三行循环显示，一行显示一辆车取车情况，包括车牌号，将会取到几号口，状态以及预计完成时间（实时显示内部维护，外部设置状态即可）
    /// </summary>
    public class BroadcastFetchBoard : BroadcastBoard
    {
        /// <summary>
        /// 状态更新互斥锁
        /// </summary>
        private object statusLock;
        /// <summary>
        /// 状态更新flag
        /// </summary>
        private bool statusUpdated;
        /// <summary>
        /// 待取车辆信息列表
        /// </summary>
        private List<FetchInfo> fetchInfoList;
        // 取车屏构造
        public BroadcastFetchBoard(int id, byte[] ip, uint port, Led5kSDK.bx_5k_card_type card_type, int tmout_sec, int mode) : base(id, ip, port, card_type, tmout_sec, mode)
        {
            fetchInfoList = new List<FetchInfo>();
            statusUpdated = false;
            statusLock = new object();
        }
        /// <summary>
        /// 更新取车信息，不存在则创建
        /// </summary>
        /// <param name="numberPlate"></param>
        /// <param name="targetTerminal"></param>
        /// <param name="completed"></param>
        /// <param name="expectedSeconds"></param>
        public void UpdateFetchInfo(string numberPlate, int targetTerminal, bool completed, int expectedSeconds)
        {
            lock (statusLock)
            {
                for (int i = 0; i < fetchInfoList.Count(); i++)
                {
                    // 找到相同号牌的记录则更新信息并退出
                    if(fetchInfoList[i].GetNumberPlate() == numberPlate)
                    {
                        fetchInfoList[i].SetInfo(numberPlate, targetTerminal, completed, expectedSeconds);
                        return;
                    }
                }
                fetchInfoList.Add(new FetchInfo(numberPlate, targetTerminal, completed, expectedSeconds));
            }
        }
        /// <summary>
        /// 初始化led屏
        /// </summary>
        /// <returns></returns>
        public override bool InitBoard()
        {
            if (boardParams == null)
                return false;

            //定义一个400*52区域,上下2像素留空
            AreaInfo area1 = new AreaInfo(boardParams.id, 0, BroadcastBoardManager.header_model);
            area1.header.AreaWidth = 50;//50=400/8
            area1.header.AreaHeight = 52;
            area1.header.AreaX = 0;
            area1.header.AreaY = 2;
            area1.header.SingleLine = 0x02;//多行
            area1.header.NewLine = 0x01;//手动换行
            area1.header.DisplayMode = 0x01;//静止显示
            area1.header.StayTime = 2;
            area1.header.RunMode = 1;
            area1.header.Speed = 2;
            area1.header.Timeout = 20;
            area1.header.DynamicAreaLoc = (byte)0;
            area1.header.Lines_sizes = 4;

            boardParams.areas?.Add(area1);
            HandlingThread(150);
            return true;
        }
        protected override void HandlingThread(int sleep_milliseconds)
        {
            //初始化时启动连接检查线程
            Task.Factory.StartNew(() =>
            {
                while (!BroadcastBoardManager.isClosing)
                {
                    Thread.Sleep(sleep_milliseconds);
                    //Task.Factory.StartNew(() =>
                    //{
                    try
                    {
                        if (boardParams.areas.Count() < 1 || boardParams.areas[0] == null)
                        {
                            Console.WriteLine("!!!!!fetch led param error");
                            continue;
                        }
                        else// 长屏仅一个区域,且仅有本线程更新,不用加显示锁
                        {
                            if (statusUpdated)
                            {
                                lock (statusLock)
                                {
                                    statusUpdated = false;
                                    string disp_str = "";
                                    for (int i = 0; i < fetchInfoList.Count(); i++)
                                    {
                                        disp_str += fetchInfoList[i].ToString() + "\\n";
                                    }
                                    bool res = UpdateArea(boardParams.areas[0].index, Encoding.Default.GetBytes(disp_str));
                                    Console.WriteLine("long area updated.." + res);
                                }
                            }
                            for (int i = 0; i < fetchInfoList.Count(); i++)
                            {
                                if (fetchInfoList[i].IsCompleted())
                                {
                                    fetchInfoList.Remove(fetchInfoList[i]);
                                }
                            }
                        }
                    }
                    catch (Exception ex) { Console.WriteLine(">>>>>>>>>>" + ex.Message + "\n" + ex.StackTrace); }
                    //});
                }
            });
        }
    }

    /// <summary>
    /// LED屏整体参数类，包含划分的动态区域列表
    /// </summary>
    public class BoardParams
    {
        /// <summary>
        /// LED屏编号
        /// </summary>
        public int id;
        /// <summary>
        /// LED屏ip地址
        /// </summary>
        public byte[] ip;
        /// <summary>
        /// LED屏端口号
        /// </summary>
        public uint port;
        /// <summary>
        /// LED屏控制卡类型
        /// </summary>
        public Led5kSDK.bx_5k_card_type card_type;
        /// <summary>
        /// 连接超时时间
        /// </summary>
        public int timeout_sec;
        /// <summary>
        /// 连接模式，通常为0
        /// </summary>
        public int mode;
        /// <summary>
        /// LED屏区域参数
        /// </summary>
        public List<AreaInfo> areas;

        public BoardParams(int id, byte[] ip, uint port, Led5kSDK.bx_5k_card_type card_type, int timeout_sec, int mode)
        {
            this.id = id;
            this.ip = ip;
            this.port = port;
            this.card_type = card_type;
            this.timeout_sec = timeout_sec;
            this.mode = mode;
            areas = new List<AreaInfo>();
        }
    }

    /// <summary>
    /// LED屏中动态区域信息类
    /// 动态区域在sdk定义中允许重叠
    /// </summary>
    public class AreaInfo
    {
        /// <summary>
        /// 队列互斥锁
        /// </summary>
        public object display_queue_lock;
        /// <summary>
        /// 待显示消息排队
        /// </summary>
        public Queue<DisplayInfo> displayInfos;
        /// <summary>
        /// 当前显示信息
        /// </summary>
        public DisplayInfo currentDisplayInfo;

        /// <summary>
        /// LED屏编号
        /// </summary>
        public int board_id;
        /// <summary>
        /// 区域编号
        /// </summary>
        public int index;
        /// <summary>
        /// 是否处于默认显示
        /// </summary>
        public bool is_default_display;
        /// <summary>
        /// 该区域显示参数
        /// </summary>
        public Led5kSDK.bx_5k_area_header header;
        /// <summary>
        /// 剩余显示时间
        /// </summary>
        public double display_time_left;
        /// <summary>
        /// 返回当前区域显示状态是否空闲
        /// </summary>
        /// <returns></returns>
        public bool AreaIdle()
        {
            return display_time_left <= 0;
        }
        /// <summary>
        /// 更新剩余显示时间
        /// </summary>
        /// <returns></returns>
        public bool AreaDisplayTimeReduce(double sec)
        {
            display_time_left -= sec;
            return AreaIdle();
        }
        /// <summary>
        /// 重置显示剩余时间
        /// </summary>
        /// <param name="sec"></param>
        public void AreaResetDisplayTimeLeft(double sec)
        {
            display_time_left = sec;
        }

        public AreaInfo(int board_id, int index, Led5kSDK.bx_5k_area_header header)
        {
            this.board_id = board_id;
            this.index = index;
            this.header = header;
            display_queue_lock = new object();
            displayInfos = new Queue<DisplayInfo>();
            currentDisplayInfo = null;
        }

        public override string ToString()
        {
            return "board_id: " + board_id + ", area index: " + index + ", is default display? " + is_default_display + ", display time left: " + display_time_left;
        }
    }

    /// <summary>
    /// 字符串显示信息类
    /// </summary>
    public class DisplayInfo
    {
        /// <summary>
        /// 最短显示时长
        /// </summary>
        public static double minimum_display_duration = 0.0;
        /// <summary>
        /// 显示字符串
        /// </summary>
        public string display_string;
        /// <summary>
        /// 本次将显示时长
        /// </summary>
        public double display_duration;
        /// <summary>
        /// 循环方式
        /// </summary>
        public int runMode;
        /// <summary>
        /// 移动方式
        /// </summary>
        public int dispMode;

        public DisplayInfo()
        {
            display_string = "";
            display_duration = 0;
            runMode = 0;
            dispMode = 0x02;
        }
    }

    /// <summary>
    /// 取车信息类
    /// </summary>
    public class FetchInfo
    {
        /// <summary>
        /// 待取车号牌
        /// </summary>
        private string numberPlate;
        /// <summary>
        /// 取车目标终端位
        /// </summary>
        private int targetTerminal;
        /// <summary>
        /// 取车完成状态
        /// </summary>
        private bool completed;
        /// <summary>
        /// 预计耗时/秒
        /// </summary>
        private int expectedSeconds;

        public FetchInfo()
        {
            numberPlate = "";
            targetTerminal = -1;
            completed = false;
            expectedSeconds = -1;
        }

        public string GetNumberPlate()
        {
            return numberPlate;
        }

        public bool IsCompleted()
        {
            return completed;
        }

        public FetchInfo(string numberPlate, int targetTerminal, bool completed, int expectedSeconds)
        {
            SetInfo(numberPlate, targetTerminal, completed, expectedSeconds);
        }

        public void SetInfo(string numberPlate, int targetTerminal, bool completed, int expectedSeconds)
        {
            this.numberPlate = numberPlate;
            this.targetTerminal = targetTerminal;
            this.completed = completed;
            this.expectedSeconds = expectedSeconds;
        }

        public override int GetHashCode()
        {
            return numberPlate.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            if (obj == null)
                return false;
            if (this.GetType() != obj.GetType())
                return false;
            if (this == obj)
                return true;
            FetchInfo fetchInfo = (FetchInfo)obj;
            if (fetchInfo.numberPlate == numberPlate && fetchInfo.targetTerminal == targetTerminal && fetchInfo.completed == completed && fetchInfo.expectedSeconds == expectedSeconds)
                return true;
            else
                return false;
        }

        public override string ToString()
        {
            return " \\C3"+numberPlate+" \\C1将取到\\C2"+targetTerminal+"\\C1号口  "+(completed?"\\C2完成":"\\C1取车中")+" \\C4预计耗时 \\C2"+expectedSeconds/60+"\\C4分钟";
        }
    }
}
