using System;
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.CodeDom.Compiler;
using System.Threading;

using MonoDevelop.Core;
using MonoDevelop.Projects;
using MonoDevelop.Core.Gui.Components;

namespace NemerleBinding
{
	public class NemerleBindingCompilerServices
	{
		class CompilerResultsParser : CompilerResults
		{
			public CompilerResultsParser() : base (new TempFileCollection ())
			{
			}
			
			bool SetErrorType(CompilerError error, string t)
			{
				switch(t.Trim ())
				{
					case "error":
						error.IsWarning = false;
						return true;
					case "warning":
						error.IsWarning = true;
						return true;
					case "hint":
						error.IsWarning = true;
						return true;
					default:
						return false;
				}
			}

			public void Parse(string l)
			{
				CompilerError error = new CompilerError();
				error.ErrorNumber = String.Empty;

				char [] delim = {':'};
				string [] s = l.Split(delim, 7);
				
				try
				{
				    SetErrorType (error, s[5]);
				    if (s[6].StartsWith ("N") && s[6].Contains (": "))
				    {
				        string[] e = s[6].Split (delim, 2);
				        error.ErrorNumber = s[0];
				        error.ErrorText = s[1].Trim ();
				    }
				    else
				        error.ErrorText = s[6].Trim ();
				    error.FileName = s[0];
				    error.Line = int.Parse(s[1]);
				    error.Column = int.Parse(s[2]);
				}
				catch
				{
				    SetErrorType (error, s[0]);
				    error.ErrorText = s[1].Trim ();
				    error.FileName = "";
				    error.Line = 0;
				    error.Column = 0;
				}
				
				/*if (SetErrorType(error, s[5]))
				{
					error.ErrorText = s[6]; // l.Substring(l.IndexOf(s[0]+": ") + s[0].Length+2);
					error.FileName  = "";
					error.Line      = 0;
					error.Column    = 0;
				} else
				if ((s.Length >= 4)  && SetErrorType(error, s[3].Substring(1)))
				{
					error.ErrorText = l.Substring(l.IndexOf(s[3]+": ") + s[3].Length+2);
					error.FileName  = s[0];
					error.Line      = int.Parse(s[1]);
					error.Column    = int.Parse(s[2]);
				} else
				{
					error.ErrorText = l;
					error.FileName  = "";
					error.Line      = 0;
					error.Column    = 0;
					error.IsWarning = false;					
				}*/
				Errors.Add(error);
			}

			public ICompilerResult GetResult()
			{
				return new DefaultCompilerResult(this, "");
			} 
		}
	
		static string ncc = "ncc";

		private string GetOptionsString (DotNetProjectConfiguration configuration, NemerleParameters cp)
		{
			string options = " ";
			if (cp.Nostdmacros)
				options += " -no-stdmacros";
			if (cp.Nostdlib)
				options += " -no-stdlib";
			if (cp.Ot)
				options += " -Ot";
			if (cp.Greedy)
				options += " -greedy";
			if (cp.Pedantic)
				options += " -pedantic-lexer";
			if (configuration.CompileTarget == CompileTarget.Library)
				options += " -tdll";
				
			return options;			
		}

		public bool CanCompile(string fileName)
		{
			return (System.IO.Path.GetExtension(fileName).ToLower() == ".n");
		} 

		public ICompilerResult Compile (ProjectFileCollection projectFiles, ProjectReferenceCollection projectReferences, DotNetProjectConfiguration configuration, IProgressMonitor monitor)
		{
			NemerleParameters cp = (NemerleParameters) configuration.CompilationParameters;
			if (cp == null) cp = new NemerleParameters ();
			
			string references = "";
			string files   = "";
			
			foreach (ProjectReference lib in projectReferences)
				foreach (string a in lib.GetReferencedFileNames())
					references += " -r \"" + a + "\"";
			
			foreach (ProjectFile f in projectFiles)
				if (f.Subtype != Subtype.Directory)
					switch (f.BuildAction)
					{
						case BuildAction.Compile:
							files += " \"" + f.Name + "\"";
						break;
					}

			if (!Directory.Exists (configuration.OutputDirectory))
				Directory.CreateDirectory (configuration.OutputDirectory);
			
			string args = "-q -no-color " + GetOptionsString (configuration, cp) + references + files  + " -o " + configuration.CompiledOutputName;
			return DoCompilation (args);
		}
		
		// This enables check if we have output without blocking 
		class VProcess : Process
		{
			Thread t = null;
			public void thr()
			{
				while (StandardOutput.Peek() == -1){};
			}
			public void OutWatch()
			{
				t = new Thread(new ThreadStart(thr));
				t.Start();
			}
			public bool HasNoOut()
			{
				return t.IsAlive;
			} 
		}
		
		private ICompilerResult DoCompilation(string arguments)
		{
			string l;
			ProcessStartInfo si = new ProcessStartInfo(ncc, arguments);
			si.RedirectStandardOutput = true;
			si.RedirectStandardError = true;
			si.UseShellExecute = false;
			VProcess p = new VProcess();
			p.StartInfo = si;
			p.Start();

			p.OutWatch();
			while ((!p.HasExited) && p.HasNoOut())
//			while ((!p.HasExited) && (p.StandardOutput.Peek() == -1)) // this could eliminate VProcess outgrowth
			{
				System.Threading.Thread.Sleep (100);
			}
			
			CompilerResultsParser cr = new CompilerResultsParser();	
			while ((l = p.StandardOutput.ReadLine()) != null)
			{
				cr.Parse(l);
			}
			
			if  ((l = p.StandardError.ReadLine()) != null)
			{
				cr.Parse("error: " + ncc + " execution problem");
			}
			
			return cr.GetResult();
		}
	}
}
