﻿using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;

public class SppDownloadInfo
{
	public string fileName;
	public bool useCache = true;
	public GameObject callbackGameObject = null;
	public string method;
	public WWWForm form = null;

	public SppDownloadInfo (GameObject callbackGameObject, string method, string fileName, bool useCache)
	{
		this.fileName = fileName;
		this.useCache = useCache;
		this.callbackGameObject = callbackGameObject;
		this.method = method;
		this.form = null;
	}
	public SppDownloadInfo (GameObject callbackGameObject, string method, string fileName, bool useCache, WWWForm form)
	{
		this.fileName = fileName;
		this.useCache = useCache;
		this.callbackGameObject = callbackGameObject;
		this.method = method;
		this.form = form;
	}
}



public class SppDownloadManager : MonoBehaviour
{
	public bool clearCacheOnStart = true;
	public GameObject spinner;
	public float spinnerDelay = 0.5f;

	private List<SppDownloadInfo> filesToDownload = new List<SppDownloadInfo> ();
	private List<SppCachedFile> cachedFiles = new List<SppCachedFile> ();
	private bool isDownloading = false;
	private WWW web;
	private long totalBytesInCache = 0;
	private string cacheFolder;
	private static long MAX_BYTES = 1024 * 1024 * 500;
	// 500 MB
	private int currentNumber;

	public void Awake ()
	{
#if UNITY_ANDROID || UNITY_WP8 || UNITY_IOS || UNITY_WEBGL || UNITY_WSA
		cacheFolder = Application.persistentDataPath; // System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
#else
        cacheFolder = System.Environment.GetFolderPath (System.Environment.SpecialFolder.Personal);
#endif
		cacheFolder = cacheFolder.Replace ("\\", "/"); 
		cacheFolder = CombinePath (cacheFolder, "cache");
		// force folder
		System.IO.Directory.CreateDirectory (cacheFolder); 
		GetCachedFiles ();
		if (clearCacheOnStart) {
			ClearCache ();
		}
	}

	public void DownloadFile (GameObject sender, string method, string fileName, bool useCache = true)
	{
		SppDownloadInfo di = new SppDownloadInfo (sender, method, fileName, useCache);
		filesToDownload.Add (di);
		if (spinner) {
			spinner.SetActive (true);
		}
	}

	public void DownloadFile (GameObject sender, string method, string fileName, WWWForm form)
	{
		SppDownloadInfo di = new SppDownloadInfo (sender, method, fileName, false, form);
		filesToDownload.Add (di);
		if (spinner) {
			spinner.SetActive (true);
		}
	}


	public void StopDownload ()
	{
		isDownloading = false;
		if (web != null) {
			web.Dispose (); 
		}
		filesToDownload.Clear ();
		Invoke ("HideSpinner", spinnerDelay);

		currentNumber = 0;
	}

	private IEnumerator InternalDownload ()
	{
		bool stopped = false;

		if (spinner) {
			spinner.SetActive (true);
		}

		isDownloading = true;

		string url = CombinePath (cacheFolder, GetFileName (filesToDownload [0].fileName));
		if (System.IO.File.Exists (url) && filesToDownload [0].useCache == true) {
			if (Application.platform == RuntimePlatform.IPhonePlayer) {
				url = url.Replace (" ", "%20");
			}

		} else {
			url = filesToDownload [0].fileName; 
			url = url.Replace (" ", "%20");
		}
		if (stopped == false) {
			if (filesToDownload [0].form != null) {
				web = new WWW (url, filesToDownload [0].form );
			} else {
				web = new WWW (url);
			}
			yield return web;
		} else {
			yield return null;
		}
	}

	public void ClearCache ()
	{
		SppCachedFile cf;
		string localFile;
		while (cachedFiles.Count > 0) {
			cf = cachedFiles [0];
			localFile = CombinePath (cacheFolder, cf.name);
			System.IO.File.Delete (localFile);
			cachedFiles.RemoveAt (0);
		}
		totalBytesInCache = 0;
	}

	public void Update ()
	{
		if (isDownloading) {
			if (web.isDone) {
				currentNumber++;
				if (string.IsNullOrEmpty (web.error)) {
					if (web.url.ToLower ().EndsWith (".png") || web.url.ToLower ().EndsWith (".jpg") || web.url.ToLower ().EndsWith (".4tgz")) {
						StoreFile (web.url, web.bytes);
					}
					filesToDownload [0].callbackGameObject.SendMessage (filesToDownload [0].method, new SppWebData (web)/*, SendMessageOptions.DontRequireReceiver*/);
				} else {
					// finished with errors
					filesToDownload [0].callbackGameObject.SendMessage (filesToDownload [0].method, new SppWebData (web)/*, SendMessageOptions.DontRequireReceiver*/);
				}
				isDownloading = false;
				try {
					filesToDownload.RemoveAt (0); // remove currently downloaded files
				} catch (System.Exception e) {
					Debug.Log (e.Message); // NOP
				}
				if (filesToDownload.Count == 0) {
					Invoke ("HideSpinner", spinnerDelay);
					isDownloading = false;
				}
			}
		} else {
			// not downloading
			if (filesToDownload.Count > 0) {
				// get next
				StartCoroutine ("InternalDownload");
			}	
		}

	}

	private void GetCachedFiles ()
	{
		totalBytesInCache = 0;
		System.IO.DirectoryInfo d = new System.IO.DirectoryInfo (cacheFolder);
		System.IO.FileInfo[] Files = d.GetFiles ("*.*"); 
		foreach (System.IO.FileInfo file in Files) {
			SppCachedFile cf = new SppCachedFile (file.Name, file.Length);
			totalBytesInCache += file.Length;
			cachedFiles.Add (cf);
		}
	}

	private string CombinePath (string prefix, string postfix)
	{
		if (!prefix.EndsWith ("/"))
			prefix = prefix + "/";
		postfix = postfix.Trim ('/');
		return (prefix + postfix);
	}

	private void StoreFile (string url, byte[] data)
	{
		string fileName;
		if (!url.StartsWith ("file:")) {
			fileName = GetFileName (url);
			string localFile = CombinePath (cacheFolder, fileName);
			localFile = WWW.UnEscapeURL (localFile);
			System.IO.File.WriteAllBytes (localFile, data);
#if UNITY_IPHONE	
			iPhone.SetNoBackupFlag (localFile);
#endif			
			SppCachedFile cf = new SppCachedFile (fileName, data.Length);
			totalBytesInCache += data.Length;
			cachedFiles.Add (cf);
			ClearCachedFiles ();
		}
	}

	public void ClearCachedFiles ()
	{
		SppCachedFile cf;
		string localFile;
		while (totalBytesInCache > MAX_BYTES) {
			cf = cachedFiles [0];
			totalBytesInCache -= cf.size;
			localFile = CombinePath (cacheFolder, cf.name);
			System.IO.File.Delete (localFile);
			cachedFiles.RemoveAt (0);
		}
	}

	private bool ByteArrayToFile (string _FileName, byte[] _ByteArray)
	{
		try {
			// Open file for reading
			System.IO.FileStream _FileStream = new System.IO.FileStream (_FileName, System.IO.FileMode.Create, System.IO.FileAccess.Write);
			// Writes a block of bytes to this stream using data from a byte array.
			_FileStream.Write (_ByteArray, 0, _ByteArray.Length);
            // close file stream
            _FileStream.Dispose ();
            return true;
		} catch (System.Exception e) {
			Debug.LogError (e.Message);
			return false;
		}
	}

	private string GetFileName (string path)
	{
		string[] parts = path.Split ('/');
		return UnEscapeURL (parts [parts.Length - 1]);
	}

	private string EscapeURL (string url)
	{
		url = WWW.EscapeURL (url);
		url = url.Replace ("+", "%20");
		return url;
	}

	private string UnEscapeURL (string url)
	{
		url = WWW.UnEscapeURL (url);
		url = url.Replace ("+", " ");
		return url;
	}

	private void HideSpinner ()
	{
		if (filesToDownload.Count == 0) {
			if (spinner) {
				spinner.SetActive (false);
			}
		}
	}

	public static void ClearFolder (string folderName)
	{
		string[] filePaths = System.IO.Directory.GetFiles (folderName); // should end with slash/backslash
		foreach (string filePath in filePaths)
			System.IO.File.Delete (filePath);	
	}

	public int GetRemainingFiles ()
	{
		return filesToDownload.Count;
	}

}
