using System;
using System.Diagnostics;
using System.IO;

namespace AMXXRelease
{
	//This class specifies the process that builds a release.
	//It also implements the functions that are unlikely to change from mod to mod.
	public abstract class ABuilder
	{
		protected Config m_Cfg;

		public virtual void OnBuild()
		{
		}

		public virtual void CreateDir(string dir)
		{
			Directory.CreateDirectory(dir);
		}

		public virtual bool Build(Config cfg, Build build)
		{
			m_Cfg = cfg;

			OnBuild();
			
			int num = build.GetMods();

			AMod mod;
			for (int i=0; i<num; i++)
			{
				mod = build.GetMod(i);
				if (!BuildMod(mod))
				{
					System.Console.WriteLine("Mod failed to build: " + mod.GetName());
					return false;
				}
				CompressDir(
					PropSlashes(m_Cfg.OutputPath() + "\\" + m_Cfg.GetReleaseName() + "-" + mod.GetName()), 
					PropSlashes(m_Cfg.OutputPath() + "\\" + mod.GetName())
					);
			}

			return true;
		}

		public virtual bool BuildMod(AMod mod)
		{
			CopyConfigs(mod);
			if (!BuildModModules(mod))
				return false;
			if (!BuildModPlugins(mod))
				return false;
			
			string basedir = PropSlashes(m_Cfg.OutputPath() + "\\" + mod.GetModPath());
			string sourcetree = m_Cfg.GetSourceTree();

			if (!mod.CopyExtraFiles(this, basedir, sourcetree))
				return false;
			
			return true;
		}

		public virtual void CopyConfigs(AMod mod)
		{
			string basedir = PropSlashes(m_Cfg.OutputPath() + "\\" + mod.GetModPath() + "\\configs");

			if (!Directory.Exists(basedir))
				CreateDir(basedir);

			string srcdir = PropSlashes(m_Cfg.GetSourceTree() + "\\configs");

			if (!Directory.Exists(srcdir))
				return;

			if (mod.GetPluginDir() != null)
				srcdir += "\\" + mod.GetBaseName();

			srcdir = PropSlashes(srcdir);

			CopyNormal(this, srcdir, basedir);
		}

		public static void CopyNormal(ABuilder ab, string src, string dest)
		{
			string[] files = Directory.GetFiles(src);

			if (!Directory.Exists(dest))
				ab.CreateDir(dest);

			for (int i=0; i<files.Length; i++)
			{
				File.Copy(files[i],
						  PropSlashes(dest + "\\" + GetFileName(files[i])),
						  true);
			}
		}

		public virtual bool BuildModPlugins(AMod mod)
		{
			int num = mod.GetPlugins();

			Plugin plugin;
			string binary, basedir;

			basedir = m_Cfg.OutputPath();
			basedir += "\\" + mod.GetModPath();
			basedir = PropSlashes(basedir);

			string dir, file, target;
			for (int i=0; i<num; i++)
			{
				plugin = mod.GetPlugin(i);
				binary = BuildPlugin(mod, plugin);
				file = PropSlashes(m_Cfg.GetSourceTree() + "\\plugins\\" + GetFileName(binary));
				if (!File.Exists(file))
				{
					System.Console.WriteLine("Plugin failed to compile: " + 
						mod.GetName() + "::" + plugin.name);
					return false;
				}
				dir = PropSlashes(basedir + "\\" + plugin.outdir);
				if (!Directory.Exists(dir))
					CreateDir(dir);
				target = PropSlashes(dir + "\\" + plugin.name + ".amxx");
				if (File.Exists(target))
					File.Delete(target);
				File.Move(file,
					target);
			}

			//Copy all files from the plugins dir to scripting
			
			string search_dir = m_Cfg.GetSourceTree() + "\\plugins";
			if (mod.GetPluginDir() != null)
				search_dir += "\\" + mod.GetPluginDir();
			search_dir = PropSlashes(search_dir);

			string dest;
			if (Directory.Exists(search_dir))
			{
				string[] files = Directory.GetFiles(search_dir);
				if (!Directory.Exists(PropSlashes(basedir + "\\scripting")))
					CreateDir(PropSlashes(basedir + "\\scripting"));
				for (int i=0; i<files.Length; i++)
				{
					dest = PropSlashes(basedir + "\\scripting\\" + GetFileName(files[i]));
					if (mod.ExcludeCopy(files[i]))
						continue;
					File.Copy(files[i], dest, true);
				}
			}

			return true;
		}

		public static string GetFileName(string input)
		{
			for (int i=input.Length-1; i>=0; i--)
			{
				if ((input[i] == '\\' || input[i] == '/') && i != input.Length-1)
				{
					return input.Substring(i+1, input.Length-i-1);
				}
			}

			return input;
		}

		public virtual bool BuildModModules(AMod mod)
		{
			int num = mod.GetModules();

			Module module;
			string binary, basedir;

			basedir = m_Cfg.OutputPath();
			basedir += "\\" + mod.GetModPath();
			basedir = PropSlashes(basedir);

			string dir;
			for (int i=0; i<num; i++)
			{
				module = mod.GetModule(i);
				binary = BuildModule(module);

				if (binary == null)
				{
					System.Console.WriteLine("Module failed to compile: " + 
						mod.GetName() + "::" + module.projname + GetLibExt());	
					return false;
				}
				dir = PropSlashes(basedir + "\\" + module.outdir);
				if (!Directory.Exists(dir))
					CreateDir(dir);
				File.Copy(binary,
					PropSlashes(dir + "\\" + module.projname + GetLibExt()),
					true);
			}

			return true;
		}

		public virtual string BuildPlugin(AMod mod, Plugin pl)
		{
			string modoffs = mod.GetPluginDir();
			string pldir;

			if (modoffs != null)
				pldir = modoffs + "\\";
			else
				pldir = "";

			AmxxPc(PropSlashes(pldir + pl.source), pl.options);

			string outfile = pldir + pl.name + ".amxx";

			return outfile;
		}

		public abstract void AmxxPc(string inpath, string args);

		public abstract string BuildModule(Module module);

		public abstract string GetLibExt();

		public abstract void CompressDir(string target, string dir);

		public static string PropSlashes(string path)
		{
			char sep;
			char alt;
			if (Releaser.IsWindows)
			{
				sep = '\\';
				alt = '/';
			} else {
				sep = '/';
				alt = '\\';
			}
			return path.Replace(alt,sep);
		}
	}
}