namespace FSpot.Bim {
	public enum EntryType : ushort {
		ObsoleteImageInfo = 0x03e8,  
		MacPrintManager = 0x03e9,
		MacPrintXML = 0x03ea,
		ObsoleteIndexedColorTable = 0x03eb,
		ResolutionInfo = 0x03ed,
		AlphaChannelNames = 0x03ee,
		DisplayInfo = 0x03ef,
		Caption = 0x03f0,
		Border = 0x03f1,
		BackgroundColor = 0x03f2,
		PrintFlags = 0x03f3,
		GrayHalftone = 0x03f4,
		ColorHalftone = 0x03f5,
		DuotoneHalftone = 0x03f6,
		GrayTransfer = 0x03f7,
		ColorTransfer = 0x03f8,
		DuotoneTransfer = 0x03f9,
		DuotoneInformation = 0x03fa,
		BWDot = 0x03fb,
		ObsoleteUnknown1 = 0x03fc,
		LayerState = 0x0400,
		WorkingPath = 0x0401,
		LayerGroupInfomation = 0x0402,
		ObsoleteUnknown2 = 0x0403,
		IPTCNAA = 0x0404,
		RawImageMode = 0x0405,
		JpegQuality = 0x0406,
		GridGuideInformation = 0x0408,
		ThumbnailResource = 0x0409,
		CopyrightFlag = 0x040a,
		URL = 0x040b,
		ThumbnailResource2 = 0x040c,
		GlobalAngle = 0x040d,
		ColorSamplers = 0x040e,
		ICCProfile = 0x040f,
		Watermark = 0x0410,
		ICCUntagged = 0x0411,
		EffectsVisible = 0x0412,
		SpotHalftone = 0x0413,
		LastID = 0x0414,
		UnicodeAlphaNames = 0x0415,
		IndexedColorTable = 0x0416,
		TransparentIndex = 0x0417,
		GlobalAltitude = 0x0419,
		Slices = 0x041a,
		WorkflowURL = 0x041b,
		XPEPJump = 0x041c,
		AlphaIdentifiers = 0x041d,
		URLList = 0x041e,
		VersionInfo = 0x0421,
		XMP = 0x0424,
		FirstPath = 0x07d0,
		LastPAth = 0x0bb6,
		ClippingPathName = 0x0bb7,
		PrintFlags2 = 0x2710,
	}

	/*
	  From what I can see it looks like these resources are a just
	  a list of records starting with a 8Bim\0 followed by a 16 bit
	  value that is the record type then a 8 bit offset and 32bit length. IPTC data is
	  type 0x0404, I don't know any other types at the moment.

	  see http://www.fine-view.com/jp/lab/doc/ps6ffspecsv2.pdf and the section on image resource blocks.
	*/

	public class Entry
	{
		public ushort  Type;
		public string  Name;
		public byte [] Data;

		const string Marker = "8BIM";
		public Entry ()
		{

		}

		public int Load (System.IO.Stream stream)
		{
			byte [] header = new byte [6];
			
			stream.Read (header, 0, header.Length);
			if (System.Text.Encoding.ASCII.GetString (header, 0, 4) != Marker)
				throw new System.Exception ("missing header");
			
			Type = FSpot.BitConverter.ToUInt16 (header, 4, false);

		        int name_length = stream.ReadByte ();
			if (name_length > 0) {
				byte [] name_data = new byte [name_length];
				stream.Read (name_data, 0, name_length);
				Name = System.Text.Encoding.ASCII.GetString (name_data);
			}
			
			if (name_length % 2 == 0)
				stream.ReadByte ();

			stream.Read (header, 0, 4);
			uint length = FSpot.BitConverter.ToUInt32 (header, 0, false);

			Data = new byte [length];
			stream.Read (Data, 0, Data.Length);

			if (Data.Length % 2 > 0)
				stream.ReadByte ();

			return header.Length + Data.Length;
		}
		
		public void Save (System.IO.Stream stream) 
		{
			byte [] tmp;
			tmp = System.Text.Encoding.ASCII.GetBytes (Marker);
			stream.Write (tmp, 0, tmp.Length);
			tmp = FSpot.BitConverter.GetBytes (Type, false);
			stream.Write (tmp, 0, tmp.Length);

			// Write the name
			stream.WriteByte ((byte)Name.Length);
			tmp = System.Text.Encoding.ASCII.GetBytes (Name);
			stream.Write (tmp, 0, tmp.Length);

			// Pad the name
			if (tmp.Length % 2 == 0)
				stream.WriteByte (0);

			// Write the data
			tmp  = FSpot.BitConverter.GetBytes ((uint)Data.Length, false);
			stream.Write (tmp, 0, tmp.Length);

			stream.Write (Data, 0, Data.Length);
			// Pad the data
			if (Data.Length % 2 > 0)
				stream.WriteByte (0);
		}
	}

	public class BimFile : SemWeb.StatementSource
	{
                // False seems a safe default
                public bool Distinct {
                        get { return false; }
                }

		System.Collections.ArrayList entries = new System.Collections.ArrayList ();

		public BimFile (System.IO.Stream stream)
		{
			Load (stream);
		}
		
		public void Select (SemWeb.StatementSink sink)
		{
			foreach (Entry e in entries) {
				EntryType type = (EntryType) e.Type;

				switch (type) {
				case EntryType.IPTCNAA:
					System.IO.Stream iptcstream = new System.IO.MemoryStream (e.Data);
					FSpot.Iptc.IptcFile iptc = new FSpot.Iptc.IptcFile (iptcstream);
					iptc.Select (sink);
					break;
				case EntryType.XMP:
					System.IO.Stream xmpstream = new System.IO.MemoryStream (e.Data);
					FSpot.Xmp.XmpFile xmp = new FSpot.Xmp.XmpFile (xmpstream);
					xmp.Select (sink);
					break;
				default:
					break;
				}
			}
		}

		public Entry FindEntry (EntryType type)
		{
			foreach (Entry current in entries)
				if (current.Type == (ushort)type)
					return current;

			return null;
		}

		public void Load (System.IO.Stream stream)
		{
			while (stream.Position < stream.Length)
			{
				Entry current = new Entry ();
				current.Load (stream);
				//System.Console.WriteLine ("read {0} - {1}", ((EntryType)current.Type).ToString (), current.Name);
				try {
					//System.Console.WriteLine (System.Text.Encoding.ASCII.GetString (current.Data));
				} catch (System.Exception e) {
					System.Console.WriteLine (e.ToString ());
				}
				entries.Add (current);
			}
		}

		public void Save (System.IO.Stream stream)
		{
			foreach (Entry e in entries) {
				e.Save (stream);
			}
		}
	}
}



