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

public partial class SmartDlExample : MonoBehaviour
{
    public InputField AppkeyInputField;
    public InputField CdnUrlField;
    public InputField MetafileNameField;
    public InputField DstPathField;

    public Button ExtraButton;

    public Text ResultText;

    public ProgressBar PercentageBar;

    private Coroutine _progressCoroutine;

    partial void OnBeforeAwake();
    partial void OnCompleteDownload(SmartDLResult result, DownloadProfiler profiler);

    void Awake()
    {
        OnBeforeAwake();

        AppkeyInputField.text = SmartDlExampleConfiguration.AppKey;
        CdnUrlField.text = SmartDlExampleConfiguration.CdnUrl;
        MetafileNameField.text = SmartDlExampleConfiguration.MetaFileName;
        DstPathField.text = SmartDlExampleConfiguration.DownloadPath;

        AppkeyInputField.onEndEdit.AddListener(SmartDlExampleConfiguration.SaveAppKey);
        CdnUrlField.onEndEdit.AddListener(SmartDlExampleConfiguration.SaveCdnUrl);
        MetafileNameField.onEndEdit.AddListener(SmartDlExampleConfiguration.SaveMetaFileName);
        DstPathField.onEndEdit.AddListener(SmartDlExampleConfiguration.SaveDownloadPath);

        AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
        {
            Debug.LogFormat("UNHANDLED APPDOMAIN EXCEPTION ({0})", e.IsTerminating ? "Terminating" : "Non-Terminating");
            var exception = e.ExceptionObject as Exception;
            if (exception != null)
                Debug.LogException(exception);
            else
                Debug.LogError("Exception is null");
        };
    }

    void Start()
    {
        SmartDlLogger.CurrentLevel = SmartDlLogger.LogLevel.Developer;
        SmartDlLogger.OnLog += (type, log) =>
        {
            switch (type)
            {
                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:
                    throw new ArgumentOutOfRangeException("type", type, null);
            }
        };
    }

    public void OnClickStartDownload()
    {
        if (string.IsNullOrEmpty(AppkeyInputField.text))
        {
            PrintResult("You MUST input appkey field");
            return;
        }

        PrintResult("Start download");
        StopUpdateProgressCoroutine();

        var profiler = new DownloadProfiler();
        profiler.Start();
        
        SmartDLUnitySkin.SetAppKey(SmartDlExampleConfiguration.AppKey);
        SmartDLUnitySkin.StartDownload(SmartDlExampleConfiguration.CdnUrl, SmartDlExampleConfiguration.MetaFileName, SmartDlExampleConfiguration.DownloadPath, result =>
        {
            profiler.End();

            OnCompleteDownload(result, profiler);
            if (result.ResultCode == (int) ResultCode.Ok ||
                result.ResultCode == (int) ResultCode.NoDiff)
            {
                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 OnClickStopDownload()
    {
        PrintResult("Stop download");
        StopUpdateProgressCoroutine();
        SmartDLUnitySkin.StopDownload();
    }
    
    public void OnClickDeleteFiles()
    {
        PrintResult(DeleteFiles() ? "Success to delete directory" : "Fail to delete directory");
    }

    internal bool DeleteFiles()
    {
        try
        {
            var directories = Directory.GetDirectories(SmartDlExampleConfiguration.DownloadPath);
            foreach (var dir in directories)
            {
                Directory.Delete(dir, true);
            }
            var files = Directory.GetFiles(SmartDlExampleConfiguration.DownloadPath, "*");
            foreach (var file in files)
            {
                File.Delete(file);
            }
            return true;
        }
        catch (Exception)
        {
            return false;
        }
    }

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

    IEnumerator UpdateProgress()
    {
        var percentage = 0.0f;
        while (true)
        {
            var progress = SmartDLUnitySkin.GetProgressInfo();

            percentage = progress.Percentage;
            if(percentage > 0 && PercentageBar.Percentage > percentage && !Mathf.Approximately(PercentageBar.Percentage, percentage))
                Debug.LogWarningFormat("Percentage Prev[{0}] > Curr[{1}]", PercentageBar.Percentage, percentage);
            PercentageBar.Percentage = percentage;
            PercentageBar.CompletedFileCount = progress.CompletedFileCount;
            PercentageBar.TotalFileCount = progress.TotalFileNumber;
            PercentageBar.CompletedFileBytes = progress.TotalReceivedBytes;
            PercentageBar.TotalFileBytes = progress.TotalFileBytes;

            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)
				yield break;
            
            yield return null;
        }
    }

    internal void PrintResult(string result)
    {
        if (ResultText != null)
            ResultText.text = result;
    }
}
