using CCWin;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SufeiUtil;
using SXLibrary;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.SQLite;
using System.IO;
using System.Net;
using System.Threading;
using System.Windows.Forms;

namespace MOKA_Factory_Tools
{
    public partial class Download : Skin_Color
    {
        public string RequireHost;//请求的服务器地址
        public string DownloadLink;//key包的下载地址
        public string DownloadSize;//key包的大小
        public string Ordername;//订单号
        public string Ordernumber;//订单数量
        public string Orderclient;//客户机型
        public string OrderPID;//PID
        public string OrderSV;//软件版本
        public string packet_md5;//key包的MD5校验
        public bool[] threadendTag;//判断各线程是否接收完文件
        public string[] filenameList;//各线程下载的小文件名列表
        public int[] filestartpos;//各线程开始读取文件的位置
        public int[] filesize;//小文件大小
        public string url;//请求链接
        public bool union;//判断文件是否合并完成
        public int thread;//线程数
        public int typeNow;//生成类型（-1:普通预绑定下载，0：EEPROM,1:FireTV）
        public SQLiteConnection errorDBNow;
        public int wholesize;
        public MidList midlistnow;
        public WhiteBalanceList whiteBalanceListnow;
        public string filename;
        public Download(System.Data.SQLite.SQLiteConnection errorDB, MidAddress MidAddress1,int type, MidList midList, WhiteBalanceList whiteBalanceList)
        {
            InitializeComponent();
            skinLabel1.Text = LResource.DownloadKeys;
            this.Text = LResource.Download;
            Control.CheckForIllegalCrossThreadCalls = false;
            DownloadLink = MidAddress1.purl;
            DownloadSize = MidAddress1.psize;
            Ordername = MidAddress1.order;
            packet_md5 = MidAddress1.pmd5;
            Ordernumber = MidAddress1.number;
            Orderclient = MidAddress1.ctype;
            OrderPID = MidAddress1.pid;
            OrderSV = MidAddress1.version;
            RequireHost = MidAddress1.host;
            errorDBNow = errorDB;
            typeNow = type;
            midlistnow = midList;
            whiteBalanceListnow = whiteBalanceList;
            filename = Path.GetFileName(DownloadLink);
        }

        private void skinButton1_Click(object sender, EventArgs e)
        {
            this.Dispose();
            this.Close();
        }

        public void StartDownload()
        {
            url = DownloadLink;
            HttpWebRequest request;
            long filesizew = 0;
            try
            {
                request = (HttpWebRequest)HttpWebRequest.Create(url);//建立一个HTTP请求
                HttpWebResponse response = request.GetResponse() as HttpWebResponse;
                filesizew = response.ContentLength;//得到文件总大小
                response.Close();
                request.Abort();//取消请求
            }
            catch (Exception ex)
            {
                MessageBox.Show(LResource.Error + ": " + ex.Message, LResource.Error);        
                Log.WriteErrorLog("\r\nCreate Http require error:"+ ex.Message);
                CommonMethod.ReportErrormsg("Fail to init download process", ex.Message + "\r\ndownload link:" + url, errorDBNow);
                return;
            }

            thread = 1;//单线程
            threadendTag = new bool[thread];
            filenameList = new string[thread];
            filestartpos = new int[thread];
            filesize = new int[thread];

            int filethread = (int)filesizew / thread;
            int filethreade = filethread + (int)filesizew % thread;
            string filePath = AppDomain.CurrentDomain.BaseDirectory + "\\download";

            //文件大小切割
            for (int i = 0; i < thread; i++)
            {
                threadendTag[i] = false;
                filenameList[i] = filePath + "\\" + filename;//+ "-" + i.ToString()
                if (i < thread - 1)
                {
                    filestartpos[i] = filethread * i;
                    filesize[i] = filethread - 1;
                }
                else
                {
                    filestartpos[i] = filethread * i;
                    filesize[i] = filethreade - 1;
                }
            }

           Thread[] threadk = new Thread[thread];
            HttpFile[] httpfile = new HttpFile[thread];

            //各进程开始下载
            for (int j = 0; j < thread; j++)
            {
                httpfile[j] = new HttpFile(this, j);
                threadk[j] = new Thread(new ThreadStart(httpfile[j].ReceiveFile));
                threadk[j].Start();
            }
        }

        private void Download_Shown(object sender, EventArgs e)
        {
            
            StartDownload();
        }

        private void Download_FormClosed(object sender, FormClosedEventArgs e)
        {
            this.Dispose();
            this.Close();
        }
    }

    public class HttpFile
    {
        public Download formm;//声明一个窗体类
        public int threadIndex;//进程标识
        public string filenamea;//下载的小文件名
        public string strUrl;//请求的链接
        public FileStream fs;
        public HttpWebRequest request;
        public HttpWebResponse response;
        public Stream ns;
        public byte[] nbytes;//接收缓冲区
        public int nreadsize;//接收字节数

        public HttpFile(Download form, int thread)
        {
            formm = form;
            threadIndex = thread;
        }

        ~HttpFile()
        {
            formm.Dispose();
        }

        public int GetThread
        {
            get
            {
                return threadIndex;
            }
        }

        public int GetFilesize
        {
            get
            {
                return formm.filesize[threadIndex];
            }
        }

        public void ReceiveFile()//线程开始接收文件
        {
            filenamea = formm.filenameList[threadIndex];
            strUrl = formm.DownloadLink;
            ns = null;
            nbytes = new byte[1024];
            nreadsize = 0;
            //formm.recevMsg.Items.Add("线程" + threadIndex.ToString() + "开始接收：" + "文件大小" + Math.Ceiling(formm.filesize[threadIndex] / 1024.0f) + "KB");
            formm.ProgressBar1.Maximum = Convert.ToInt32(Math.Ceiling(formm.filesize[threadIndex] / 1024.0f));
            fs = new FileStream(filenamea, FileMode.Create);
            string dbPath = AppDomain.CurrentDomain.BaseDirectory + "\\Keys\\" + formm.Ordername + ".db";
            
            try
            {                              
                request = (HttpWebRequest)WebRequest.Create(strUrl);
                request.KeepAlive = false;
                //request.ProtocolVersion = HttpVersion.Version10;
                request.Proxy = WebRequest.DefaultWebProxy;//不使用代理，减少自动搜索代理的过程，提升性能
                //要接收文件的字节范围
                //request.AddRange(formm.filestartpos[threadIndex], formm.filestartpos[threadIndex] + formm.filesize[threadIndex]);
                request.Timeout = 5 * 60 * 1000;
                System.Net.ServicePointManager.DefaultConnectionLimit = 200;
                //ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;
                //request.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)";
                response = request.GetResponse() as HttpWebResponse;
                ns = response.GetResponseStream();
                nreadsize = ns.Read(nbytes, 0, nbytes.Length);
                int preocess = 0;
                while (nreadsize > 0)
                {
                    fs.Write(nbytes, 0, nreadsize);
                    //formm.recevMsg.Items.Add("线程" + threadIndex.ToString() + "已下载字节：" + Math.Ceiling(nreadsize / 1024.0f) + "KB");                   
                    nreadsize = ns.Read(nbytes, 0, nbytes.Length);                  
                    preocess += (int)Math.Ceiling(nreadsize / 1024.0f);
                    if(preocess<= formm.ProgressBar1.Maximum)
                    {
                        formm.ProgressBar1.Value = preocess;
                    }
                }
                fs.Close();
                ns.Close();
                response.Close();
                GC.Collect();

                Log.WriteGetKeyLog("Download key package success:" + formm.filename);
                if(!File.Exists(dbPath))
                {
                    if (SQLiteHelper.NewDbFile(dbPath))
                    {
                        SQLiteHelper.NewTable(dbPath, "keys", "(sn varchar,keys varvhar,copy_date DATETIME,report_date DATETIME)");
                        SQLiteHelper.NewTable(dbPath, "mid", "(bid varchar,number varchar, pid varchar,ctype varvhar,version varvhar,host varchar,purl varvhar,psize varvhar,pmd5 varvhar,status varvhar,start_date DATETIME DEFAULT (datetime(CURRENT_TIMESTAMP,'localtime')),finish_date DATETIME,desc varvhar)");
                        SQLiteHelper.NewTable(dbPath, "report", "(url varchar,content varchar,gener_date DATETIME,report_date DATETIME)");
                        SQLiteHelper.NewTable(dbPath, "rokuCustomer", "(ordernum varchar,region varchar,brand varchar,oemmodel varchar,supporturl varchar,supportphone varchar,productiondate varchar,remotetype varchar)");
                        SQLiteHelper.NewTable(dbPath, "whitebalance", "(ordernum varchar,hdmirgain varchar,hdmiggain varchar,hdmibgain varchar,nrgain varchar,nggain varchar,nbgain varchar,lrgain varchar,lggain varchar,lbgain varchar,updatetime DATETIME DEFAULT (datetime(CURRENT_TIMESTAMP,'localtime')))");
                        SQLiteHelper.NewTable(dbPath, "dsn", "(ordernum varchar,dsn varchar,updatetime DATETIME DEFAULT (datetime(CURRENT_TIMESTAMP,'localtime')))");
                        SQLiteHelper.AddOneLine(dbPath, new object[] { formm.Ordername, formm.Ordernumber, formm.OrderPID, formm.Orderclient, formm.OrderSV, formm.RequireHost, formm.DownloadLink, formm.DownloadSize, formm.packet_md5, "0" });
                        if (formm.midlistnow.rokuCustomer != null)
                            SQLiteHelper.AddRokuOneLine(dbPath, new object[] { formm.midlistnow.rokuCustomer.ordernum, formm.midlistnow.rokuCustomer.region, formm.midlistnow.rokuCustomer.brand, formm.midlistnow.rokuCustomer.oemmodel, formm.midlistnow.rokuCustomer.supporturl, formm.midlistnow.rokuCustomer.supportphone, formm.midlistnow.rokuCustomer.productiondate, formm.midlistnow.rokuCustomer.remotetype });
                        if (formm.whiteBalanceListnow != null)
                            if (formm.whiteBalanceListnow.whiteBalanceInfo != null)
                                if (formm.whiteBalanceListnow.whiteBalanceInfo.isExemption != "1")
                                {
                                    SQLiteHelper.AddwbOneLine(dbPath, new object[] { formm.whiteBalanceListnow.whiteBalanceInfo.ordernum,
                                formm.whiteBalanceListnow.whiteBalanceInfo.hdmirgain, formm.whiteBalanceListnow.whiteBalanceInfo.hdmiggain, formm.whiteBalanceListnow.whiteBalanceInfo.hdmibgain,
                                formm.whiteBalanceListnow.whiteBalanceInfo.nrgain, formm.whiteBalanceListnow.whiteBalanceInfo.nggain, formm.whiteBalanceListnow.whiteBalanceInfo.nbgain,
                                formm.whiteBalanceListnow.whiteBalanceInfo.lrgain, formm.whiteBalanceListnow.whiteBalanceInfo.lggain, formm.whiteBalanceListnow.whiteBalanceInfo.lbgain });
                                }

                        if (formm.midlistnow.keytype.ContainsKey("DSN"))
                        {
                            SQLiteHelper.AdddsnOneLine(dbPath, new object[] { formm.Ordername, formm.midlistnow.keytype["DSN"] });
                        }
                    }
                }
                

            }
            catch (Exception ex)
            {
                SQLiteHelper.UpdateStatus(dbPath, "0");
                MessageBox.Show(ex.Message, LResource.Error) ;
                Log.WriteErrorLog("\r\nDownload error:" + ex.Message);
                CommonMethod.ReportErrormsg("Fail to download key package", ex.Message + "\r\ndownload link:" + formm.DownloadLink, formm.errorDBNow);
                fs.Close();
                formm.Close();
                GC.Collect();
                return;
            }
            string keyinfo = "";
            string downloadMD5 = GetMD5.GetMD5HashFromFile(filenamea);
            if (downloadMD5 == formm.packet_md5)
            {
                try
                {
                    SQLiteHelper.UpdateTime(dbPath, formm.Ordername);
                    List<object[]> keyDatas = new List<object[]>();
                    using (System.IO.StreamReader file = System.IO.File.OpenText(filenamea))
                    {
                        using (JsonTextReader reader = new JsonTextReader(file))
                        {
                            JArray o = (JArray)JToken.ReadFrom(reader);
                            formm.ProgressBar1.Maximum = o.Count;
                            int index = 0;
                            formm.skinLabel1.Text = LResource.ParseKeys;
                            foreach (var ss in o)  //查找某个字段与值
                            {
                                var sn = ((JObject)ss)["sn"];
                                var data = ((JObject)ss)["key"];
                                keyDatas.Add(new object[] { sn, data });
                                formm.ProgressBar1.Value = index;
                                switch(formm.typeNow)
                                {
                                    case -1:
                                        {
                                            break;
                                        }
                                    case 0:
                                        {
                                            if (!Countkey.CreateEEPROMKey(data.ToString(), formm.Ordername, out keyinfo, out string error))
                                            {
                                                Log.WriteErrorLog("\r\nCreate EEPROM fail:\r\n" + error);
                                                MessageBox.Show(LResource.CreateEEPROMError + "\r\n" + error);
                                                formm.skinLabel1.Text = LResource.ParseKeysError;
                                                return;
                                            }
                                            break;
                                        }
                                    case 1:
                                        {
                                            if(!FireTVkey.CreateFireTVKeys(data.ToString(), formm.Ordername,out string error))
                                            {
                                                Log.WriteErrorLog("\r\nCreate FireTV keys fail:\r\n" + error);
                                                MessageBox.Show( "生成FireTV订单离线key失败\r\n" + error);
                                                formm.skinLabel1.Text = LResource.ParseKeysError;
                                                return;
                                            }
                                            break;
                                        }

                                                                    
                                }
                                index++;
                            }
                        }
                    }
                    formm.skinLabel1.Text = LResource.ParseKeys;
                    formm.skinLabel1.Text = LResource.InsertKeystoDB;
                    if (formm.typeNow == 0)
                    {
                        MessageBox.Show("合成的Key类型有:" + keyinfo);
                     }
                    if (InsertKeys(dbPath, keyDatas))
                    {
                        SQLiteHelper.UpdateStatus(dbPath, "1");
                    }
                    else
                    {
                        MessageBox.Show(LResource.TransferDBFail);
                    }                    
                }
                catch(Exception ex)
                {
                    SQLiteHelper.UpdateStatus(dbPath, "0");
                    Log.WriteErrorLog(ex.Message);
                    formm.Close();
                    GC.Collect();
                    return;
                }                
            }
                
            else
            {
                SQLiteHelper.UpdateStatus(dbPath, "0");
                MessageBox.Show("MD5 checking fail,please try again"+ downloadMD5);
                CommonMethod.ReportErrormsg("Key package MD5 checking fail", "MD5 checking error targetMD5:" + formm.packet_md5 + "\r\ndownload file MD5:" + downloadMD5, formm.errorDBNow);
                Log.WriteErrorLog("\r\nMD5 checking error targetMD5:" + formm.packet_md5 + "\r\ndownload file MD5:" + downloadMD5);
            }
            while (true)
            {
                /*
                 GetAvailableThreads()：检索由 GetMaxThreads 返回的线程池线程的最大数目和当前活动数目之间的差值。
                 而GetMaxThreads 检索可以同时处于活动状态的线程池请求的数目。
                 通过最大数目减可用数目就可以得到当前活动线程的数目，如果为零，那就说明没有活动线程，说明所有线程运行完毕。
                 */
                ThreadPool.GetMaxThreads(out int maxWorkerThreads, out int portThreads);
                ThreadPool.GetAvailableThreads(out int workerThreads, out portThreads);
                if (maxWorkerThreads - workerThreads == 0)
                {
                    Log.WriteGetKeyLog("Dowload thread Finished!");
                    break;
                }
                else
                {
                    Thread.Sleep(500);
                    Log.WriteGetKeyLog("Dowload thread unfinished!");
                }
            }
            formm.Close();            
        }
        public bool InsertKeys(string dbPath, List<object[]> keys)
        {
            try
            {
                SQLiteConnection sqliteConn = new SQLiteConnection("data source=" + dbPath);
                if (sqliteConn.State != System.Data.ConnectionState.Open)
                {
                    sqliteConn.Open();
                    SQLiteCommand cmd = new SQLiteCommand();
                    cmd.Connection = sqliteConn;
                    int index = 0;
                    DbTransaction trans = sqliteConn.BeginTransaction();
                    foreach (var key in keys)
                    {
                        formm.ProgressBar1.Value = index;
                        cmd.CommandText = string.Format("insert into keys(sn,keys) values('{0}','{1}')", key[0], key[1]);
                        cmd.ExecuteNonQuery();
                        index++;
                    }
                    trans.Commit();
                }
                sqliteConn.Close();
                sqliteConn.Dispose();
                return true;
            }
            catch (Exception ex)
            {
                Log.WriteErrorLog("\r\nFail to transfer key to DB:\r\n" + ex.Message);
                return false;
            }
        }
    }
}
