using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.IO.Compression;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Brush = System.Drawing.Brush;
using Color = System.Drawing.Color;
using Pen = System.Drawing.Pen;
using PixelFormat = System.Drawing.Imaging.PixelFormat;
using Rectangle = System.Drawing.Rectangle;

namespace Air105Camera
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    [PropertyChanged.AddINotifyPropertyChangedInterface]
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        //将Bitmap对象转换成bitmapImage对象
        private BitmapImage ConvertBitmapToBitmapImage(Bitmap bitmap)
        {
            MemoryStream stream = new MemoryStream();
            bitmap.Save(stream, ImageFormat.Bmp);
            BitmapImage image = new BitmapImage();
            image.BeginInit();
            image.StreamSource = stream;
            image.EndInit();
            return image;
        }

        //刷新显示当前图片
        private void ShowImage()
        {
            this.Dispatcher.Invoke(() =>
            {
                var temp = ConvertBitmapToBitmapImage(image);
                CameraImage.Source = temp;
            });
        }

        //缓存图片数据
        Bitmap image = new Bitmap(1, 1, PixelFormat.Format16bppRgb565);
        //串口对象
        private SerialPort Uart = new SerialPort();
        //总共收到多少包
        public int totalPack { get; set; } = 0;
        //总共刷了多少张
        public int totalPic { get; set; } = 0;
        //FPS数
        public double fpsNow { get; set; } = 0;
        public bool connectEnable { get; set; } = true;
        public bool disconnectEnable { get; set; } = true;

        //当前宽高，给ui显示用
        public int imageWidth { get; set; } = 0;
        public int imageHeight { get; set; } = 0;


        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            this.DataContext = this;

            //图片显示一下
            ShowImage();

            // 绑定事件监听,用于监听HID设备插拔
            (PresentationSource.FromVisual(this) as HwndSource)?.AddHook(WndProc);
            RefreshPortList();

            //串口数据处理，随便写写
            new Thread(Uart2Image).Start();
        }


        private void ConnectButton_Click(object sender, RoutedEventArgs e)
        {
            if (Uart.IsOpen)
            {
                return;
            }
            if (((string)SerialComboBox.SelectedItem).Length > 0)
            {
                var portName = (string)SerialComboBox.SelectedItem;
                Task.Run(() =>
                {
                    try
                    {
                        Uart.PortName = portName;
                        Uart.Open();
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message);
                    }
                });
            }
        }

        private void DisconnectButton_Click(object sender, RoutedEventArgs e)
        {
            if (!Uart.IsOpen)
            {
                return;
            }
            try
            {
                Uart.Close();
            }
            catch(Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private static int UsbPluginDeley = 0;
        private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            if (msg == 0x219)// 监听USB设备插拔消息
            {
                if (UsbPluginDeley == 0)
                {
                    ++UsbPluginDeley;   // Task启动需要准备时间,这里提前对公共变量加一
                    Task.Run(() =>
                    {
                        do Task.Delay(100).Wait();
                        while (++UsbPluginDeley < 10);
                        UsbPluginDeley = 0;
                        RefreshPortList();
                    });
                }
                else UsbPluginDeley = 1;
                handled = true;
            }
            return IntPtr.Zero;
        }

        /// <summary>
        /// 刷新设备列表，并在断开恢复后重连
        /// </summary>
        /// <returns>串口列表是否成功获取</returns>
        private bool RefreshPortList()
        {
            bool result = true;
            var list = SerialPort.GetPortNames();
            Dispatcher.Invoke(delegate
            {
                SerialComboBox.ItemsSource = list;
                if (SerialComboBox.Items.Count == 0)
                {
                    result = false;
                }
                else if (SerialComboBox.Items.Contains(Uart.PortName))
                {
                    SerialComboBox.SelectedItem = Uart.PortName;
                }
                else SerialComboBox.SelectedIndex = 0;
            });
            return result;
        }

        bool closed = false;
        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            closed = true;
            //直接强关软件
            Environment.Exit(0);
        }

        private void RefreshButton_Click(object sender, RoutedEventArgs e)
        {
            RefreshPortList();
        }

        /*
        16字节头+N字节数据
        4字节字符串 “VCAM” + 2字节图像宽度 + 2字节图像高度 + 4字节本次数据从哪行开始刷 + 2字节未压缩数据长度 + 2字节压缩后图像数据长度
        如果压缩前和压缩后数据长度是一致的，那就没有压缩；如果不一致，则后面的N字节要用zlib解压
        RGB565大端格式
        */
        //串口数据处理
        private void Uart2Image()
        {
            byte[] temp = new byte[10 * 1024 * 1024];//缓冲区
            int p = 0;//上次的位置
            Stopwatch sw = new Stopwatch();
            sw.Start();
            bool getNewData = false;//记一下是不是收到新数据了
            while (true)
            {
                if (closed)
                    return;
                connectEnable = !Uart.IsOpen;
                disconnectEnable = Uart.IsOpen;
                try
                {
                    if (Uart.IsOpen && Uart.BytesToRead > 0)
                    {
                        //读数据
                        var toread = Uart.BytesToRead;
                        Uart.Read(temp, p, toread);
                        p += toread;
                        getNewData = true;
                    }
                    else if (!Uart.IsOpen)
                    {
                        fpsNow = 0;
                        p = 0;
                    }
                    if(getNewData)
                    {
                        getNewData = false;//收到新数据再刷
                        for (int i = 3; i < p; i++)
                        {
                            //匹配VCAM头
                            if (temp[i - 3] == 'V' && temp[i - 2] == 'C' && temp[i - 1] == 'A' && temp[i] == 'M'
                                && p - i > 16)
                            {
                                var width = BitConverter.ToUInt16(temp, i + 1);
                                var height = BitConverter.ToUInt16(temp, i + 3);
                                var start = BitConverter.ToUInt32(temp, i + 5);
                                var rawLen = BitConverter.ToUInt16(temp, i + 9);
                                var comLen = BitConverter.ToUInt16(temp, i + 11);
                                Debug.WriteLine($"found VCAM: {BitConverter.ToString(temp, i - 3, 16)}\r\n" +
                                    $"{width},{height},{start},{rawLen},{comLen},{i},{p}");
                                //获取实际数据长度
                                var realLength = comLen < rawLen ? comLen : rawLen;
                                if (realLength <= p - i - 13)//一包数据够了
                                {
                                    totalPack++;
                                    Debug.WriteLine($"{realLength} bytes, start unpack");

                                    //存储rgb数据
                                    byte[] data = null;
                                    if (comLen < rawLen)//包压缩了
                                    {
                                        //Debug.WriteLine(BitConverter.ToString(temp, i + 13, comLen));
                                        using MemoryStream compressed = new MemoryStream(temp, i + 13, comLen);
                                        using MemoryStream decompressed = new MemoryStream();
                                        using InflaterInputStream inputStream = new InflaterInputStream(compressed);
                                        inputStream.CopyTo(decompressed);
                                        data = decompressed.ToArray();
                                    }
                                    else//没压缩
                                    {
                                        Debug.WriteLine($"rawLen,{rawLen},{i + 13 + rawLen}");
                                        data = new byte[rawLen];
                                        for (int n = i + 13; n < i + 13 + rawLen; n++)
                                            data[n - (i + 13)] = temp[n];
                                    }

                                    //自适应当前图片大小
                                    if(image.Width != width || image.Height != height)
                                    {
                                        image = new Bitmap(width, height, PixelFormat.Format16bppRgb565);
                                        (imageWidth, imageHeight) = (width, height);
                                    }

                                    //直接把rgb565数据写入bitmap，这样快
                                    //先把数据改成小端
                                    for (int n = 0; n < data.Length; n += 2)
                                    {
                                        (data[n], data[n + 1]) = (data[n + 1], data[n]);
                                    }
                                    var rect = new Rectangle(0, (int)start, width, data.Length / 2 / width);
                                    BitmapData bmpData = image.LockBits(rect, ImageLockMode.ReadWrite, image.PixelFormat);
                                    IntPtr ptr = bmpData.Scan0;// Get the address of the first line.
                                    System.Runtime.InteropServices.Marshal.Copy(data, 0, ptr, data.Length);
                                    image.UnlockBits(bmpData);

                                    //刷到最后一行时再显示当前图片
                                    if (start >= height - 16)
                                    {
                                        sw.Stop();
                                        fpsNow = 1000.0 / sw.ElapsedMilliseconds;
                                        sw.Restart();
                                        totalPic++;
                                        ShowImage();
                                    }

                                    //剩下的数据全部左移到开头
                                    for (int n = i + 13 + realLength; n < p; n++)
                                        temp[n - (i + 13 + realLength)] = temp[n];
                                    p -= i + 13 + realLength;
                                    Debug.WriteLine($"done, p now {p}");
                                    if (p > 3)
                                        i = 2;
                                    else
                                        break;
                                }
                            }
                        }
                    }
                    Thread.Sleep(1);
                }
                catch (Exception e)
                {
                    Debug.WriteLine(e.Message);
                }
            }
        }
    }
}
