using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace Install.Core.Common
{
    public class HttpExt
    {
        /// <summary>
        /// http下载文件
        /// </summary>
        /// <param name="url">下载文件地址</param>
        /// <returns></returns>
        public static byte[] HttpDownload(string url)
        {
            try
            {
                MemoryStream fs = new MemoryStream();
                // 设置参数
                HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
                //发送请求并获取相应回应数据
                HttpWebResponse response = request.GetResponse() as HttpWebResponse;
                //直到request.GetResponse()程序才开始向目标网页发送Post请求
                Stream responseStream = response.GetResponseStream();
                //创建本地文件写入流
                long totalSize = response.ContentLength;
                int postion = 0;
                byte[] bArr = new byte[1024];
                int size = responseStream.Read(bArr, 0, (int)bArr.Length);

                while (size > 0)
                {
                    postion += size;

                    fs.Write(bArr, 0, size);
                    size = responseStream.Read(bArr, 0, (int)bArr.Length);

                }
                Console.WriteLine();
                //stream.Close();
                fs.Close();
                responseStream.Close();

                return fs.ToArray();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                return null;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="url">下载的路径</param>
        /// <param name="path">下载到的本地位置</param>
        /// <param name="md5">md5</param>
        /// <param name="totalSizeChanged">文件总大小 byte</param>
        /// <param name="currSizeChanged">已经下载大小 byte</param>
        /// <param name="speedChanged">下载速度 byte/s</param>
        /// <param name="elapsedTimeSecChanged">已经消耗的秒数</param>
        /// <param name="remainingTimeSecChanged">预计剩余秒数</param>
        /// <returns></returns>
        public static bool BreakpointDownload(
            string url, string path, string md5,
            Action<long> totalSizeChanged = null,
            Action<long> currSizeChanged = null,
            Action<long> speedChanged = null,
            Action<int> elapsedTimeSecChanged = null,
            Action<int> remainingTimeSecChanged = null)
        {
            //1.下载 md5 文件
            string path_md5 = $"{path}.md5";

            if (File.Exists(path_md5))
            {
                //获取本地 md5
                StreamReader sr = new StreamReader(path_md5, System.Text.Encoding.UTF8);
                string localMd5 = sr.ReadLine();
                localMd5 = localMd5.Trim();
                sr.Close();

                //2. 检测 下载回来的md5 与 本地的md5 是否一致， 一致则断点下载
                if (md5 == localMd5)
                {
                    //相同， 继续下载
                    bool ret = BreakpointDownload(url, path, false, totalSizeChanged, currSizeChanged, speedChanged, elapsedTimeSecChanged, remainingTimeSecChanged);
                    if (ret == true)
                    {
                        //下载完了
                        //把本地的 md5 文件删除
                        File.Delete(path_md5);
                    }
                    return ret;
                } 
            }


            {
                //没有本地 md5 信息，创建
                //保存到 filePath.md5
                StreamWriter sw = new StreamWriter(path_md5, false, System.Text.Encoding.UTF8);
                sw.WriteLine(md5);
                sw.Flush();
                sw.Close();

                //重新下载
                return BreakpointDownload(url, path, true, totalSizeChanged, currSizeChanged, speedChanged, elapsedTimeSecChanged, remainingTimeSecChanged);
            }

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="url">下载的路径</param>
        /// <param name="path">下载到的本地位置</param>
        /// <param name="isRedownload">重新下载？</param>
        /// <param name="totalSizeChanged">文件总大小 byte</param>
        /// <param name="currSizeChanged">已经下载大小 byte</param>
        /// <param name="speedChanged">下载速度 byte/s</param>
        /// <param name="elapsedTimeSecChanged">已经消耗的秒数</param>
        /// <param name="remainingTimeSecChanged">预计剩余秒数</param>
        /// <returns></returns>
        public static bool BreakpointDownload(
            string url, string path, bool isRedownload=false,
            Action<long> totalSizeChanged = null,
            Action<long> currSizeChanged =null,
            Action<long> speedChanged = null,
            Action<int> elapsedTimeSecChanged = null,
            Action<int> remainingTimeSecChanged = null)
        {
            string tempPath = System.IO.Path.GetDirectoryName(path);// + @"\temp";
            if (!string.IsNullOrEmpty(tempPath))
            {
                //先新建文件夹
                if(!System.IO.Directory.Exists(tempPath))
                    System.IO.Directory.CreateDirectory(tempPath);
                tempPath += @"\";
            }
            //System.IO.Directory.CreateDirectory(tempPath);  //创建临时文件目录
            string tempFile = $@"{tempPath}{System.IO.Path.GetFileName(path)}.temp"; //临时文件

            if (isRedownload)
            {
                if (System.IO.File.Exists(path))
                {
                    System.IO.File.Delete(path);    //存在则删除
                }
                //重新下载
                if (System.IO.File.Exists(tempFile))
                {
                    System.IO.File.Delete(tempFile);    //存在则删除
                }
            }
            else {
                if (System.IO.File.Exists(path))
                    return true;//已经下载过了
            }

            try
            {
                //FileStream fs = new FileStream(tempFile, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
                // 设置参数
                HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;

                FileStream fs = new FileStream(tempFile, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);

                request.AddRange(Convert.ToInt32(fs.Length));//接上次下载的字节开始下载文件   

                Stopwatch stopwatch = new Stopwatch();
                stopwatch.Start();
                int elapsedTimeSec = 0;
                //发送请求并获取相应回应数据
                HttpWebResponse response = request.GetResponse() as HttpWebResponse;
                //直到request.GetResponse()程序才开始向目标网页发送Post请求
                Stream responseStream = response.GetResponseStream();

                long downloadSize = 0;
                long currSize = fs.Length;
                long totalSize = response.ContentLength + currSize;
                currSizeChanged?.Invoke(currSize);
                totalSizeChanged?.Invoke(totalSize);
                
                do {
                    byte[] bArr = new byte[1024];
                    int size = responseStream.Read(bArr, 0, (int)bArr.Length);
                    if (size <= 0)
                        break;//没有数据。。。。
                    currSize += size;
                    currSizeChanged?.Invoke(currSize);

                    downloadSize += size;
                    if (elapsedTimeSec != (int)stopwatch.Elapsed.TotalSeconds) {

                        elapsedTimeSec = (int)stopwatch.Elapsed.TotalSeconds;
                        elapsedTimeSecChanged?.Invoke(elapsedTimeSec);
                        //过了1秒
                        long speed = (long)(downloadSize / stopwatch.Elapsed.TotalSeconds);
                        speedChanged?.Invoke(speed);

                        //更新剩余时间
                        int remainingTimeSec = (int)((totalSize - currSize) / speed);
                        remainingTimeSecChanged?.Invoke(remainingTimeSec);
                    }

                    fs.Write(bArr, 0, size);
                } while (true);

                fs.Close();
                responseStream.Close();

                //把临时文件，重命名
                System.IO.File.Move(tempFile, path);
                return true;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
    }
}
