using System;
using System.IO;
using System.Collections;
using System.Reflection;
using nft.util;
using System.Windows.Forms;
using nft.framework.plugin;
using System.Diagnostics;

namespace nft.framework
{
	public delegate void SaveDirChangedEventHandler(string newpath, string oldpath);
    /// <summary>
	/// Environment の概要の説明です。
	/// </summary>
	public class Directories
	{
		
		private Directories()
		{
		}

		static private string baseDir;
        static private string resDir; // static application resources
		static private string pluginDir;
		static private string dataDir;
		static private string settingDir;
		static private string defaultSaveDir;
		static private string workDir1;
        static private string currentSaveDir;

		static public void Initialize(Hashtable args)
		{
			// set application program existing path

			string current = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
			baseDir = getPath(args,"-B","-BASEDIR", current);
            string appname = GetAppFolderName();
            string appDataDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
            appDataDir = Path.Combine(appDataDir, appname);
            string localAppDataDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
            localAppDataDir = Path.Combine(localAppDataDir, appname);
            resDir = Path.Combine(baseDir, @"res\");
			GetOrCreate(resDir,false);
            dataDir = Path.Combine(appDataDir, @"data\");
			GetOrCreate(dataDir,false);
            defaultSaveDir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
			GetOrCreate(defaultSaveDir,false);
            workDir1 = Path.Combine(localAppDataDir, @"work\");
			GetOrCreate(workDir1,false);
            settingDir = getPath(args, "-S", "-SETTINGDIR", Path.Combine(appDataDir, @"settings\"));
            GetOrCreate(settingDir, false);
            pluginDir = getPath(args, "-P", "-PLUGINDIR", Path.Combine(baseDir, @"plugins\"));
            GetOrCreate(pluginDir, false);
            currentSaveDir = null;
		}

        static private string GetAppFolderName() {
            string ver = Application.ProductVersion;
            int i = ver.IndexOf('.',ver.IndexOf('.')+1);
            string ret = Path.Combine(Application.ProductName, ver.Substring(0,i));
            return ret;
        }

		static private string getPath(Hashtable args, string key1, string key2, string defaultPath)
		{
			string path="";
			try
			{
				if( args.ContainsKey(key1) )
				{
					path = args[key1].ToString();
					return GetOrCreate(path,false).FullName;
				}
				else if( args.ContainsKey(key2) )
				{
					path = args[key2].ToString();
					return GetOrCreate(path,false).FullName;
				}
			}
			catch(Exception e)
			{
                string msg = I18n.F("Failed to create a directory '{0}'. Use default path instead.", path);
				if(UIUtil.ShowException(msg,e,UIInformLevel.normal))
                    throw;
			}
			return defaultPath;
		}

        /// <summary>
        /// Utility function to calculate total size of files in the specific directory.
        /// </summary>
        /// <param name="dirInfo"></param>
        /// <returns></returns>
        public static long GetDirectorySize(string dirname) {
            DirectoryInfo info = new DirectoryInfo(dirname);
            return GetDirectorySize(info);
        }

        static private long GetDirectorySize(DirectoryInfo info) {
            long size = 0;
            foreach (FileInfo fi in info.GetFiles()) {
                size += fi.Length;
            }
            foreach (DirectoryInfo di in info.GetDirectories()) {
                size += GetDirectorySize(di);
            }
            return size;
        }

		/// <summary>
		/// returns specified DirectoryInfo, createing if not exist.
		/// (when prompt=true, and if user doesn't allow create directory, returns null.)
		/// </summary>
		/// <param name="dir">full path for the directory</param>
		/// <param name="prompt">show prompt before creating directory</param>
		/// <returns></returns>
		static public DirectoryInfo GetOrCreate(string dir, bool prompt)
		{
			if(!Directory.Exists(dir))
			{
				if(prompt)
				{
                    string msg = I18n.F("Directory '{0}' is not exist. Do you want to create?",dir);
					if(!UIUtil.YesNoMessage(msg))
						return null;
				}
				Directory.CreateDirectory(dir);
			}
			return new DirectoryInfo(dir);
		}

		static public string AppBaseDir
		{
			get{ return baseDir; }
		}
        /// <summary>
        /// Directory which contanins static application resources
        /// </summary>
		static public string SystemResourceDir
		{
			get{ return resDir; }
		}
        /// <summary>
        /// Directroy where contains plug-ins
        /// </summary>
		static public string PluginDir
		{
			get{ return pluginDir;	}
		}
        /// <summary>
        /// Drectory where application data will be stored.
        /// Mainly used for cache data of plug-ins or longlife(game independent) resource data.
        /// </summary>
		static public string DataDir
		{
			get{ return dataDir;	}
		}
        /// <summary>
        /// Directory where application settings are stored.
        /// </summary>
		static public string SettingDir
		{
			get{ return settingDir;	}
			set{ settingDir = value; }
		}
        /// <summary>
        /// Directory where game data will be cached.
        /// Recommended for cache directory associated to current game.
        /// </summary>
		static public string WorkDir
		{
			get{ return workDir1;	}
			set{ workDir1 = value; }
		}

        /// <summary>
        /// Default game saving directory.
        /// </summary>
		static public string SaveDirRoot
		{
			get{ return defaultSaveDir;	}
			set{ defaultSaveDir = value; }
		}

        static public event SaveDirChangedEventHandler SaveDirChanged;
        static public string CurrentSaveDir {
            get { return currentSaveDir; }
            set {
                string old = currentSaveDir;
                currentSaveDir = value;
                if (SaveDirChanged != null)
                    SaveDirChanged(currentSaveDir, old);
            }

        }

        static private readonly string subdir_classes = "classes\\";
        static private readonly string subdir_plugins = "plguins\\";

        /// <summary>
        /// retruns diretory path under the DataDir, which name based on some object.
        /// base object is recommended to be an instance of Plugin or Contribution.
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        static public string MakeDataDirNameFor(Object obj)
        {
            string offset = MakeSubDirFor(obj);
            string ret = Path.Combine(dataDir,offset);
            Directory.CreateDirectory(ret);
            return ret;
        }
        /// <summary>
        /// retruns diretory path under the  WorkDir, which name based on some object.
        /// base object is recommended to be an instance of Plugin or Contribution.
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        static public string MakeWorkDirNameFor(Object obj)
        {
            string offset = MakeSubDirFor(obj);
            string ret = Path.Combine(workDir1, offset);
            Directory.CreateDirectory(ret);
            return ret;
        }
        /// <summary>
        /// retruns diretory path under the SettingDir, which name based on some object.
        /// base object is recommended to be an instance of Plugin or Contribution.
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        static public string MakeSettingDirNameFor(Object obj)
        {
            string offset = MakeSubDirFor(obj);
            string ret = Path.Combine(settingDir, offset);
            return ret;
        }

        static protected string MakeSubDirFor(Object o)
        {
            if (o == null)
            {
                return "";
            }
            else
            {
                Contribution c = o as Contribution;
                if (c != null)
                {
                    VContributionId cid = c.ContributionID;
                    return subdir_plugins + Path.Combine(cid.PluginID, cid.SubID);
                }
                else
                {
                    Plugin p = o as Plugin;
                    if (p != null)
                    {
                        return subdir_plugins + p.ID;
                    }
                    else {
                        Type t = o as Type;
                        if (t == null)
                        {
                            t = o.GetType();
                        }
                        return subdir_classes + t.FullName;
                    }                    
                    
                }
            }
        }
	}
}
