// A Qt to C# binding generator.
//
// Copyright (C) 2002  Adam Treat (manyoso@yahoo.com)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

using System;
using System.IO;
using System.Text;
using System.Collections;

namespace QtCSharp {

	public class Printer {

		QType qtype;
		string tab, directory;

		public Printer (QType qtype, string directory)
		{
			this.qtype = qtype;
			this.directory = directory;
			tab = "\t";
			if (qtype.IsInterface)
				PrintInterface ();
			else
				Print ();
		}

		public void Print ()
		{
			StreamWriter writer = new StreamWriter(directory + Path.DirectorySeparatorChar +
				qtype.Name + ".cs", false, new ASCIIEncoding());
			writer.Write(Header);
			writer.Write(StartClass(qtype.Name, qtype.Access, new ArrayList(qtype.QAncestors)));

			foreach (QEnum qenum in qtype.QEnums) {
				writer.Write(StartEnum(qenum.Name, qenum.Access, qenum.Type));
				writer.Write(Items(qenum.QItems));
				writer.Write(EndMember);
			}

			foreach (QCtor qctor in qtype.QCtors) {
				if (qctor.Throttle) {
				} else if (qctor.Inherited) {
					writer.Write (WriteInherited (qctor.Name, qctor.Access));
				} else if (qctor.Dummy) {
					writer.Write (WriteDummy (qctor.Name, qctor.Access));
				} else if (qctor.Boxer) {
					writer.Write (WriteBoxer (qctor.Name, qctor.Access));
				} else if (!qctor.Overload) {
					writer.Write (ImportAttrib);
					writer.Write (ImportCtorCall (qctor.Name, qctor.Id, qctor.PinvokeParams));
					writer.Write (StartCtor (qctor.Name, qctor.Access, qctor.CSharpParams));
					writer.Write (RawObject (qctor.Name, qctor.Id, qctor.PinvokeCallParams));
					writer.Write (CheckParent (qctor.Parent));
					writer.Write (Register);
					writer.Write (DefaultConnections ());
					writer.Write (EndMember);
				} else {
					writer.Write (OverLoadCtor(qctor.Name, qctor.Access, qctor.CSharpParams, qctor.OverloadParams));
				}
			}

			writer.Write (StartDCtor (qtype.Name));
			writer.Write (DCtorCall);
			writer.Write (EndMember);

			//writer.Write (DisposePublic ());

			foreach (QDCtor qdctor in qtype.QDCtors) {
				writer.Write (ImportAttrib);
				writer.Write (ImportDCtorCall (qdctor.Name));
			}

			//writer.Write (DisposeProtected ());
			writer.Write (Delete ());

			foreach (QMethod qmethod in qtype.QMethods) {
				if (qmethod.Throttle) {
				} else if (!qmethod.Overload) {
					writer.Write(ImportAttrib);
					if (qmethod.Access == "public" || qmethod.Access == "protected" || qmethod.Access == "internal") {
						writer.Write(ImportMethodCall(qtype.Name, qmethod.Name, qmethod.Id, qmethod.PinvokeReturn, qmethod.PinvokeParams));
						writer.Write(StartMethod(qmethod.PascalName, qmethod.Access, qmethod.Return, qmethod.CSharpParams));
						writer.Write(MethodCall(qmethod.QStringReturn, qmethod.Boxer, qtype.Name, qmethod.Name, "rawObject", qmethod.Id, qmethod.Return, qmethod.PinvokeCallParams));
					} else {
						writer.Write(StaticImportMethodCall(qtype.Name, qmethod.Name, qmethod.Id, qmethod.PinvokeReturn, qmethod.PinvokeParams));
						writer.Write(StartMethod(qmethod.PascalName, qmethod.Access, qmethod.Return, qmethod.CSharpParams));
						writer.Write(StaticMethodCall(qmethod.QStringReturn, qmethod.Boxer, qtype.Name, qmethod.Name, qmethod.Id, qmethod.Return, qmethod.PinvokeCallParams));
					}
					writer.Write(EndMember);
				} else {
					writer.Write(OverLoadMethod(qmethod.PascalName, qmethod.Access, qmethod.Return, qmethod.CSharpParams, qmethod.OverloadParams));
				}
			}

			foreach (QAncestor qancestor in qtype.QAncestors) {
				writer.Write("\n\n"+tab+tab+"// Begin interface methods.\n");
				if (qancestor.IsInterface) {
					Printer printer = new Printer (qancestor, directory);
					foreach (QMethod qmethod in qancestor.QMethods) {
						if (qmethod.Throttle) {
						} else if (!qmethod.Overload) {
							writer.Write(ImportAttrib);
							if (qmethod.Access == "public" || qmethod.Access == "protected") {
								writer.Write(ImportMethodCall(qancestor.Name, qmethod.Name, qmethod.Id, qmethod.PinvokeReturn, qmethod.PinvokeParams));
								writer.Write(StartMethod(qmethod.PascalName, qmethod.Access, qmethod.Return, qmethod.CSharpParams));
								writer.Write(MethodCall(qmethod.QStringReturn, qmethod.Boxer, qancestor.Name, qmethod.Name, qancestor.Name+" ()", qmethod.Id, qmethod.Return, qmethod.PinvokeCallParams));
							} else {
								writer.Write(StaticImportMethodCall(qancestor.Name, qmethod.Name, qmethod.Id, qmethod.PinvokeReturn, qmethod.PinvokeParams));
								writer.Write(StartMethod(qmethod.PascalName, qmethod.Access, qmethod.Return, qmethod.CSharpParams));
								writer.Write(StaticMethodCall(qmethod.QStringReturn, qmethod.Boxer, qancestor.Name, qmethod.Name, qmethod.Id, qmethod.Return, qmethod.PinvokeCallParams));
							}
							writer.Write(EndMember);
						} else {
							writer.Write(OverLoadMethod(qmethod.PascalName, qmethod.Access, qmethod.Return, qmethod.CSharpParams, qmethod.OverloadParams));
						}
					}
				}
			}

			writer.Write(EndClass);
			writer.Write(Footer);
			writer.Close();
		}

		public void PrintInterface ()
		{
			StreamWriter writer = new StreamWriter(directory + Path.DirectorySeparatorChar +
				qtype.IName + ".cs", false, new ASCIIEncoding());
			writer.Write(Header);
			writer.Write(StartClass(qtype.IName, "public", qtype.QAncestors));

			foreach (QMethod qmethod in qtype.QMethods) {
				if (qmethod.Throttle || qmethod.Access == "public static" || qmethod.Access == "protected") {
				} else {
					writer.Write(IMethod(qmethod.PascalName, qmethod.Return, qmethod.CSharpParams));
				}
			}
			writer.Write("\n"+tab+tab+" IntPtr "+qtype.Name+" ();");
			writer.Write(EndClass);
			writer.Write(Footer);
			writer.Close();
		}

		public string StartClass (string name, string access, ArrayList qancestors)
		{
			StringBuilder sb = new StringBuilder ();

			if (qtype.IsInterface) {
				sb.Append(tab+access+" interface "+name+" {");
				return sb.ToString ();
			}

			sb.Append(tab+access+" class "+name+" : ");
			if (qancestors.Count == 0) sb.Append ("QtSupport");
			foreach (QAncestor qancestor in qancestors) {
				if (!qancestor.IsInterface) {
					if (qancestor.Name == "Qt") {
						sb.Append ("QtSupport");
						break;
					} else {
						sb.Append (qancestor.Name);
						qancestors.Remove (qancestor);
						break;
					}
				}
			}

			foreach (QAncestor qancestor in qancestors) {
				if (qancestor.IsInterface) {
						sb.Append (", "+qancestor.IName);
				}
			}

			sb.Append (", IDisposable {");
			return sb.ToString ();
		}

		public string EndClass
		{
			get {return "\n"+tab+"}";}
		}

		public string StartEnum (string name, string access, string type)
		{
			if (type != null)
				return 	"\n\n"+tab+tab+access+" enum "+name+" : "+type+" {";
			else
				return 	"\n\n"+tab+tab+access+" enum "+name+" {";
		}

		public string WriteInherited (string name, string access)
		{
			return 	"\n\n"+tab+tab+access+" "+name+" () : this (QNull.Instance) {}";
		}

		public string WriteDummy (string name, string access)
		{
			return 	"\n\n"+tab+tab+access+" "+name+" (QNull dummy) : base (QNull.Instance) {}";
		}

		public string WriteBoxer (string name, string access)
		{
			return 	"\n\n" +
			        tab+tab+access+" "+name+" (IntPtr ptr) : this (QNull.Instance)\n"+tab+tab+"{\n" +
			        tab+tab+tab+"rawObject = ptr;\n" +
					tab+tab+tab+"RegisterObject(this);\n" +
					tab+tab+"}";
		}

		public string CheckParent (bool check)
		{
			if (check)
				return "\n\n\t\t\tif ((qparent = parent) != null)\n" +
					   "\t\t\t\tqparent.AddChild (this);\n";
			else
				return "";
		}

		public string StartCtor (string name, string access, ArrayList csharpparams)
		{
			return 	"\n"+tab+tab+access+" "+name+" ("+Params(csharpparams)+") : this (QNull.Instance)\n"+tab+tab+"{\n";
		}

		public string StartDCtor (string name)
		{
			return 	"\n\n"+tab+tab+"~"+name+" ()\n"+tab+tab+"{\n";
		}

		public string StartMethod (string name, string access, string returntype, ArrayList csharpparams)
		{
			string str = "\n"+tab+tab+access+" "+returntype+" "+name+" ("+Params(csharpparams)+")\n"+tab+tab+"{\n";

			// QEvents should never be disposed inside C#
			if (access.IndexOf ("static") <= 0)
				str += "\t\t\tif (disposed)\n\t\t\t\tthrow new ObjectDisposedException (this+\": Attempted use of disposed object\");\n\n";

			return str;
		}

		public string IMethod (string name, string returntype, ArrayList csharpparams)
		{
			return 	"\n"+tab+tab+" "+returntype+" "+name+" ("+Params(csharpparams)+");";
		}

		public string EndMember
		{
			get {return "\n"+tab+tab+"}";}
		}

		public string OverLoadCtor (string name, string access, ArrayList csharpparams, ArrayList overloadparams)
		{
			return 	"\n\n"+tab+tab+access+" "+name+" ("+Params(csharpparams)+") : this ("+Params(overloadparams)+") {}";
		}

		public string OverLoadMethod (string name, string access, string returntype, ArrayList csharpparams, ArrayList overloadparams)
		{
			string str;

			if (returntype == "void")
				str = name + "(" + Params(overloadparams) + ");\n" + tab + tab;
			else
				str = "return "+name+"("+Params(overloadparams)+");\n" + tab+tab;

			return "\n\n"+tab+tab+access+" "+returntype+" "+name +
				" ("+Params(csharpparams)+")\n" + tab+tab+"{\n" + tab+tab+tab+ str + "}";
		}

		public string Items (ArrayList qitems)
		{
			int count = 0;
			StringBuilder sb = new StringBuilder();
			foreach (QItem qitem in qitems) {
				count++;
				sb.Append("\n"+tab+tab+tab+qitem.Name+" = "+qitem.Value);
				if (count != qitems.Count)
					sb.Append(",");
			}
			return sb.ToString();
		}

		public string Params (ArrayList qparams)
		{
			int count = 0;
			StringBuilder sb = new StringBuilder();
			foreach (QParam qparam in qparams) {
				count++;
				if (qparam.Type != "")
					sb.Append(qparam.Type+" "+qparam.Name);
				else
					sb.Append(qparam.Name);

				if (count != qparams.Count)
					sb.Append(", ");
			}
			return sb.ToString();
		}

		public string ImportCtorCall (string name, string id, ArrayList qparams)
		{
			if (id != "0")
				return "\n"+tab+tab+"private static extern IntPtr qt_new_"+name+id+" ("+Params(qparams)+");";
			else
				return "\n"+tab+tab+"private static extern IntPtr qt_new_"+name+" ("+Params(qparams)+");";
		}

		public string ImportDCtorCall (string name)
		{
			return "\n"+tab+tab+"private static extern void qt_del_"+name+" (IntPtr raw);";
		}

		public string ImportMethodCall (string type, string name, string id, string returntype, ArrayList qparams)
		{
			if (name.StartsWith ("Q_"))
				name = name.Replace ("Q_", "");
			string comma = "";
			if (qparams.Count != 0) comma = ", ";
			if (id != "0")
				return "\n"+tab+tab+"private static extern "+returntype+" qt_"+type+"_"+name+id+" (IntPtr raw"+comma+Params(qparams)+");";
			else
				return "\n"+tab+tab+"private static extern "+returntype+" qt_"+type+"_"+name+" (IntPtr raw"+comma+Params(qparams)+");";
		}

		public string StaticImportMethodCall (string type, string name, string id, string returntype, ArrayList qparams)
		{
			if (name.StartsWith ("Q_"))
				name = name.Replace ("Q_", "");
			if (id != "0")
				return "\n"+tab+tab+"private static extern "+returntype+" qt_"+type+"_"+name+id+" ("+Params(qparams)+");";
			else
				return "\n"+tab+tab+"private static extern "+returntype+" qt_"+type+"_"+name+" ("+Params(qparams)+");";
		}

		public string RawObject (string name, string id, ArrayList qparams)
		{
			if (qtype.IsQObject) {
				ArrayList newparams = new ArrayList ();

				foreach (QParam parm in qparams) {
					//Console.WriteLine ("--> {0}", parm.Name);
					if (parm.Name == "parent.RawObject") {
						QParam newparm = parm.Clone() as QParam;
						newparm.Name = "parent != null ? parent.RawObject : IntPtr.Zero";
						newparams.Add (newparm);
					}
					else
						newparams.Add (parm);
				}

				if (id != "0")
					return tab+tab+tab+"rawObject = qt_new_"+name+id+" ("+Params(newparams)+");";
				else
					return tab+tab+tab+"rawObject = qt_new_"+name+" ("+Params(newparams)+");";
			}
			else {
				if (id != "0")
					return tab+tab+tab+"rawObject = qt_new_"+name+id+" ("+Params(qparams)+");";
				else
					return tab+tab+tab+"rawObject = qt_new_"+name+" ("+Params(qparams)+");";
			}
		}

		public string DCtorCall {
			get {
				return tab+tab+tab+"Dispose (false);";
			}
		}

		public string MethodCall (bool qstring, bool boxer, string type, string name, string instPtr, string id, string ReturnType, ArrayList qparams)
		{
			if (name.StartsWith ("Q_"))
				name = name.Replace ("Q_", "");

			string ret = "";
			string comma = qparams.Count == 0 ? "" : ", ";
			string newid = id == "0" ? "" : id;

			if (boxer && qstring) {
				ret = "\t\t\treturn new QString (qt_"+type+"_"+name+newid+" ("+instPtr+comma+Params (qparams)+"));\n";
			}
			else if (boxer) {
				ret = "\t\t\treturn LookupObject (qt_"+type+"_"+name+newid+" ("+instPtr+comma+Params (qparams)+"), typeof ("+ReturnType+")) as "+ReturnType+";";
			}
			else {
				ret = "qt_"+type+"_"+name+newid+" ("+instPtr+comma+Params (qparams)+");";

				if (ReturnType != "void")
					ret = "return " + ret;

				ret = "\t\t\t" + ret;
			}

			return ret;
		}

		public string StaticMethodCall (bool qstring, bool boxer, string type, string name, string id, string ReturnType, ArrayList qparams)
		{
			if (name.StartsWith ("Q_"))
				name = name.Replace ("Q_", "");

			string ret;
			string newid = id == "0" ? "" : id;

			if (boxer && qstring) {
				ret = "\t\t\treturn new QString (qt_"+type+"_"+name+newid+" ("+Params (qparams)+"));\n";
			}
			else if (boxer) {
				ret = "\t\t\treturn LookupObject (qt_"+type+"_"+name+newid+" ("+Params (qparams)+"), typeof ("+ReturnType+")) as "+ReturnType+";\n";
			}
			else {
				ret = "qt_"+type+"_"+name+newid+" ("+Params (qparams)+");";

				if (ReturnType != "void")
					ret = "return " + ret;

				ret = "\t\t\t" + ret;
			}

			return ret;
		}

		public string ImportAttrib
		{
			get {return "\n\n"+tab+tab+"[DllImport(\"libqtc\", CharSet=CharSet.Ansi)]";}
		}

		public string Import () {
			if (!qtype.IsInterface) {
				return tab + "using System.Runtime.InteropServices;\n\n";
			} else {
				return "\n";
			}
		}

		public string Header
		{
			get {
			return	"// "+qtype.Name+".cs - A Qt to C# binding.\n" +
					"//\n" +
					"// Copyright (C) 2002  Adam Treat (manyoso@yahoo.com)\n" +
					"//\n" +
					"// This program is free software; you can redistribute it and/or\n" +
					"// modify it under the terms of the GNU General Public License\n" +
					"// as published by the Free Software Foundation; either version 2\n" +
					"// of the License, or (at your option) any later version.\n" +
					"//\n" +
					"// This program is distributed in the hope that it will be useful,\n" +
					"// but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +
					"// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n" +
					"// GNU General Public License for more details.\n" +
					"//\n" +
					"// You should have received a copy of the GNU General Public License\n" +
					"// along with this program; if not, write to the Free Software\n" +
					"// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n" +
					"//\n" +
					"// Generated File.  Do Not Modify.\n" +
					"\n" +
					"namespace Qt {\n" +
					"\n" +
					tab + "using Qt;\n" +
    				tab + "using System;\n" + Import ();
			}
		}

		public string Footer
		{
			get {return "\n}";}
		}

		public string Register
		{
			get
			{
				return "\n"+tab+tab+tab+"RegisterObject (this);";
			}
		}

		public string Delete ()
		{

			string ret = "\n" +
						 "\t\tinternal override void Delete ()\n" +
						 "\t\t{\n";

			// Let Qt manage QEvents
			if (! (qtype.Name.StartsWith ("Q") && qtype.Name.EndsWith ("Event")))
			{
				ret +=  "\t\t\tif (deleted) return;\n\n";

				if (qtype.QDCtors.Count > 0)
					ret += "\t\t\tqt_del_"+qtype.Name+" (rawObject);\n";
				else
					ret = "\n" + ret + "\t\t\t// libqtc lacks a qt_del_"+qtype.Name+" function\n";

				ret += "\t\t\tdeleted = true;\n";
			}

			ret += "\t\t}";

			return ret;

		}

		public string DefaultConnections ()
		{
			if (qtype.IsQObject)
				return "\n\t\t\tConnect (this, SIGNAL (\"destroyed ()\"), SLOT (\"NativeDestroyed ()\"));";
			else
				return "";
		}
	}
}
