using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using cn.edu.suda.sumcu.iot.util;

namespace RemoteUpdate
{
    public partial class frm_remoteUpdate : Form
    {
        //定义使用的全局变量
        private frm_main fmain;  //主页面变量

        private Hex hex1;  //Hex文件信息，整体更新用
        private Hex hex2;  //Hex文件信息，增量更新用
        private Hex hex3;  //Hex文件信息，增量更新用
        HCICom com;             //HCICom对象
        private Dictionary<string, Update> dicUpdate;  //<IMSI,Update>键值对，每个终端对应一个Update变量
        private string showimsi;  //单独显示更新进度的终端imsi号



        //======================================================================
        //函数名称：frm_remoteUpdate
        //函数返回：frm_remoteUpdate窗体构造函数
        //参数说明：无
        //功能概要：完成frm_remoteUpdate窗体的初始化工作
        //======================================================================
        public frm_remoteUpdate()
        {
            InitializeComponent();
        }

        //======================================================================
        //函数名称：frm_remoteUpdate_Load
        //函数返回：无
        //参数说明：无
        //功能概要：窗体加载事件，窗体加载时自动调用
        //======================================================================
        private void frm_remoteUpdate_Load(object sender, EventArgs e)
        {
            Control.CheckForIllegalCrossThreadCalls = false;
            //（1）本窗体由frmMain创建，所以本窗体的父窗体为frmMain
            fmain = (frm_main)this.MdiParent;  //得到frmMain窗体变量
            //（2）初始化Hex变量
            hex1 = new Hex();
            hex2 = new Hex();
            hex3 = new Hex();
            //（3）初始化HCICOM
            com = new HCICom();
            com.HCIComTarget = "local:21522";  //设置侦听的IP地址和端口号
            if (com.Init(null) == 0)
            {
                fmain.lbl_mainstatus.Text = "运行状态：侦听本机端口21522成功";
                txtShowAll("侦听本机端口21522成功\r\n");
                //注册接收数据事件处理程序
                com.DataReceivedEvent += new HCICom.DataReceived(IoT_recv);
            }
            //（4）初始化Update哈希表
            dicUpdate = new Dictionary<string, Update>();
            //（5）默认不单独显示更新进度的终端imsi号
            showimsi = "";

        }

        //======================================================================
        //函数名称：tab_updatestyle_SelectedIndexChanged
        //函数返回：无
        //参数说明：无
        //功能概要：切换更新方式时判断可否进行更新操作
        //======================================================================
        private void tab_updatestyle_SelectedIndexChanged(object sender, EventArgs e)
        {
            //允许、禁止更新操作判断
            enableUpdateJudge();
        }

        //======================================================================
        //函数名称：btn_fileOpen1_Click
        //函数返回：无
        //参数说明：无
        //功能概要：导入待整体更新Hex文件并对该文件进行解析取出其有效数据
        //======================================================================
        private void btn_fileopen1_Click(object sender, EventArgs e)
        {
            //（1）变量声明
            int flag;
            string filePath;       //文件路径
            string fileName;

            //（2）导入Hex文件
            OpenFileDialog ofd = new OpenFileDialog();  //打开文件对话框
            ofd.Filter = "hex file(*.hex)|*.hex";
            ofd.ShowDialog();
            //（3）获取文件名
            filePath = ofd.FileName;
            //（4）导入解析Hex文件
            flag = hex1.loadFile(filePath);
            if (flag != 0)
            {
                fmain.lbl_mainstatus.Text = "运行状态：请重新选择文件";
                MessageBox.Show("Hex文件异常!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                return;
            }
            //（5）状态提示
            fileName = hex1.getFileName();

            this.lbl_filename1.Text = fileName;
            txtShowAll("整体更新，导入文件" + fileName + "成功！\r\n");

            fmain.lbl_mainstatus.Text = "运行状态：" + fileName + "文件选择成功，文件数据解析成功";
            //（6）允许、禁止更新操作判断
            enableUpdateJudge();
        }

        //======================================================================
        //函数名称：btn_fileopen2_Click
        //函数返回：无
        //参数说明：无
        //功能概要：导入待增量更新Hex文件（新）并对该文件进行解析取出其有效数据
        //======================================================================
        private void btn_fileopen2_Click(object sender, EventArgs e)
        {
            //（1）变量声明
            int flag;
            string filePath;       //文件路径
            string fileName;

            //（2）导入Hex文件
            OpenFileDialog ofd = new OpenFileDialog();  //打开文件对话框
            ofd.Filter = "hex file(*.hex)|*.hex";
            ofd.ShowDialog();
            //（3）获取文件名
            filePath = ofd.FileName;
            //（4）导入解析Hex文件
            flag = hex2.loadFile(filePath);
            if (flag != 0)
            {
                fmain.lbl_mainstatus.Text = "运行状态：请重新选择文件";
                MessageBox.Show("Hex文件异常!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                return;
            }
            //（5）状态提示
            fileName = hex2.getFileName();

            this.lbl_filename2.Text = fileName;
            txtShowAll("整体更新，导入文件" + fileName + "成功！\r\n");

            fmain.lbl_mainstatus.Text = "运行状态：" + fileName + "文件选择成功，文件数据解析成功";
            //（6）允许、禁止更新操作判断
            enableUpdateJudge();
        }

        //======================================================================
        //函数名称：btn_fileopen3_Click
        //函数返回：无
        //参数说明：无
        //功能概要：导入待增量更新Hex文件（旧）并对该文件进行解析取出其有效数据
        //======================================================================
        private void btn_fileopen3_Click(object sender, EventArgs e)
        {
            //（1）变量声明
            int flag;
            string filePath;       //文件路径
            string fileName;

            //（2）导入Hex文件
            OpenFileDialog ofd = new OpenFileDialog();  //打开文件对话框
            ofd.Filter = "hex file(*.hex)|*.hex";
            ofd.ShowDialog();
            //（3）获取文件名
            filePath = ofd.FileName;
            //（4）导入解析Hex文件
            flag = hex3.loadFile(filePath);
            if (flag != 0)
            {
                fmain.lbl_mainstatus.Text = "运行状态：请重新选择文件";
                MessageBox.Show("Hex文件异常!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                return;
            }
            //（5）状态提示
            fileName = hex3.getFileName();

            this.lbl_filename3.Text = fileName;
            txtShowAll("整体更新，导入文件" + fileName + "成功！\r\n");

            fmain.lbl_mainstatus.Text = "运行状态：" + fileName + "文件选择成功，文件数据解析成功";
            //（6）允许、禁止更新操作判断
            enableUpdateJudge();
        }



        //======================================================================
        //函数名称：btn_autoupdate_Click
        //函数返回：无
        //参数说明：无
        //功能概要：“一键自动更新”（整体更新/增量更新）按钮点击事件，
        //          进行程序更新操作
        //======================================================================
        private void btn_autoupdate_Click(object sender, EventArgs e)
        {
            //（1）变量声明
            int i;
            string imsi;
            Update update = null;

            //（2）查看终端设备复选框中勾选的终端
            for (i = 0; i < this.ckl_imsi.Items.Count; i++)
            {
                if (this.ckl_imsi.GetItemChecked(i))
                {
                    imsi = this.ckl_imsi.Items[i].ToString();  //当前勾选的IMSI号
                    txtShowAll("运行状态：正在更新终端设备（IMSI）：" + imsi + "\r\n");
                    //初始化更新
                    if (this.tab_updatestyle.SelectedIndex == 0)
                    {
                        update = new Update(0,hex1.getHexList());
                    }
                    else if (this.tab_updatestyle.SelectedIndex == 1)
                    {
                        update = new Update(0,hex2.getHexList(), hex3.getHexList());
                    }
                    //将update变量加入哈希表dicUpdate
                    if (dicUpdate.ContainsKey(imsi))  //若哈希表已有变量，先删除再重新添加
                    {
                        dicUpdate.Remove(imsi);
                        dicUpdate.Add(imsi, update);
                    }
                    else
                        dicUpdate.Add(imsi, update);
                }
            }
        }

        //======================================================================
        //函数名称：ckl_imsi_SelectedIndexChanged
        //函数返回：无
        //参数说明：无
        //功能概要：切换显示不同终端的更新状态
        //======================================================================
        private void ckl_imsi_SelectedIndexChanged(object sender, EventArgs e)
        {
            //（1）允许、禁止更新操作判断
            enableUpdateJudge();

            //（2）
            curUEChange(this.ckl_imsi.SelectedItem.ToString());
        }

        //======================================================================
        //函数名称：chk_chooseall_CheckedChanged
        //函数返回：无
        //参数说明：无
        //功能概要：选中全部终端，或全部不选中
        //======================================================================
        private void chk_chooseall_CheckedChanged(object sender, EventArgs e)
        {
            //（1）变量声明
            int i;
            int count;

            //（2）获取当前勾选的终端个数
            count = 0;
            for (i = 0; i < this.ckl_imsi.Items.Count; i++)
                if (this.ckl_imsi.GetItemChecked(i)) count++;
            //（3）判断是全部勾选或取消勾选
            if (count == this.ckl_imsi.Items.Count)
            {
                //所有终端已勾选，则取消勾选
                for (i = 0; i < this.ckl_imsi.Items.Count; i++)
                    this.ckl_imsi.SetItemChecked(i, false);
            }
            else
            {
                //尚有终端未勾选，则全部勾选
                for (i = 0; i < this.ckl_imsi.Items.Count; i++)
                    this.ckl_imsi.SetItemChecked(i, true);
            }
            //（4）允许、禁止更新操作判断
            enableUpdateJudge();
        }

        //======================================================================
        //函数名称：IoT_recv
        //函数返回：无
        //参数说明：无
        //功能概要：HCICOM接收处理程序，负责接收终端心跳包或更新返回数据
        //======================================================================
        private void IoT_recv()
        {
            string imsiRecv = null;         //用于存储接收帧中的IMSI号
            byte[] frameData = null;        //用于存储接收帧中的有效数据
            byte[] senddata;
            int cur;
            int sum;
            Update ueupdate;
            try
            {
                //（1）判断是否有数据可取，若无数据，退出；若有读取到imsiRecv、frameData中
                if (com.Read(ref imsiRecv, ref frameData) == false) goto IoT_recv_EXIT;
                txtShowAll(System.Text.Encoding.Default.GetString(frameData) + "\r\n");
                //（2）向“终端复选列表框”添加新的IMSI号
                if (!this.ckl_imsi.Items.Contains(imsiRecv))
                {
                    this.ckl_imsi.Items.Add(imsiRecv);
                    dicUpdate.Add(imsiRecv, new Update());
                }
                if (dicUpdate.ContainsKey(imsiRecv))
                {
                    ueupdate = dicUpdate[imsiRecv];
                    //（3）判断是否接收到心跳包heartbeat
                    if (System.Text.Encoding.Default.GetString(frameData) == "heartbeat")  //接收到心跳包
                    {
                        if ((ueupdate.getFrameNum() == 0) || (ueupdate.getFrameNum() == ueupdate.getNextIndex() + 1))  //该终端无需更新或已更新结束
                        {
                            com.Send(imsiRecv, System.Text.Encoding.Default.GetBytes("heartbeat"));  //回发更新心跳包
                            goto IoT_recv_EXIT;
                        }
                        else //终端处于更新状态
                        {
                            goto IoT_recv_BACK;
                        }
                    }
                    txtShowAll("收到更新数据\r\n");
                    //（4）接收更新返回
                    if (ueupdate.updateRecv(frameData) != 0) goto IoT_recv_EXIT;  //未接收到正确更新返回信息
                    //（5）返回终端更新数据
                IoT_recv_BACK:
                    senddata = ueupdate.getNextFrame();
                    sum = ueupdate.getFrameNum();
                    cur = ueupdate.getNextIndex();
                    System.Threading.Thread.Sleep(100); //延时100毫秒

                    com.Send(imsiRecv, senddata);  //回发更新数据

                    dicUpdate[imsiRecv] = ueupdate;
                    //状态提示
                    //当前帧数据操作成功
                    this.prg_update.Value = (cur + 1) * 100 / sum;            //进度条显示
                    this.prg_update.Refresh();
                    this.lbl_progressbar.Text = "更新进度："+(cur + 1) * 100 / sum + "%";

                    curUEShow(imsiRecv, cur, sum);
                    //成功提示
                    txtShowAll(imsiRecv + "：当前第" + (cur + 1).ToString() + "/" + sum.ToString() + "帧 \r\n");
                }

            }
            catch (Exception ex)
            {
 
            }
        IoT_recv_EXIT:
            return;
        }

        //======================================================================
        //函数名称：txtShowAll
        //函数返回：无
        //参数说明：str:在终端执行信息提示框追加显示的内容
        //功能概要：增量更新过程中，显示所有终端执行状态     
        //======================================================================
        private void txtShowAll(string str)
        {
            this.txt_updateinfo.Text += str;
            this.txt_updateinfo.Refresh();
            this.txt_updateinfo.SelectionStart = this.txt_updateinfo.Text.Length;  //光标指向最后一位
            this.txt_updateinfo.ScrollToCaret();   //移动到光标处
        }

        //======================================================================
        //函数名称：txtShowPart
        //函数返回：无
        //参数说明：str:在终端执行信息提示框追加显示的内容
        //功能概要：增量更新过程中，显示指定选中终端执行状态       
        //======================================================================
        private void txtShowPart(string str)
        {
            this.txt_ueupdateinfo.Text += str;
            this.txt_ueupdateinfo.Refresh();
            this.txt_ueupdateinfo.SelectionStart = this.txt_ueupdateinfo.Text.Length;  //光标指向最后一位
            this.txt_ueupdateinfo.ScrollToCaret();   //移动到光标处
        }

        //======================================================================
        //函数名称：enableUpdateJudge
        //函数返回：无
        //参数说明：无
        //功能概要：若更新文件已导入，并已选中终端，则允许进行更新操作；
        //          否则，不允许      
        //======================================================================
        private void enableUpdateJudge()
        {
            //（1）变量声明
            int i;
            int count;

            //（2）计算勾选的终端个数
            count = 0;
            for (i = 0; i < this.ckl_imsi.Items.Count; i++)
                if (this.ckl_imsi.GetItemChecked(i)) count++;
            if (count == 0)  //若未勾选终端，直接退出
            {
                this.btn_autoupdate.Enabled = false;
                return;
            }
            //（3）判断是否允许更新操作
            if ((this.tab_updatestyle.SelectedIndex == 0 && hex1.getFileName() != "")
                || (this.tab_updatestyle.SelectedIndex == 1 && hex2.getFileName() != "" && hex3.getFileName() != ""))
            {
                this.btn_autoupdate.Enabled = true;
            }
            else
            {
                this.btn_autoupdate.Enabled = false;
            }
        }

        //======================================================================
        //函数名称：curUEChange
        //函数返回：无
        //参数说明：imsi：待显示的终端imsi号
        //功能概要：根据传入的imsi号，单独显示该终端的更新状态   
        //======================================================================
        private void curUEChange(string imsi)
        {
            //（1）变量声明
            int sum;
            int cur;
            Update update;

            //（2）判断当前选中终端是否正在更新
            if (dicUpdate.ContainsKey(imsi) && dicUpdate[imsi].getFrameNum() != 0)  //更新过程中
            {
                //状态提示
                this.lbl_ueimsi.Text = "终端：" + imsi;
                update = dicUpdate[imsi];
                sum = update.getFrameNum();
                cur = update.getNextIndex();

                this.prg_update.Value = (cur + 1) * 100 / sum;            //进度条显示
                this.prg_update.Refresh();
                this.lbl_progressbar.Text = "更新进度：" + (cur + 1) * 100 / sum + "%";

                //成功提示
                this.txt_ueupdateinfo.Text = "";
                if (cur + 1 < sum)
                    txtShowPart("终端" + imsi + "更新中...\r\n");
                else
                    txtShowPart("终端" + imsi + "更新完毕！\r\n");
            }
            else  //尚未更新
            {
                //状态提示
                this.lbl_ueimsi.Text = "终端：" + imsi;
                this.prg_update.Value = 0;
                this.lbl_progressbar.Text = "终端尚未更新";
                this.txt_ueupdateinfo.Text = "终端" + imsi + "尚未更新\r\n";
            }

            //（3）
            showimsi = imsi;
        }

        //======================================================================
        //函数名称：curUEChange
        //函数返回：无
        //参数说明：imsi：待显示的终端imsi号
        //功能概要：根据传入的imsi号，单独显示该终端的更新状态   
        //======================================================================
        private void curUEShow(string imsi, int cur, int sum)
        {
            //（1）变量声明

            //（2）判断imsi号
            if (showimsi == imsi && showimsi != "")
            {
                //状态提示
                this.lbl_ueimsi.Text = "终端：" + imsi;
                this.prg_update.Value = (cur + 1) * 100 / sum;            //进度条显示
                this.prg_update.Refresh();
                this.lbl_progressbar.Text = "更新进度：" + (cur + 1) * 100 / sum + "%";
                txtShowPart("当前第" + (cur + 1).ToString() + "/" + sum.ToString() + "帧 \r\n");
            }
        }






    }
}
