// 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.Text;
using System.Collections;
using System.Collections.Specialized;
//using System.Text.RegularExpressions;

namespace QtCSharp {

	public class Converter {

		ArrayList qtypes;
		TQType qtype;
		TQTypeMap qmap;
		StringCollection sigs;
		StringBuilder sig;

		public Converter (ArrayList qtypes, TQType qtype, TQTypeMap qmap)
		{
			this.qtypes = qtypes;
			this.qtype = qtype;
			this.qmap = qmap;
			sigs = new StringCollection ();
			sig = new StringBuilder ();
			if (!qtype.IsConverted)
				Convert ();
			Ancestors ();
			qtype.IsConverted = true;
		}

		public TQType GetTQType ()
		{
			return qtype;
		}

		public void Convert ()
		{
			foreach (TQCtor qctor in qtype.TQCtors) {
				qctor.Name = qmap.ReservedType (qctor.Name);
				if (!qctor.Overload) {
					ConvertCSharpParams (qctor.CSharpParams);
					ConvertPinvokeCallParams (qctor.PinvokeCallParams);
					ConvertPinvokeParams (qctor.PinvokeParams);
				} else {
					ConvertOverloadParams (qctor.OverloadParams);
				}
				CheckSig (qctor);
			}
			foreach (TQMethod qmethod in qtype.TQMethods) {
				if (qmethod.Name.StartsWith ("protected_"))
					qmethod.Name = qmethod.Name.Replace ("protected_", "");
				qmethod.Name = qmap.ReservedType (qmethod.Name);
				if (!qmethod.Overload) {
					ConvertCSharpParams (qmethod.CSharpParams);
					ConvertPinvokeCallParams (qmethod.PinvokeCallParams);
					ConvertPinvokeParams (qmethod.PinvokeParams);
					ConvertReturnType (qmethod);
				} else {
					ConvertOverloadParams (qmethod.OverloadParams);
					ConvertReturnType (qmethod);
				}
				qmethod.PascalName = ToPascalCase (qmethod.Name);
				CheckSig (qmethod);
			}
		}

		public void CheckSig (TQMethod qmethod)
		{
			sig.Append (qmethod.PascalName);
			foreach (TQParam qparam in qmethod.CSharpParams) {
				sig.Append (qparam.Type);
			}
			if (!sigs.Contains (sig.ToString ()) && !sigs.Contains ("The"+sig.ToString ())) {
				sigs.Add (sig.ToString ());
			} else {
				Console.WriteLine ("Throttling "+qtype.Name+" "+qmethod.PascalName);
				qmethod.Throttle = true;
			}
			sig.Length = 0;
		}

		public void CheckSig (TQCtor qctor)
		{
			sig.Append (qctor.Name);
			foreach (TQParam qparam in qctor.CSharpParams) {
				//if (qparam.Type == "TQWidget" && qparam.Name == "parent")
				if (qparam.Name == "parent")
					qctor.Parent = true;
				sig.Append (qparam.Type);
			}
			if (!sigs.Contains (sig.ToString ()))
				sigs.Add (sig.ToString ());
			else {
				Console.WriteLine ("Throttling "+qtype.Name+" "+qctor.Access+" "+qctor.Name);
				qctor.Throttle = true;
			}
			sig.Length = 0;
		}

		public void ConvertCSharpParams (ArrayList qparams)
		{
			foreach (TQParam qparam in qparams) {
				qparam.Type = qmap.ArrayType (qparam.Type);
				qparam.Type = StripBad (qparam.Type);
				qparam.Type = qmap.CSharpType (qparam.Type);
				qparam.Type = ConvertTQString (qparam.Type);
				qparam.Name = qmap.ReservedType (qparam.Name);
			}
		}

		public void ConvertPinvokeCallParams (ArrayList qparams)
		{
			foreach (TQParam qparam in qparams) {
				qparam.Type = qmap.ArrayType (qparam.Type);
				qparam.Type = StripBad (qparam.Type);
				qparam.Type = qmap.CSharpType (qparam.Type);
				qparam.Name = qmap.ReservedType (qparam.Name);
				if (IsTQObject (qparam.Type))
					qparam.Name = qparam.Name + ".RawObject";
				if (IsITQObject (qparam.Type))
					qparam.Name = qparam.Name + "." + StripInterface (qparam.Type) + " ()";
		/*		if (IsTQString (qparam.Type))
					qparam.Name = "new TQString ("+StripPtr(qparam.Name)+").RawObject";*/
				qparam.Type = "";
			}
		}

		public void ConvertPinvokeParams (ArrayList qparams)
		{
			foreach (TQParam qparam in qparams) {
				qparam.Type = qmap.ArrayType (qparam.Type);
				qparam.Type = StripBad (qparam.Type);
				qparam.Type = qmap.PinvokeType (qparam.Type);
				qparam.Name = qmap.ReservedType (qparam.Name);
				if (IsTQObject (qparam.Type) || IsITQObject (qparam.Type))
					qparam.Type = "IntPtr";
			}
		}

		public void ConvertOverloadParams (ArrayList qparams)
		{
			foreach (TQParam qparam in qparams) {
				qparam.Type = qmap.ArrayType (qparam.Type);
				qparam.Type = StripBad (qparam.Type);
				qparam.Type = qmap.CSharpType (qparam.Type);
				OverloadedLastParam (qparam, qparams);
				OverloadedNull (qparam);
				OverloadedTQString (qparam);
				OverloadedTQObject (qparam);
				OverloadedNestedEnum (qparam);
				OverloadedNullString (qparam);
				OverloadedBool (qparam);
				OverloadedEnum (qparam);
				OverloadedArray (qparam);
				OverloadedHex (qparam);
				OverloadedDefault (qparam);
			}
		}

		public void OverloadedLastParam (TQParam qparam, ArrayList qparams)
		{
			if (qparams.IndexOf (qparam) != qparams.Count - 1)
				qparam.Default = null;
		}

		public void OverloadedNull (TQParam qparam)
		{
			if (qparam.Default == null)
				qparam.Type = "";
		}

		public void OverloadedTQString (TQParam qparam)
		{
			if (IsTQString (qparam.Type)){
				qparam.Type = "TQString";
				if (qparam.Default == "TQString::null")
					qparam.Default = "null";
				else if (qparam.Default == "quotquot")
					qparam.Default = "null";
				else
					qparam.Default = "\""+qparam.Default+"\"";
			}
		}

		public void OverloadedTQObject (TQParam qparam)
		{
			if (IsTQObject (qparam.Type)) {
				qparam.Name = "new "+qparam.Type+" ()";
				qparam.Type = "";
			}
		}

		public void OverloadedNestedEnum (TQParam qparam)
		{
			foreach (TQEnum qenum in qtype.TQEnums) {
				if (qparam.Type == qenum.Name) {
					foreach (TQItem qitem in qenum.TQItems) {
						if (qparam.Default == qitem.Name) {
							qparam.Name = qparam.Type+"."+qparam.Default;
							qparam.Type = "";
						}
					}
				}
			}
		}

		public void OverloadedNullString (TQParam qparam)
		{
			if (qmap.OverloadType (qparam.Type) == "string" && qparam.Default == "0") {
				qparam.Type = "";
				qparam.Name = "\"\"";
			}
		}

		public void OverloadedBool (TQParam qparam)
		{
			if (qparam.Default == "TRUE") {
				qparam.Type = "";
				qparam.Name = "true";
			} else if (qparam.Default == "FALSE") {
				qparam.Type = "";
				qparam.Name = "false";
			} else if (qparam.Type == "bool" && qparam.Default == "1") {
				qparam.Type = "";
				qparam.Name = "true";
			} else if (qparam.Type == "bool" && qparam.Default == "0") {
				qparam.Type = "";
				qparam.Name = "false";
			}
		}

		public void OverloadedEnum (TQParam qparam)
		{
			if (IsEnum (qparam.Type)) {
				qparam.Name = qparam.Type + "." + EnumValue (qparam.Type, qparam.Default);
				qparam.Type = "";
			}
		}

		public void OverloadedArray (TQParam qparam)
		{
			if (IsArray (qparam.Type)) {
				qparam.Name = "new "+qparam.Type+"{"+qparam.Default+"}";
				qparam.Type = "";
			}
		}

		public void OverloadedHex (TQParam qparam)
		{
			if (qparam.Default == "0xffffffff")
				qparam.Default = "1";
		}

		public void OverloadedDefault (TQParam qparam)
		{
			if (qparam.Type != "") {
				qparam.Type = "("+qmap.OverloadType (qparam.Type)+")";
				qparam.Name = qparam.Default;
			}
		}

		public void ConvertReturnType (TQMethod qmethod)
		{
			qmethod.Return = qmap.ArrayType (qmethod.Return);
			qmethod.Return = qmap.PinvokeType (StripBad (qmethod.Return));
			if (IsTQObject(qmethod.Return)) {
				qmethod.Boxer = true;
				qmethod.PinvokeReturn = "IntPtr";
			} else {
				qmethod.PinvokeReturn = qmethod.Return;
			}
			if (qmethod.Return == "TQString") {
				qmethod.TQStringReturn = true;
			}
		}

		public string StripBad (string str)
		{
				str = StripPointer (str);
				str = StripColon (str);
			return str;
		}

		public string StripPointer (string str)
		{
			str = str.Replace ("*", "");
			str = str.Replace ("&", "");
			if (str.StartsWith ("amp"))
				str = str.Replace ("amp", "");
			if (str.EndsWith ("amp"))
				str = str.Replace ("amp", "");
			return str;
		}
		
		public string ConvertTQString (string str)
		{	
			if (IsTQString (str))
				return "TQString";
			else
				return str;
		}

		public string StripColon (string str)
		{
			return str = str.Replace ("::", ".");
		}
		
		public string StripPtr (string str)
		{
			return str = str.Replace (".RawObject", "");
		}
		
		public string StripInterface (string str)
		{
			return str = str.Replace ("I", "");
		}

		public string StripEnum (string str)
		{
			str = StripColon (str);
			if (str.IndexOf (".") > 0)
				return str.Substring (str.IndexOf (".")+1);
			else
				return str;
		}

		public string ToPascalCase (string name)
		{
			string pascal = System.Char.ToUpper (name[0]).ToString ()+name.Substring (1, name.Length -1);
			foreach (TQEnum qenum in qtype.TQEnums) {
				if (pascal == qenum.Name)
					pascal = "The"+pascal;
			}
			return pascal;
		}

		public string EnumValue (string type, string value)
		{
			bool match = false;
			string enumname = StripEnum (type);
			value = StripEnum (value);

			// There _has_ to be a better way, but I'm tired...
			foreach (TQType qtype in qtypes) {
				foreach (TQEnum qenum in qtype.TQEnums) {
					if (enumname == qenum.Name) {
						foreach (TQItem qitem in qenum.TQItems) {
							if (value == qitem.Name) {
								match = true;
							}
						}
						if (!match) {
							foreach (TQItem qitem in qenum.TQItems) {
								value = qitem.Name;
								break;
							}
						}
					}
				}
			}
			return value;
		}

		public void Ancestors ()
		{
			if (qtype.IsInterface || qtype.TQAncestors.Count < 2)
			  return;

			string iname = "";
			foreach (TQAncestor qancestor in qtype.TQAncestors) {
				iname = qmap.InterfaceType (qancestor.Name);
				foreach (TQType _qtype in qtypes) {
					if (_qtype.Name == qancestor.Name && iname != qancestor.Name) {
						if (!_qtype.IsConverted) {
							Converter converter = new Converter (qtypes, _qtype, qmap);
						}
						qtype.AddTQMethod (instPointer (qancestor.Name));
						qancestor.TQMethods = _qtype.TQMethods;
						qancestor.IsInterface = true;
						qancestor.IName = iname;
						foreach (TQMethod qmethod in qancestor.TQMethods) {
							CheckSig (qmethod);
						}
					}
				}
			}
		}

		public TQMethod instPointer (string name)
		{
			TQMethod qmethod = new TQMethod ();
			qmethod.Name = name;
			qmethod.PascalName = name;
			qmethod.Access = "public";
			qmethod.PinvokeReturn = "IntPtr";
			qmethod.Return = "IntPtr";
			qmethod.Id = "0";
			return qmethod;
		}
		
		public bool IsTQString (string str)
		{
			if (qtype.Name == "TQString")
				return true;
			else if (IsTQObject (str) && str == "TQString")
				return true;
			else
				return false;
   		}

		public bool IsTQObject (string str)
		{
			//IndexOf is a hack to search for a char ;-)
			if (str.StartsWith ("Q") && str.IndexOf (".") < 0)
				return true;
			else
				return false;
   		}
		
		public bool IsITQObject (string str)
		{
			//IndexOf is a hack to search for a char ;-)
			if (str == "IntPtr") return false;
			if (str.StartsWith ("I") && str.IndexOf (".") < 0)
				return true;
			else
				return false;
   		}

		public bool IsEnum (string str)
		{
			//IndexOf is a hack to search for a char ;-)
			if (str.IndexOf (".") > 0)
				return true;
			else
				return false;
   		}

		public bool IsArray (string str)
		{
			if (str.EndsWith ("[]"))
				return true;
			else
				return false;
   		}
	}
}
