namespace GtkSharp.Generation {

	using System;
	using System.Collections;
	using System.IO;
	using System.Xml;

	public class StructABIField : StructField {
		protected new ClassBase container_type;
		public string parent_structure_name;
		public string abi_info_name;

		public StructABIField (XmlElement elem, ClassBase container_type,
				string info_name) : base (elem, container_type) {
			this.container_type = container_type;
			this.getOffsetName = null;
			this.abi_info_name = info_name;
		}

		public override string CName {

			get {
				if (parent_structure_name != null)
					return parent_structure_name + '.' + elem.GetAttribute ("cname");
				return elem.GetAttribute ("cname");
			}
		}

		public override void Generate (GenerationInfo gen_info, string indent) {
			base.Generate(gen_info, indent);
		}

		// All field are visible and private
		// as the goal is to respect the ABI
		protected override string Access {
			get {
				return "private";
			}
		}

		public override bool Hidden {
			get {
				return false;
			}
		}

		public override bool Validate (LogWriter log)
		{
			string cstype = SymbolTable.Table.GetCSType(CType, true);

			if (elem.GetAttributeAsBoolean("is_callback"))
				return true;

			if (cstype == null || cstype == "") {
				log.Warn (" field \"" + CName + "\" has no cstype, can't generate ABI field.");
				return false;
			}

			if (!base.Validate (log))
				return false;

			return true;
		}

		public void SetGetOffseName() {
			this.getOffsetName = "Get" + CName + "Offset";
		}

		public override string GenerateGetSizeOf(string indent) {
			return base.GenerateGetSizeOf(indent) + " // " + CName;
		}

		public virtual StructABIField Generate (GenerationInfo gen_info, string indent,
				StructABIField prev_field, StructABIField next_field, string parent_name,
				TextWriter structw) {
			StreamWriter sw = gen_info.Writer;
			IGeneratable gen = SymbolTable.Table[CType];

			sw.WriteLine("{0}\tnew GLib.AbiField(\"{1}\"", indent, CName);

			indent = indent + "\t\t";
			if (prev_field != null) {
				sw.WriteLine(indent + ", -1");
			} else {
				if (parent_name != "")
					sw.WriteLine(indent + ", " + parent_name + "." + abi_info_name + ".Fields");
				else
					sw.WriteLine(indent + ", 0");
			}

			sw.WriteLine(indent + ", " + GenerateGetSizeOf(""));

			var prev_field_name = prev_field != null ? "\"" + prev_field.CName + "\"" : "null";
			sw.WriteLine(indent + ", " + prev_field_name);

			var container_name = container_type.CName.Replace(".", "_");
			var sanitized_name = CName.Replace(".", "_");
			var alig_struct_name = container_name + "_" + sanitized_name + "Align";
			var next_field_name = next_field != null ? "\"" + next_field.CName + "\"" : "null";
			sw.WriteLine(indent + ", " + next_field_name);

			if (structw != null) {
				string min_align = gen != null ? gen.GenerateAlign() : null;

				// Do not generate structs if the type is a simple pointer.
				if (IsCPointer())
					min_align = "(uint) sizeof(IntPtr)";

				if (IsBitfield)
					min_align = "1";

				if (min_align == null) {
					var tmpindent = "\t\t";
					structw.WriteLine(tmpindent + "[StructLayout(LayoutKind.Sequential)]");
					structw.WriteLine(tmpindent + "public struct " + alig_struct_name);
					structw.WriteLine(tmpindent + "{");
					structw.WriteLine(tmpindent + "\tsbyte f1;");
					base.Generate(gen_info, tmpindent + "\t", true, structw);
					structw.WriteLine(tmpindent + "}");
					structw.WriteLine();

					var fieldname = SymbolTable.Table.MangleName (CName).Replace(".", "_");
					if (IsArray && IsNullTermArray)
						fieldname += "Ptr";
					sw.WriteLine(indent + ", (long) Marshal.OffsetOf<" + alig_struct_name + ">(\"" + fieldname + "\")");
				} else {
					sw.WriteLine(indent + ", " + min_align);
				}
			}

			gen_info.Writer = sw;

			uint bits = 0;
			var bitsstr = elem.GetAttribute("bits");
			if (bitsstr != null && bitsstr != "")
				bits = (uint) Int32.Parse(bitsstr);

			sw.WriteLine(indent + ", " + bits);
			sw.WriteLine(indent + "),");

			return this;
		}
	}
}
