﻿using System;
using System.Collections;
using System.IO;
using Toast.SmartDownloader;
using UnityEngine;
using UnityEngine.UI;
using Debug = UnityEngine.Debug;

namespace Toast.SmartDownloader.Example
{
    public partial class SmartDlExample : MonoBehaviour
    {
        [Header("Input Fields")]
        public InputField AppkeyInputField;
        public InputField ServiceNameField;
        public InputField DstPathField;

        [Space]

        [Header("Config Sliders")]
        public Slider RetryCountSlider;
        public Slider ConnectTimeoutSlider;
        public Slider ReadTimeoutSlider;
        public Toggle CheckStreamingAssetsToggle;
        public Dropdown CheckPatchCheckDropdown;

        [Space]

        [Header("Result Items")]
        public Text ResultText;
        public ProgressBar PercentageBar;
        public Button DownloadAfterCheckButton;

        [Space]

        [Header("Etc.")]
        public Button ExtraButton;
        public RectTransform ExitPanel;

        private Coroutine _progressCoroutine;
        private ResultCode _recentResultCode = ResultCode.UNKNOWN;
        private DownloadConfig downloadConfig = DownloadConfig.Default;

        #region Define partial methods
        partial void OnBeforeAwake();
        partial void OnCompleteDownload(DownloadResult result, DownloadProfiler profiler);
        partial void OnSetupDownloadConfig(DownloadConfig config);
        #endregion

        void Awake()
        {
            OnBeforeAwake();

            AppkeyInputField.text = SmartDlExampleConfiguration.Appkey;
            ServiceNameField.text = SmartDlExampleConfiguration.ServiceName;
            DstPathField.text = SmartDlExampleConfiguration.DownloadPath;

            RetryCountSlider.value = SmartDlExampleConfiguration.RetryCount;
            ConnectTimeoutSlider.value = SmartDlExampleConfiguration.ConnectionTimeoutSeconds;
            ReadTimeoutSlider.value = SmartDlExampleConfiguration.ReadTimeoutSeconds;

            AppkeyInputField.onEndEdit.AddListener(SmartDlExampleConfiguration.SaveAppkey);
            ServiceNameField.onEndEdit.AddListener(SmartDlExampleConfiguration.SaveServiceName);
            DstPathField.onEndEdit.AddListener(SmartDlExampleConfiguration.SaveDownloadPath);

            CheckPatchCheckDropdown.value = SmartDlExampleConfiguration.CheckOption;
            ChangeCheckOption(CheckPatchCheckDropdown.value);

            RetryCountSlider.onValueChanged.AddListener(retryCount => SmartDlExampleConfiguration.SaveRetryCount((int)retryCount));
            ConnectTimeoutSlider.onValueChanged.AddListener(connectionTimeoutSeconds => SmartDlExampleConfiguration.SaveConnectionTimeoutSeconds((int)connectionTimeoutSeconds));
            ReadTimeoutSlider.onValueChanged.AddListener(readTimeoutSeconds => SmartDlExampleConfiguration.SaveReadTimeoutSeconds((int)readTimeoutSeconds));

            CheckStreamingAssetsToggle.onValueChanged.AddListener(isCheck =>
            {
                if (isCheck == true)
                {
                    downloadConfig.CheckOption |= PatchCheckOption.COMPARE_WITH_STREAMING_ASSETS;
                }
                else
                {
                    downloadConfig.CheckOption ^= PatchCheckOption.COMPARE_WITH_STREAMING_ASSETS;
                }
            });

            CheckPatchCheckDropdown.onValueChanged.AddListener(index =>
            {
                SmartDlExampleConfiguration.SaveCheckOption(index);
                ChangeCheckOption(index);
            });


#if UNITY_2018_1_OR_NEWER
            Application.wantsToQuit += WantsToQuit;
#endif
        }

        void OnDestroy()
        {
#if UNITY_2018_1_OR_NEWER
            Application.wantsToQuit -= WantsToQuit;
#endif
        }

        void Start()
        {
            SmartDlLogger.CurrentLevel = SmartDlLogger.LogLevel.Trace;
            SmartDlLogger.OnLog += (type, log) =>
            {
                switch (type)
                {
                    case SmartDlLogger.LogLevel.Trace:
                    case SmartDlLogger.LogLevel.Developer:
                    case SmartDlLogger.LogLevel.Debug:
                    case SmartDlLogger.LogLevel.Info:
                        Debug.Log(log);
                        break;
                    case SmartDlLogger.LogLevel.Warn:
                        Debug.LogWarning(log);
                        break;
                    case SmartDlLogger.LogLevel.Error:
                        Debug.LogError(log);
                        break;
                    default:
                        Debug.Log(log);
                        break;
                }
            };

            #region Set DownloadConfig
            downloadConfig.RetryDownloadCountPerFile = (int)RetryCountSlider.value;
            downloadConfig.DownloadConnectTimeout = TimeSpan.FromSeconds(ConnectTimeoutSlider.value);
            downloadConfig.DownloadReadTimeout = TimeSpan.FromSeconds(ReadTimeoutSlider.value);
            OnSetupDownloadConfig(downloadConfig);
            #endregion
        }

        public void OnClickCheckDownload()
        {
            if (IsVerified() == false)
            {
                return;
            }

            PrintResult(string.Format("Check download (option= {0})", downloadConfig.CheckOption));

            var profiler = new DownloadProfiler();
            profiler.Start();

            _recentResultCode = ResultCode.UNKNOWN;
            SmartDl.SetUserId("SmartDlExample");
            SmartDl.CheckDownload(
                SmartDlExampleConfiguration.Appkey,
                SmartDlExampleConfiguration.ServiceName,
                SmartDlExampleConfiguration.DownloadPath,
                downloadConfig,
                result =>
                {
                    profiler.End();
                    _recentResultCode = result.Code;

                    switch (result.Code)
                    {
                        case ResultCode.SUCCESS:
                            {
                                double totalSize = SmartDl.Progress.TotalFileBytes / 1024.0;
                                PrintResult(string.Format("Check elapsed time : {0:F2} ms / {1:F2} s - Download size: {2:F2}KB / file count: {3}", profiler.ElapsedMilliseconds, profiler.ElapsedSeconds, totalSize, SmartDl.Progress.TotalFileCount));
                                DownloadAfterCheckButton.gameObject.SetActive(true);
                                break;
                            }
                        case ResultCode.SUCCESS_NO_DIFFERENCE:
                            {
                                PrintResult(string.Format("Success elapsed time : {0:F2} ms / {1:F2} s",
                                    profiler.ElapsedMilliseconds,
                                    profiler.ElapsedSeconds));
                                break;
                            }
                        default:
                            {
                                PrintResult(string.Format("Fail to download [{0}]", result));
                                break;
                            }
                    }
                });
        }

        public void OnClickDownloadAfterCheck()
        {
            if (IsVerified() == false)
            {
                return;
            }

            PrintResult(string.Format("Start download (option= {0})", downloadConfig.CheckOption));
            StopUpdateProgressCoroutine();

            var profiler = new DownloadProfiler();
            profiler.Start();

            SmartDl.StartDownload(
                result =>
                {
                    profiler.End();
                    _recentResultCode = result.Code;

                    DownloadAfterCheckButton.gameObject.SetActive(false);

                    OnCompleteDownload(result, profiler);
                    if (result.Code == ResultCode.SUCCESS || result.Code == ResultCode.SUCCESS_NO_DIFFERENCE)
                    {
                        PrintResult(string.Format("Success elapsed time : {0:F2} ms / {1:F2} s",
                            profiler.ElapsedMilliseconds,
                            profiler.ElapsedSeconds));
                    }
                    else
                    {
                        PrintResult(string.Format("Fail to download [{0}]", result));
                        StopUpdateProgressCoroutine();
                    }
                });

            _progressCoroutine = StartCoroutine(UpdateProgress());
        }

        public void OnClickStartDownload()
        {
            if (IsVerified() == false)
            {
                return;
            }

            PrintResult(string.Format("Start download (option= {0})", downloadConfig.CheckOption));
            StopUpdateProgressCoroutine();

            var profiler = new DownloadProfiler();
            profiler.Start();

            _recentResultCode = ResultCode.UNKNOWN;
            SmartDl.SetUserId("SmartDlExample");
            SmartDl.StartDownload(SmartDlExampleConfiguration.Appkey,
                SmartDlExampleConfiguration.ServiceName,
                SmartDlExampleConfiguration.DownloadPath,
                downloadConfig,
                result =>
            {
                profiler.End();
                _recentResultCode = result.Code;

                OnCompleteDownload(result, profiler);
                switch (result.Code)
                {
                    case ResultCode.SUCCESS:
                    case ResultCode.SUCCESS_NO_DIFFERENCE:
                        {
                            PrintResult(string.Format("Success elapsed time : {0:F2} ms / {1:F2} s",
                                profiler.ElapsedMilliseconds,
                                profiler.ElapsedSeconds));
                            break;
                        }
                    default:
                        {
                            PrintResult(string.Format("Fail to download [{0}]", result));
                            StopUpdateProgressCoroutine();
                            break;
                        }
                }
            });
            _progressCoroutine = StartCoroutine(UpdateProgress());
        }

        public void OnClickStopDownload()
        {
            PrintResult("Stop download");
            StopUpdateProgressCoroutine();
            DownloadAfterCheckButton.gameObject.SetActive(false);
            SmartDl.StopDownload();
        }

        public void OnClickDeleteFiles()
        {
            PrintResult(FileUtil.DeleteFilesInDownloadPath() ? "Success to delete directory" : "Fail to delete directory");
        }

        private void StopUpdateProgressCoroutine()
        {
            if (_progressCoroutine != null)
            {
                StopCoroutine(_progressCoroutine);
            }
        }

        private IEnumerator UpdateProgress()
        {
            while (true)
            {
                var progress = SmartDl.Progress;

                PercentageBar.Percentage = progress.Percentage;
                PercentageBar.CompletedFileCount = progress.CompletedFileCount;
                PercentageBar.TotalFileCount = progress.TotalFileCount;
                PercentageBar.CompletedFileSizeKB = progress.DownloadedBytes / 1024.0;
                PercentageBar.TotalFileSizeKB = progress.TotalFileBytes / 1024.0;

                if (progress.FileMap != null)
                {
                    var threadCount = progress.FileMap.Count;
                    ThreadProgressContainer.Instance.ThreadCount = threadCount;
                    for (int i = 0; i < threadCount; i++)
                    {
                        var file = progress.FileMap[i];
                        ThreadProgressContainer.Instance[i].ThreadNumber = i.ToString();
                        ThreadProgressContainer.Instance[i].FileName = file.FileName;
                        ThreadProgressContainer.Instance[i].ProgressBar.Percentage = (file.DownloadedBytes / (float)file.TotalBytes) * 100.0f;
                    }
                }

                if (progress.IsCompleted == true)
                {
                    yield break;
                }

                yield return null;
            }
        }

        private void ChangeCheckOption(int value)
        {
            switch (value)
            {
                case 1:
                    {
                        downloadConfig.CheckOption |= PatchCheckOption.CHECK_LIST_WITH_SAVED_DATA;
                        break;
                    }
                default:
                    {
                        downloadConfig.CheckOption &= ~PatchCheckOption.CHECK_LIST_WITH_SAVED_DATA;
                        break;
                    }
            }
        }

        private bool IsVerified()
        {
            if (string.IsNullOrEmpty(AppkeyInputField.text))
            {
                PrintResult("You MUST input appkey field");
                return false;
            }

            if (string.IsNullOrEmpty(ServiceNameField.text))
            {
                PrintResult("You MUST input service name");
                return false;
            }

            return true;
        }
    }
}