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

namespace BizHawk.Emulation.DiscSystem
{
	/// <summary>
	/// Class to represent the file/directory information read from the disk.
	/// </summary>
	public class ISONodeRecord
	{
		#region Constants

		/// <summary>
		/// String representing the current directory entry
		/// </summary>
		public const string CURRENT_DIRECTORY = ".";

		/// <summary>
		/// String representing the parent directory entry
		/// </summary>
		public const string PARENT_DIRECTORY = "..";

		#endregion

		#region Public Properties

		/// <summary>
		/// The length of the record in bytes.
		/// </summary>
		public byte Length;

		/// <summary>
		/// The file offset of the data for this file/directory (in sectors).
		/// </summary>
		public long OffsetOfData;
		/// <summary>
		/// The length of the data for this file/directory (in bytes).
		/// </summary>
		public long LengthOfData;

		/// <summary>
		/// The file/directory creation year since 1900.
		/// </summary>
		public byte Year;
		/// <summary>
		/// The file/directory creation month.
		/// </summary>
		public byte Month;
		/// <summary>
		/// The file/directory creation day.
		/// </summary>
		public byte Day;
		/// <summary>
		/// The file/directory creation hour.
		/// </summary>
		public byte Hour;
		/// <summary>
		/// The file/directory creation minute.
		/// </summary>
		public byte Minute;
		/// <summary>
		/// The file/directory creation second.
		/// </summary>
		public byte Second;
		/// <summary>
		/// The file time offset from GMT.
		/// </summary>
		public byte TimeZoneOffset;

		/// <summary>
		/// Flags representing the attributes of this file/directory.
		/// </summary>
		public byte Flags;

		/// <summary>
		/// The length of the file/directory name.
		/// </summary>
		public byte NameLength;
		/// <summary>
		/// The file/directory name.
		/// </summary>
		public string Name;

		#endregion

		#region Construction

		/// <summary>
		/// Constructor
		/// </summary>
		public ISONodeRecord()
		{
			// Set initial values
			this.Length = 0;

			this.OffsetOfData = 0;
			this.LengthOfData = 0;

			this.Year = 0;
			this.Month = 0;
			this.Day = 0;
			this.Hour = 0;
			this.Minute = 0;
			this.Second = 0;
			this.TimeZoneOffset = 0;

			this.Flags = 0;

			this.NameLength = 0;
			this.Name = null;
		}

		#endregion

		#region File/Directory Methods

		/// <summary>
		/// Return true if the record represents a file.
		/// </summary>
		/// <returns>True if a file.</returns>
		public bool IsFile()
		{
			return ((this.Flags >> 1) & 0x01) == 0;
		}

		/// <summary>
		/// Return true if the record represents a directory.
		/// </summary>
		/// <returns>True if a directory.</returns>
		public bool IsDirectory()
		{
			return ((this.Flags >> 1) & 0x01) == 1;
		}

		#endregion

		#region Parsing

		/// <summary>
		/// Parse the record from an array and offset.
		/// </summary>
		/// <param name="data">The array to parse from.</param>
		/// <param name="cursor">The offset to start parsing at.</param>
		public void Parse(byte[] data, int cursor)
		{
			// Put the array into a memory stream and pass to the main parsing function
			MemoryStream s = new MemoryStream(data);
			s.Seek(cursor, SeekOrigin.Begin);
			this.Parse(s);
		}

		/// <summary>
		/// Parse the node record from the given stream.
		/// </summary>
		/// <param name="s">The stream to parse from.</param>
		public void Parse(Stream s)
		{
			EndianBitConverter bc = EndianBitConverter.CreateForLittleEndian();
			long startPosition = s.Position;
			byte[] buffer = new byte[ISOFile.SECTOR_SIZE];

			// Get the length
			s.Read(buffer, 0, 1);
			this.Length = buffer[0];

			//the number of sectors in the attribute record
			s.Read(buffer, 0, 1);

			// Read Data Offset
			s.Read(buffer, 0, 8);
			this.OffsetOfData = (long)bc.ToInt32(buffer);

			// Read Data Length
			s.Read(buffer, 0, 8);
			this.LengthOfData = (long)bc.ToInt32(buffer);

			// Read the time and flags
			s.Read(buffer, 0, 8);
			this.Year = buffer[0];
			this.Month = buffer[1];
			this.Day = buffer[2];
			this.Hour = buffer[3];
			this.Minute = buffer[4];
			this.Second = buffer[5];
			this.TimeZoneOffset = buffer[6];

			this.Flags = buffer[7];

			s.Read(buffer, 0, 6);

			// Read the name length
			s.Read(buffer, 0, 1);
			this.NameLength = buffer[0];

			// Read the directory name
			s.Read(buffer, 0, this.NameLength);
			if (this.NameLength == 1 && (buffer[0] == 0 || buffer[0] == 1))
			{
				if (buffer[0] == 0)
					this.Name = ISONodeRecord.CURRENT_DIRECTORY;
				else
					this.Name = ISONodeRecord.PARENT_DIRECTORY;
			}
			else
			{
				this.Name = ASCIIEncoding.ASCII.GetString(buffer, 0, this.NameLength);
			}

			// Seek to end
			s.Seek(startPosition + this.Length, SeekOrigin.Begin);
		}

		#endregion
	}
}
