﻿using UnityEditor;

using UnityEngine;

using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;

using Net.FabreJean.UnityEditor;

namespace Net.FabreJean.PlayMaker.Ecosystem
{

/* RawData
{
    "name": "Vector3ToVector2.cs",
    "type": "Action",
    "pretty name": "Vector3 To Vector2",
    "path": "Assets/PlayMaker Custom Actions/Vector2/Vector3ToVector2.cs",
    "category": "Vector2",
    "unity_version": "3",
    "beta": "false",
    "repository": {
		"type": "snipt", || Github
		"id": "138880",
		"raw_url": "https://snipt.net/raw/e61f68c609f0000528613833abbc3bb6/", ( snipt only)
		"preview_url": "https://jeanfabre.snipt.net/anykey-d56e7086/", ( snipt only)
        "name": "PlayMakerCustomActions_U3",
        "full_name": "jeanfabre/PlayMakerCustomActions_U3", ( Github only)
        "owner": {
            "login": "jeanfabre",
            "id": 1140265
        }
    }
}

metadata
{
"__ECO__":"__PACKAGE__",
"Type":"Unity Module",
"UnityModules":["UI"],
"Version":"1.0.0",
"UnityMinimumVersion":"4.6.0",
"PlayMakerMinimumVersion":"1.7.0",
"unitypackage":"PlayMaker/Ecosystem/Custom Packages/uGui/uGuiProxy.unitypackage",
"pingAssetPath":"Assets/PlayMaker uGui/PlayMakerUGuiComponentProxy.cs",
"YoutubeVideos":["http://www.youtube.com/playlist?list=PLFXYYxmSM-Ge4Qxx80EnT6xiLHQXKGHW3"]
}
*/
	
	public class Item {

		#region Enums
		public enum ItemTypes {Action,Template,Sample,Package};
		public enum RepositoryTypes {Github,Snipt};

		public enum AsynchContentStatus {Pending,Downloading,Available,Unavailable};

		public enum urltypes {RestDownload,Preview,Raw,YouTube};


		#endregion

		#region Define


		// action screenshots is generated by PlayMaker in one folder, so we'll stick to this, it's enough to have to do the skin dance between dark and light
		// 0-> repository FullPath
		// 1-> skin
		// 2-> filename
		static string __ActionScreenShotUrlFormat__ = "https://github.com/{0}/raw/master/PlayMaker/Screenshots/{1}/{2}";

		// other items will have their doc as a relative path to the package itself, which will be easier to deal with I suspect since it's not in the Assets folder itself anyway.
		// maybe the meta data should be able to overide this to share docs? maybe...
		// but taking screenshots of inspector is tricky...
		// 0 -> Repository FullPath
		// 1-> root Path
		// 2-> skin
		// 3-> filename
		static string __PackageScreenShotUrlFormat__ = "https://github.com/{0}/raw/master/{1}/Documentation/Screenshots/{2}/{3}";

		#endregion Define

		#region Cache 

		static Dictionary<string,Texture> DocumentationImage_Cache = new Dictionary<string, Texture>();

		#endregion Cache

		#region Interface

		public Item(Hashtable _rawData)
		{
			RawData = _rawData;

			// process properties guaranteed to be used
			switch ((string)_rawData["type"])
			{
			case "Action":
				_Type = ItemTypes.Action;
				break;
			case "Template":
				_Type = ItemTypes.Template;
				break;
			case "Sample":
				_Type = ItemTypes.Sample;
				break;
			case "Package":
				_Type = ItemTypes.Package;
				break;
			} 
			_Name =	(string)RawData["name"];
			_PrettyName = (string)RawData["pretty name"];
			_Path = (string)RawData["path"];

			_FolderPath = _Path.Substring(0,_Path.LastIndexOf('/'));

			Hashtable rep = (Hashtable)RawData["repository"];

			switch ((string)rep["type"])
			{
			case "Github":
				_RepositoryType = RepositoryTypes.Github;
				break;
			case "Snipt":
				_RepositoryType = RepositoryTypes.Snipt;
				break;
			} 


			_RepositoryFullNamePath = (string)rep["full_name"];
		//	Debug.Log("_RepositoryFullNamePath:"+_RepositoryFullNamePath);
			//...
		}

		public override string ToString()
		{
			return "Item";
		}


		public Texture DocumentationImage
		{
			get{ 

				if (DocumentationImage_Cache.ContainsKey(DocumentationImageUrl))
				{
					return DocumentationImage_Cache[DocumentationImageUrl]; 
				}else
				{
					EditorCoroutine.start(LoadDocumentationImage());
				}

				return null;
			}
		}
		#endregion Interface


		#region Public Properties
		public Hashtable RawData;

		ItemTypes _Type;
		public ItemTypes Type
		{
			get{
				return _Type;
			}
		}

		string _Name;
		public string Name
		{
			get{
				return _Name;
			}
		}
		string _PrettyName;
		public string PrettyName
		{
			get{
				return _PrettyName;
			}
		}


		string _FolderPath;
		public string FolderPath
		{
			get{
				return _FolderPath;
			}
		}

		string _Path;
		public string Path
		{
			get{
				return _Path;
			}
		}

		RepositoryTypes _RepositoryType;
		public RepositoryTypes RepositoryType
		{
			get{
				return _RepositoryType;
			}
		}

		public string UrlUid;


		string _RepositoryFullNamePath;
		/*
		public string RepositoryFullNamePath
		{
			get{
				return _RepositoryFullNamePath;
			}
		}
		*/

		public AsynchContentStatus DocumentationImageStatus = AsynchContentStatus.Pending;
		public AsynchContentStatus MetaDataStatus = AsynchContentStatus.Pending;

		bool _hasVideo;
		public bool HasVideo
		{
			get{
				return _hasVideo;
			}
		}


		#endregion

		#region Private Properties

		string DocumentationImageUrl;

		#endregion


		#region Public Methods

		/// <summary>
		/// Go over the metaData if exists and extract informations for direct access. Will reset related properties
		/// </summary>
		public void ProcessMetaData()
		{
			_hasVideo = false;

			if (!RawData.ContainsKey("metaData"))
			{
				Debug.Log("ProcessMetaData: metaData key not found in RawData");
				return;
			}

			Hashtable metaData = (Hashtable)RawData["metaData"];
			if (metaData!=null)
			{
				_hasVideo = metaData.ContainsKey("YoutubeVideos");
			}
		}
		/// <summary>
		/// Return a url for this item within a specific context, github, rest.
		/// </summary>
		/// <returns>The URL</returns>
		/// <param name="urlType">URL type.</param>
		public string GetUrl(urltypes urlType,params object[] parameters)
		{

			if (urlType == urltypes.YouTube)
			{
				int index = 0;

				if (parameters.Length>0)
				{
					index = Mathf.Max(0,(int)parameters[0]);
				}


				Hashtable metaData = (Hashtable)RawData["metaData"];
				if (metaData!=null)
				{
					ArrayList _list = (ArrayList)metaData["YoutubeVideos"];
					if (_list!=null && _list.Count>index)
						return (string)_list[index];
				}
				return null;
			}



			string itemPath = (string)RawData["path"];
			Hashtable rep = (Hashtable)RawData["repository"];



			if (_RepositoryType == RepositoryTypes.Github)
			{
				string itemPathEscaped = itemPath.Replace(" ","%20");
				string repositoryPath = (string)rep["full_name"];

				switch (urlType)
				{
				case urltypes.RestDownload:
					string url = EcosystemBrowser.__REST_URL_BASE__ +"download?type=Github&repository="+ Uri.EscapeDataString(repositoryPath)+"&file="+ Uri.EscapeDataString(itemPathEscaped);
					
					RawData["RepositoryRawUrl"] = url;
					
					return url;
					
				case urltypes.Raw:
					return "https://raw.github.com/"+ repositoryPath+"/master/"+ itemPathEscaped;
					
				case urltypes.Preview:
					return "https://github.com/"+ repositoryPath+"/blob/master/"+ itemPathEscaped;
				}


			}else if (_RepositoryType == RepositoryTypes.Snipt)
			{
				switch (urlType)
				{
				case urltypes.RestDownload:
					string url = EcosystemBrowser.__REST_URL_BASE__ +"download?type=Snipt&url="+ Uri.EscapeDataString((string)rep["raw_url"])+"&slug="+ Uri.EscapeDataString((string)RawData["slug"]);
					
					RawData["RepositoryRawUrl"] = url;
					
					return url;
					
				case urltypes.Raw:
					return (string)rep["raw_url"];
					
				case urltypes.Preview:
					return (string)rep["preview_url"];
				}
			}




		
			return null;
		}

		#endregion

		public delegate void ProcessLoadDocumentation();

		public void LoadDocumentation()
		{
			if (EcosystemBrowser.IsDebugOn) Debug.Log("LoadDocumentation for <"+Name+"> status:"+DocumentationImageStatus);

			if (DocumentationImageStatus == AsynchContentStatus.Pending)
			{
				EditorCoroutine.start(LoadDocumentationImage());
			}
			if(MetaDataStatus == AsynchContentStatus.Pending)
			{
				EditorCoroutine.start(LoadMetaData());
			}
		}

		/// <summary>
		/// Loads the meta data. output will be in json within the item raw data or the metadata property.
		/// Process is Asynchrone
		/// </summary>
		IEnumerator LoadMetaData()
		{
			if (EcosystemBrowser.IsDebugOn) Debug.Log("LoadMetaData for <"+Name+">");

			string url = GetUrl(urltypes.Raw);
			MetaDataStatus = AsynchContentStatus.Downloading;
			WWW _www = new WWW(url);
			while (!_www.isDone) yield return null;
			
			if (string.IsNullOrEmpty(_www.error))
			{
				MetaDataStatus = AsynchContentStatus.Available;

				string jsonString = _www.text;

				bool canDecode = true;
				if (Type== ItemTypes.Action)
				{
					// we have to find the meta data within the script
					Match match = Regex.Match(jsonString,@"(?<=EcoMetaStart)[^>]*(?=EcoMetaEnd)",RegexOptions.IgnoreCase);
					
					// Here we check the Match instance.
					if (match.Success)
					{
						//	Debug.Log("we have meta data :" + match.Value);
						jsonString = match.Value;
					}else{
						canDecode = false;
						MetaDataStatus = AsynchContentStatus.Unavailable;
					}
				}
			
				if (canDecode)
				{
					if (EcosystemBrowser.IsDebugOn)Debug.Log("decode attempt for item"+Name+" from url <"+url+"> content:"+jsonString);
					try{
						Hashtable _meta = (Hashtable)JSON.JsonDecode(jsonString);
						RawData["metaData"] = _meta;

						ProcessMetaData();
					}catch(Exception e)
					{
						MetaDataStatus = AsynchContentStatus.Unavailable;
						if (EcosystemBrowser.IsDebugOn) Debug.LogError(e);
						if (EcosystemBrowser.IsDebugOn) Debug.LogError("could not decode json string for item"+Name+" from url <"+url+"> content:"+jsonString);
					}
				}
			}else{
				if (EcosystemBrowser.IsDebugOn) Debug.LogError("LoadMetaData error for "+Name+" with url <"+url+"> : "+_www.error);
				MetaDataStatus = AsynchContentStatus.Unavailable;
			}

			yield break;
		}

		/// <summary>
		/// Loads the documentation image. Output will be saved in DocumentationImage_Cache
		/// Process is Asynchrone
		/// </summary>
		IEnumerator LoadDocumentationImage()
		{

			if (string.IsNullOrEmpty(DocumentationImageUrl))
			{
				string screenshotFileName;
				string skin = EditorGUIUtility.isProSkin?"Dark":"Light";

				if (_Type == ItemTypes.Action)
				{
					screenshotFileName = _Name.Replace(".cs",".png");
					DocumentationImageUrl = string.Format(__ActionScreenShotUrlFormat__,_RepositoryFullNamePath,skin,screenshotFileName);
				}else{
					screenshotFileName = _Name.Replace("."+_Type.ToString().ToLower()+".txt",".png");
					DocumentationImageUrl = string.Format(__PackageScreenShotUrlFormat__,_RepositoryFullNamePath,_FolderPath,skin,screenshotFileName);
				}
				DocumentationImageUrl = DocumentationImageUrl.Replace(" ","%20");

				//Debug.Log(DocumentationImageUrl);
			}
			 
			Debug.Log(_RepositoryFullNamePath);

			if (EcosystemBrowser.IsDebugOn) Debug.Log("LoadDocumentation for <"+Name+"> url:"+DocumentationImageUrl);

			DocumentationImageStatus = AsynchContentStatus.Downloading;
			WWW _www = new WWW(DocumentationImageUrl);
			while (!_www.isDone) yield return null;
			
			if (string.IsNullOrEmpty(_www.error))
			{
				DocumentationImageStatus = AsynchContentStatus.Available;
				DocumentationImage_Cache[DocumentationImageUrl] = _www.textureNonReadable;
			}else{
				if (EcosystemBrowser.IsDebugOn) Debug.LogWarning("LoadDocumentation error for "+Name+" : "+_www.error);
				DocumentationImageStatus = AsynchContentStatus.Unavailable;
			}

			yield break;
		}
	



	}

}
