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

namespace BizHawk.Emulation.DiscSystem.CUE
{
	internal abstract class SS_Base : ISectorSynthJob2448
	{
		public IBlob Blob;
		public long BlobOffset;

		public DiscMountPolicy Policy;

		//subQ data
		public SubchannelQ sq;

		//subP data
		public bool Pause; 

		public abstract void Synth(SectorSynthJob job);

		protected void SynthSubchannelAsNeed(SectorSynthJob job)
		{
			//synth P if needed
			if ((job.Parts & ESectorSynthPart.SubchannelP) != 0)
			{
				SynthUtils.SubP(job.DestBuffer2448, job.DestOffset + 2352, Pause);
			}

			//synth Q if needed
			//TODO - why not already have it serialized? Into a disc resource, even.
			if ((job.Parts & ESectorSynthPart.SubchannelQ) != 0)
			{
				SynthUtils.SubQ_Serialize(job.DestBuffer2448, job.DestOffset + 2352 + 12, ref sq);
			}

			//clear R-W if needed
			if ((job.Parts & ESectorSynthPart.Subchannel_RSTUVW) != 0)
			{
				Array.Clear(job.DestBuffer2448, job.DestOffset + 2352 + 12 + 12, (12 * 6));
			}

			//subcode has been generated deinterleaved; we may still need to interleave it
			if((job.Parts & ESectorSynthPart.SubcodeAny) != 0)
				if ((job.Parts & (ESectorSynthPart.SubcodeDeinterleave)) == 0)
				{
					SynthUtils.InterleaveSubcodeInplace(job.DestBuffer2448, job.DestOffset + 2352);
				}
		}
	}

	/// <summary>
	/// Represents a Mode1 2048-byte sector
	/// </summary>
	class SS_Mode1_2048 : SS_Base
	{
		public override void Synth(SectorSynthJob job)
		{
			//read the sector user data
			if((job.Parts & ESectorSynthPart.User2048) != 0)
				Blob.Read(BlobOffset, job.DestBuffer2448, job.DestOffset + 16, 2048);

			if ((job.Parts & ESectorSynthPart.Header16) != 0)
				SynthUtils.SectorHeader(job.DestBuffer2448, job.DestOffset + 0, job.LBA, 1);

			if ((job.Parts & ESectorSynthPart.ECMAny) != 0)
				SynthUtils.ECM_Mode1(job.DestBuffer2448, job.DestOffset + 0, job.LBA);

			SynthSubchannelAsNeed(job);
		}
	}

	/// <summary>
	/// Represents a 2352-byte sector of any sort
	/// </summary>
	class SS_2352 : SS_Base
	{
		public override void Synth(SectorSynthJob job)
		{
			//read the sector user data
			Blob.Read(BlobOffset, job.DestBuffer2448, job.DestOffset, 2352);

			//if subcode is needed, synthesize it
			SynthSubchannelAsNeed(job);
		}
	}

	/// <summary>
	/// Encodes a pre-gap sector
	/// </summary>
	class SS_Gap : SS_Base
	{
		public CueTrackType TrackType;

		public override void Synth(SectorSynthJob job)
		{
			//this isn't fully analyzed/optimized
			Array.Clear(job.DestBuffer2448, job.DestOffset, 2352);

			byte mode = 255;
			int form = -1;
			switch (TrackType)
			{
				case CueTrackType.Audio:
					mode = 0;
					break;

				case CueTrackType.CDI_2352:
				case CueTrackType.Mode1_2352:
					mode = 1;
					break;

				case CueTrackType.Mode2_2352:
					mode = 2;
					if (Policy.CUE_PregapMode2_As_XA_Form2)
					{
						job.DestBuffer2448[job.DestOffset + 12 + 6] = 0x20;
						job.DestBuffer2448[job.DestOffset + 12 + 10] = 0x20;
					}
					form = 2; //no other choice right now really
					break;

				case CueTrackType.Mode1_2048:
					mode = 1;
					Pause = true;
					break;

				case CueTrackType.Mode2_2336:
				default:
					throw new InvalidOperationException("Not supported: " + TrackType);
			}

			//audio has no sector header but the others do
			if (mode != 0)
			{
				if ((job.Parts & ESectorSynthPart.Header16) != 0)
					SynthUtils.SectorHeader(job.DestBuffer2448, job.DestOffset + 0, job.LBA, mode);
			}

			if (mode == 1)
			{
				if ((job.Parts & ESectorSynthPart.ECMAny) != 0)
					SynthUtils.ECM_Mode1(job.DestBuffer2448, job.DestOffset + 0, job.LBA);
			}
			if (mode == 2 && form == 2)
			{
				SynthUtils.EDC_Mode2_Form2(job.DestBuffer2448, job.DestOffset);
			}

			SynthSubchannelAsNeed(job);
		}
	}


}