// 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;
		QType qtype;
		QTypeMap qmap;
		StringCollection sigs;
		StringBuilder sig;

		public Converter (ArrayList qtypes, QType qtype, QTypeMap 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 QType GetQType ()
		{
			return qtype;
		}

		public void Convert ()
		{
			foreach (QCtor qctor in qtype.QCtors) {
				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 (QMethod qmethod in qtype.QMethods) {
				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 (QMethod qmethod)
		{
			sig.Append (qmethod.PascalName);
			foreach (QParam 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 (QCtor qctor)
		{
			sig.Append (qctor.Name);
			foreach (QParam qparam in qctor.CSharpParams) {
				//if (qparam.Type == "QWidget" && 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 (QParam qparam in qparams) {
				qparam.Type = qmap.ArrayType (qparam.Type);
				qparam.Type = StripBad (qparam.Type);
				qparam.Type = qmap.CSharpType (qparam.Type);
				qparam.Type = ConvertQString (qparam.Type);
				qparam.Name = qmap.ReservedType (qparam.Name);
			}
		}

		public void ConvertPinvokeCallParams (ArrayList qparams)
		{
			foreach (QParam 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 (IsQObject (qparam.Type))
					qparam.Name = qparam.Name + ".RawObject";
				if (IsIQObject (qparam.Type))
					qparam.Name = qparam.Name + "." + StripInterface (qparam.Type) + " ()";
		/*		if (IsQString (qparam.Type))
					qparam.Name = "new QString ("+StripPtr(qparam.Name)+").RawObject";*/
				qparam.Type = "";
			}
		}

		public void ConvertPinvokeParams (ArrayList qparams)
		{
			foreach (QParam 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 (IsQObject (qparam.Type) || IsIQObject (qparam.Type))
					qparam.Type = "IntPtr";
			}
		}

		public void ConvertOverloadParams (ArrayList qparams)
		{
			foreach (QParam 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);
				OverloadedQString (qparam);
				OverloadedQObject (qparam);
				OverloadedNestedEnum (qparam);
				OverloadedNullString (qparam);
				OverloadedBool (qparam);
				OverloadedEnum (qparam);
				OverloadedArray (qparam);
				OverloadedHex (qparam);
				OverloadedDefault (qparam);
			}
		}

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

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

		public void OverloadedQString (QParam qparam)
		{
			if (IsQString (qparam.Type)){
				qparam.Type = "QString";
				if (qparam.Default == "QString::null")
					qparam.Default = "null";
				else if (qparam.Default == "quotquot")
					qparam.Default = "null";
				else
					qparam.Default = "\""+qparam.Default+"\"";
			}
		}

		public void OverloadedQObject (QParam qparam)
		{
			if (IsQObject (qparam.Type)) {
				qparam.Name = "new "+qparam.Type+" ()";
				qparam.Type = "";
			}
		}

		public void OverloadedNestedEnum (QParam qparam)
		{
			foreach (QEnum qenum in qtype.QEnums) {
				if (qparam.Type == qenum.Name) {
					foreach (QItem qitem in qenum.QItems) {
						if (qparam.Default == qitem.Name) {
							qparam.Name = qparam.Type+"."+qparam.Default;
							qparam.Type = "";
						}
					}
				}
			}
		}

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

		public void OverloadedBool (QParam 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 (QParam qparam)
		{
			if (IsEnum (qparam.Type)) {
				qparam.Name = qparam.Type + "." + EnumValue (qparam.Type, qparam.Default);
				qparam.Type = "";
			}
		}

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

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

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

		public void ConvertReturnType (QMethod qmethod)
		{
			qmethod.Return = qmap.ArrayType (qmethod.Return);
			qmethod.Return = qmap.PinvokeType (StripBad (qmethod.Return));
			if (IsQObject(qmethod.Return)) {
				qmethod.Boxer = true;
				qmethod.PinvokeReturn = "IntPtr";
			} else {
				qmethod.PinvokeReturn = qmethod.Return;
			}
			if (qmethod.Return == "QString") {
				qmethod.QStringReturn = 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 ConvertQString (string str)
		{	
			if (IsQString (str))
				return "QString";
			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 (QEnum qenum in qtype.QEnums) {
				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 (QType qtype in qtypes) {
				foreach (QEnum qenum in qtype.QEnums) {
					if (enumname == qenum.Name) {
						foreach (QItem qitem in qenum.QItems) {
							if (value == qitem.Name) {
								match = true;
							}
						}
						if (!match) {
							foreach (QItem qitem in qenum.QItems) {
								value = qitem.Name;
								break;
							}
						}
					}
				}
			}
			return value;
		}

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

			string iname = "";
			foreach (QAncestor qancestor in qtype.QAncestors) {
				iname = qmap.InterfaceType (qancestor.Name);
				foreach (QType _qtype in qtypes) {
					if (_qtype.Name == qancestor.Name && iname != qancestor.Name) {
						if (!_qtype.IsConverted) {
							Converter converter = new Converter (qtypes, _qtype, qmap);
						}
						qtype.AddQMethod (instPointer (qancestor.Name));
						qancestor.QMethods = _qtype.QMethods;
						qancestor.IsInterface = true;
						qancestor.IName = iname;
						foreach (QMethod qmethod in qancestor.QMethods) {
							CheckSig (qmethod);
						}
					}
				}
			}
		}

		public QMethod instPointer (string name)
		{
			QMethod qmethod = new QMethod ();
			qmethod.Name = name;
			qmethod.PascalName = name;
			qmethod.Access = "public";
			qmethod.PinvokeReturn = "IntPtr";
			qmethod.Return = "IntPtr";
			qmethod.Id = "0";
			return qmethod;
		}
		
		public bool IsQString (string str)
		{
			if (qtype.Name == "QString")
				return true;
			else if (IsQObject (str) && str == "QString")
				return true;
			else
				return false;
   		}

		public bool IsQObject (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 IsIQObject (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;
   		}
	}
}
