using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

using BizHawk.Emulation.Common;

namespace BizHawk.Emulation.Cores.Sony.PSP
{
	[CoreAttributes(
		"PPSSPP",
		"hrydgard",
		isPorted: true,
		isReleased: false
		)]
	public class PSP : IEmulator, IVideoProvider, ISyncSoundProvider
	{
		public static readonly ControllerDefinition PSPController = new ControllerDefinition
		{
			Name = "PSP Controller",
			BoolButtons =
			{					
				"Up", "Down", "Left", "Right", "Select", "Start", "L", "R", "Square", "Triangle", "Circle", "Cross", "Power"
			},
			FloatControls =
			{
				"Stick X", "Stick Y"
			},
			FloatRanges = // TODO
			{
				new[] {-1.0f, 0.0f, 1.0f},
				new[] {-1.0f, 0.0f, 1.0f},
			}
		};

		public IVideoProvider VideoProvider { get { return this; } }
		public ISoundProvider SoundProvider { get { return null; } }
		public ISyncSoundProvider SyncSoundProvider { get { return this; } }
		public bool StartAsyncSound() { return false; }
		public void EndAsyncSound() { }
		public ControllerDefinition ControllerDefinition { get { return PSPController; } }
		public IController Controller { get; set; }
		public bool DeterministicEmulation { get { return true; } }
		public string SystemId { get { return "PSP"; } }
		public bool BinarySaveStatesPreferred { get { return true; } }
		public CoreComm CoreComm { get; private set; }

		public string BoardName { get { return null; } }

		PPSSPPDll.LogCB logcallback = null;
		Queue<string> debugmsgs = new Queue<string>();
		void LogCallbackFunc(char type, string message)
		{
			debugmsgs.Enqueue(string.Format("PSP: {0} {1}", type, message));
		}
		void LogFlush()
		{
			while (debugmsgs.Count > 0)
			{
				Console.WriteLine(debugmsgs.Dequeue());
			}
		}

		public Dictionary<string, int> GetCpuFlagsAndRegisters()
		{
			throw new NotImplementedException();
		}

		bool disposed = false;
		static PSP attachedcore = null;
		GCHandle vidhandle;

		public PSP(CoreComm comm, string isopath)
		{
			if (attachedcore != null)
			{
				attachedcore.Dispose();
				attachedcore = null;
			}
			CoreComm = comm;

			logcallback = new PPSSPPDll.LogCB(LogCallbackFunc);

			bool good = PPSSPPDll.init(isopath, logcallback);
			LogFlush();
			if (!good)
				throw new Exception("PPSSPP Init failed!");
			vidhandle = GCHandle.Alloc(screenbuffer, GCHandleType.Pinned);
			PPSSPPDll.setvidbuff(vidhandle.AddrOfPinnedObject());

			CoreComm.VsyncDen = 1;
			CoreComm.VsyncNum = 60;
			CoreComm.RomStatusDetails = "It puts the scythe in the chicken or it gets the abyss again!";

			attachedcore = this;
		}

		public void Dispose()
		{
			if (!disposed)
			{
				vidhandle.Free();
				PPSSPPDll.setvidbuff(IntPtr.Zero);
				PPSSPPDll.die();
				logcallback = null;
				disposed = true;
				LogFlush();
				Console.WriteLine("PSP Core Disposed.");
			}
		}



		public void FrameAdvance(bool render, bool rendersound = true)
		{
			PPSSPPDll.advance();
			// problem 1: audio can be 48khz, if a particular core parameter is set.  we're not accounting for that.
			// problem 2: we seem to be getting approximately the right amount of output, but with
			// a lot of jitter on the per-frame buffer size
			nsampavail = PPSSPPDll.mixsound(audiobuffer, audiobuffer.Length / 2);
			LogFlush();
			//Console.WriteLine("Audio Service: {0}", nsampavail);
		}

		public int Frame
		{
			get { return 0; }
		}

		public int LagCount
		{
			get
			{
				return 0;
			}
			set
			{
			}
		}

		public bool IsLagFrame
		{
			get { return true; }
		}

		public byte[] ReadSaveRam()
		{
			return new byte[0];
		}

		public void StoreSaveRam(byte[] data)
		{
		}

		public void ClearSaveRam()
		{
		}

		public bool SaveRamModified
		{
			get
			{
				return false;
			}
			set
			{
			}
		}

		public void ResetCounters()
		{
		}

		public void SaveStateText(System.IO.TextWriter writer)
		{
		}

		public void LoadStateText(System.IO.TextReader reader)
		{
		}

		public void SaveStateBinary(System.IO.BinaryWriter writer)
		{
		}

		public void LoadStateBinary(System.IO.BinaryReader reader)
		{
		}

		public byte[] SaveStateBinary()
		{
			return new byte[0];
		}

		public MemoryDomainList MemoryDomains
		{
			get { throw new NotImplementedException(); }
		}

		const int screenwidth = 480;
		const int screenheight = 272;
		readonly int[] screenbuffer = new int[screenwidth * screenheight];
		public int[] GetVideoBuffer() { return screenbuffer; }
		public int VirtualWidth { get { return screenwidth; } }
		public int BufferWidth { get { return screenwidth; } }
		public int BufferHeight { get { return screenheight; } }
		public int BackgroundColor { get { return unchecked((int)0xff000000); } }

		readonly short[] audiobuffer = new short[2048 * 2];
		int nsampavail = 0;
		public void GetSamples(out short[] samples, out int nsamp)
		{			
			samples = audiobuffer;
			nsamp = nsampavail;
		}
		public void DiscardSamples()
		{
		}

		public object GetSettings() { return null; }
		public object GetSyncSettings() { return null; }
		public bool PutSettings(object o) { return false; }
		public bool PutSyncSettings(object o) { return false; }
	}
}
