using System;
using System.Drawing;

using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX.DirectSound;


namespace DarkStrideToolbox
{
	//Structures to hold our loaded graphics
	public class LoadedTexture
	{
		private Vector2 vSize;
		public Texture oTexture;
		public string sFileName;
		public System.Drawing.Color oAlphaColor;
		public bool bIsSystemResource;
		public System.IO.Stream oStream = null;
		public long m_nRealPicLeft = 0;
		public long m_nRealPicTop = 0;
		public long m_nRealPicRight = 0;
		public long m_nRealPicBottom = 0;

		public Vector2 Size
		{
			get
			{
				Vector2 vNewSize = new Vector2( vSize.X - m_nRealPicLeft - m_nRealPicRight,
												vSize.Y - m_nRealPicTop - m_nRealPicBottom );
				return( vNewSize );
			}
			set
			{
				vSize = new Vector2( value.X + m_nRealPicLeft + m_nRealPicRight,
									 value.Y + m_nRealPicTop + m_nRealPicBottom );
			}
		}		
	}
	public class LoadedSound
	{
		public DateTime dtLastPlayed = DateTime.MinValue;
		public double nSecondsBetweenPlays = 0;
		public long nMaxSoundBuffers = 0;
		public string sKey = "";
		public string sFileName = "";
		public bool bIsSystemResource = false;
		public System.IO.Stream oStream = null;
		public System.Collections.ArrayList m_oSoundInstances = new System.Collections.ArrayList();
	}


	public class DSResourceManager
	{
		#region Properties
		private Microsoft.DirectX.DirectSound.Device m_oDirectSoundDevice = null;
		private Microsoft.DirectX.Direct3D.Device m_oDirect3DDevice = null;
		private static DSResourceManager m_oSingletonInstanceOfMe = null;

		private DSSortedList m_oLoadedMeshes = new DSSortedList();
		private DSSortedList m_oLoadedSounds = new DSSortedList();
		private DSSortedList m_oLoadedTextures = new DSSortedList();
		private DSSortedList m_oLoadedGobFiles = new DSSortedList();

		private DSSortedList m_oLoadedFonts = new DSSortedList();
		private const string m_cSTANDARDFONT_KEY = "Arial Standard";
		private string m_cSTANDARDFONT_NAME = "Arial";
		#endregion


		private DSResourceManager()
		{
		}

		public static DSResourceManager GetGlobalInstance()
		{
			if( m_oSingletonInstanceOfMe == null )
			{
				m_oSingletonInstanceOfMe = new DSResourceManager();
			}

			return( m_oSingletonInstanceOfMe );
		}


		#region Event Functions
		public void OnCreateDevice( Microsoft.DirectX.DirectSound.Device oDirectSoundDevice )
		{
			m_oDirectSoundDevice = oDirectSoundDevice;

			//Go through and restore any lost textures
			OnRestoreSoundDeviceObjects();
		}
		public void OnCreateDevice( Microsoft.DirectX.Direct3D.Device oDirect3DDevice )
		{
			Microsoft.DirectX.Direct3D.Font oNewFont = null;


			m_oDirect3DDevice = oDirect3DDevice;

			//Go through and restore any lost textures
			OnRestore3DDeviceObjects();

			//Load our fonts
			if( GetFont() == null )
			{
				oNewFont = new Microsoft.DirectX.Direct3D.Font(
								m_oDirect3DDevice, 14, 0, FontWeight.Regular,
								1, false, CharacterSet.Default, Precision.Default, FontQuality.Default, 
								PitchAndFamily.DefaultPitch | PitchAndFamily.FamilyDoNotCare, m_cSTANDARDFONT_NAME );
				AddFont( m_cSTANDARDFONT_KEY,oNewFont );
			}
		}

		public void OnRestore3DDeviceObjects()
		{
			DSMesh oLoopMesh = null;
			Microsoft.DirectX.Direct3D.Font oLoopFont = null;
			string sKey = "";


			//Go through and restore any lost textures
			for( int i=0 ; i<m_oLoadedTextures.Count ; i++ )
			{
				sKey = (string)m_oLoadedTextures.GetKey( i );
				RestoreLoadedTexture( sKey );
			}

			//And fonts
			for( int i=0 ; i<m_oLoadedFonts.Count ; i++ )
			{
				oLoopFont = (Microsoft.DirectX.Direct3D.Font)m_oLoadedFonts.GetByIndex( i );
				oLoopFont.OnResetDevice();
			}

			//And meshes
			for( int i=0 ; i<m_oLoadedMeshes.Count ; i++ )
			{
				oLoopMesh = (DSMesh)m_oLoadedMeshes.GetByIndex( i );
				oLoopMesh.OnResetDevice();
			}
		}
		public void OnRestoreSoundDeviceObjects()
		{
			string sKey = "";


			//Go through and restore any lost sounds
			for( int i=0 ; i<m_oLoadedSounds.Count ; i++ )
			{
				sKey = (string)m_oLoadedSounds.GetKey( i );
				RestoreLoadedSound( sKey );
			}
		}
		#endregion
        
		#region Texture Functions
		public LoadedTexture LoadTexture( string sKey,System.IO.Stream oImageStream,System.Drawing.Color oAlphaColor )
		{
			LoadedTexture oLoadedTexture = null;
			const string sRoutineName = "DarkStrideToolbox.DSResourceManager.LoadTexture";


			try
			{
				oLoadedTexture = new LoadedTexture();
				oLoadedTexture.Size = new Vector2( 0,0 );
				oLoadedTexture.sFileName = "";
				oLoadedTexture.oStream = oImageStream;
				oLoadedTexture.oAlphaColor = oAlphaColor;
				oLoadedTexture.bIsSystemResource = false;
			
				m_oLoadedTextures.Add( sKey,oLoadedTexture );

				if( m_oDirect3DDevice != null )
				{
					RestoreLoadedTexture( sKey );
				}
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed loading <" + sKey + ">.",oEx );
			}

			return( oLoadedTexture);
		}
		public LoadedTexture LoadTexture( string sKey,string sFileName )
		{
			LoadedTexture oLoadedTexture = null;

			LoadTexture( sKey,sFileName, System.Drawing.Color.Transparent );

			return( oLoadedTexture );
		}
		public LoadedTexture LoadTexture( string sKey,string sFileName,System.Drawing.Color oAlphaColor )
		{
			LoadedTexture oLoadedTexture = null;

			LoadTexture( sKey,sFileName,oAlphaColor,false );

			return( oLoadedTexture );
		}

		public LoadedTexture LoadTexture( string sKey,string sSysResName,bool bIsSystemResource )
		{
			LoadedTexture oLoadedTexture = null;

			oLoadedTexture = LoadTexture( sKey,sSysResName,System.Drawing.Color.Transparent,bIsSystemResource );

			return( oLoadedTexture );
		}

		public LoadedTexture LoadTexture( string sKey,string sSysResName,System.Drawing.Color oAlphaColor,bool bIsSystemResource )
		{
			LoadedTexture oLoadedTexture = null;
			const string sRoutineName = "DarkStrideToolbox.DSResourceManager.LoadTexture";


			try
			{
				oLoadedTexture = new LoadedTexture();
				oLoadedTexture.Size = new Vector2( 0,0 );
				oLoadedTexture.sFileName = sSysResName;
				oLoadedTexture.oAlphaColor = oAlphaColor;
				oLoadedTexture.bIsSystemResource = bIsSystemResource;
			
				m_oLoadedTextures.Add( sKey,oLoadedTexture );

				if( m_oDirect3DDevice != null )
				{
					RestoreLoadedTexture( sKey );
				}
			}
			catch( System.Exception oEx )
			{
				throw new System.Exception( sRoutineName + " Failed loading <" + sKey + ">.",oEx );
			}

			return( oLoadedTexture );
		}


		//11/23/2003 Chris Hill  C# seems to have this wierd behavior of not remembering the changes you make to objects
		//you pull out of arrays.  This is very disturbing and warents further investigation... later.
		private void RestoreLoadedTexture( string sKey )
		{
			LoadedTexture oLoadedTexture = null;
			Microsoft.DirectX.Direct3D.SurfaceDescription oSurfaceDesc;
			System.Reflection.Assembly oAssembly = null;
			System.IO.Stream oStream = null;


			//Remove the texture from the collection
			oLoadedTexture = (LoadedTexture)m_oLoadedTextures.GetByKey( sKey );
			m_oLoadedTextures.Remove( sKey );

			//Get the texture
			if( oLoadedTexture.bIsSystemResource == false )
			{
				if( oLoadedTexture.oAlphaColor == System.Drawing.Color.Transparent )
				{
					if( oLoadedTexture.oStream == null )
					{
						oLoadedTexture.oTexture = TextureLoader.FromFile( m_oDirect3DDevice, oLoadedTexture.sFileName );
					}
					else
					{
						oLoadedTexture.oTexture = TextureLoader.FromStream( m_oDirect3DDevice, oLoadedTexture.oStream );
					}
				}
				else
				{
					if( oLoadedTexture.oStream == null )
					{
						oLoadedTexture.oTexture = TextureLoader.FromFile(	m_oDirect3DDevice, oLoadedTexture.sFileName, 
																			(int)oLoadedTexture.Size.X,(int)oLoadedTexture.Size.Y,
																			1, 0, Format.A8B8G8R8, Pool.Managed, Filter.None, 
																			Filter.None, oLoadedTexture.oAlphaColor.ToArgb() );
					}
					else
					{
						oLoadedTexture.oStream.Seek( 0,System.IO.SeekOrigin.Begin );
						oLoadedTexture.oTexture = TextureLoader.FromStream( m_oDirect3DDevice, oLoadedTexture.oStream, 
																			(int)oLoadedTexture.Size.X,(int)oLoadedTexture.Size.Y,
																			1, 0, Format.A8B8G8R8, Pool.Managed, Filter.None, 
																			Filter.None, oLoadedTexture.oAlphaColor.ToArgb() );
					}
				}
			}
			else
			{
				oAssembly = System.Reflection.Assembly.GetExecutingAssembly();
				oStream = oAssembly.GetManifestResourceStream( oLoadedTexture.sFileName );

				if( oLoadedTexture.oAlphaColor == System.Drawing.Color.Transparent )
				{
					oLoadedTexture.oTexture = TextureLoader.FromStream( m_oDirect3DDevice, oStream );
				}
				else
				{
					oLoadedTexture.oTexture = TextureLoader.FromStream(		m_oDirect3DDevice, oStream, (int)oStream.Length,
																			(int)oLoadedTexture.Size.X,(int)oLoadedTexture.Size.Y,
																			1, 0, Format.A8B8G8R8, Pool.Managed, Filter.None, 
																			Filter.None, Color.Black.ToArgb() );
				}
			}

			//Set the textues settings
			oSurfaceDesc = oLoadedTexture.oTexture.GetLevelDescription(0);
			oLoadedTexture.Size = new Vector2( oSurfaceDesc.Width,oSurfaceDesc.Height );

			//Save the texture
			m_oLoadedTextures.Add( sKey,oLoadedTexture );				
		}


		public Texture GetTexture( string sKey )
		{
			LoadedTexture oLoadedTexture = null;


			oLoadedTexture = (LoadedTexture)m_oLoadedTextures.GetByKey( sKey );	
			if( oLoadedTexture == null )
			{
				throw new System.Exception( "Texture for key <" + sKey + "> not found." );
			}


			return( oLoadedTexture.oTexture );
		}
		public LoadedTexture GetLoadedTexture( string sKey )
		{
			LoadedTexture oLoadedTexture = null;


			oLoadedTexture = (LoadedTexture)m_oLoadedTextures.GetByKey( sKey );	
			if( oLoadedTexture == null )
			{
				throw new System.Exception( "Texture for key <" + sKey + "> not found." );
			}


			return( oLoadedTexture );
		}

		#endregion

		#region Mesh Functions
		public void LoadMesh( string sKey,string sFileName )
		{

			LoadMesh( sKey,sFileName,1 );
		}
		public void LoadMesh( string sKey,string sFileName,float nResizeFactor )
		{
			DSMesh oMesh = null;


			oMesh = new DSMesh( m_oDirect3DDevice );
			oMesh.Load( sFileName );

			if( nResizeFactor > 1.1 || nResizeFactor < .9 )
			{
				oMesh.Resize( nResizeFactor );
			}

			LoadMesh( sKey,oMesh );
		}

		public void LoadMesh( string sKey,DSMesh oMesh )
		{
			if( m_oLoadedMeshes.ContainsKey( sKey ) == false )
			{
				m_oLoadedMeshes.Add( sKey,oMesh );
			}
		}


		public DSMesh GetMesh( string sKey )
		{
			DSMesh oMesh = null;


			oMesh = (DSMesh)m_oLoadedMeshes.GetByKey( sKey );
			if( oMesh == null )
			{
				throw new System.Exception( "DSMesh for key <" + sKey + "> not found." );
			}

			return( oMesh );
		}

		#endregion

		#region Sound Functions
		public LoadedSound LoadSound( string sKey,System.IO.Stream oImageStream )
		{
			return( LoadSound( sKey,oImageStream,-1,0 ) );
		}
		public LoadedSound LoadSound( string sKey,System.IO.Stream oImageStream,long nMaxSoundBuffers,double nSecondsBetweenPlays )
		{
			LoadedSound oLoadedSound = null;


			oLoadedSound = new LoadedSound();
			oLoadedSound.sKey = sKey;
			oLoadedSound.sFileName = "";
			oLoadedSound.oStream = oImageStream;
			oLoadedSound.bIsSystemResource = false;
			oLoadedSound.nMaxSoundBuffers = nMaxSoundBuffers;
			oLoadedSound.nSecondsBetweenPlays = nSecondsBetweenPlays;
			
			m_oLoadedSounds.Add( sKey,oLoadedSound );
			RestoreLoadedSound( sKey );

			return( oLoadedSound );
		}
		public LoadedSound LoadSound( string sKey,string sFileName )
		{
			return( LoadSound( sKey,sFileName,false,-1 ) );
		}
		public LoadedSound LoadSound( string sKey,string sFileName,long nMaxSoundBuffers )
		{
			return( LoadSound( sKey,sFileName,false,nMaxSoundBuffers ) );
		}
		public LoadedSound LoadSound( string sKey,string sFileName,bool bIsASystemResource )
		{
			return( LoadSound( sKey,sFileName,bIsASystemResource,-1 ) );
		}
		public LoadedSound LoadSound( string sKey,string sFileName,bool bIsASystemResource,long nMaxSoundBuffers )
		{
			LoadedSound oLoadedSound = null;


			oLoadedSound = new LoadedSound();
			oLoadedSound.oStream = null;
			oLoadedSound.sKey = sKey;
			oLoadedSound.nMaxSoundBuffers = nMaxSoundBuffers;

			m_oLoadedSounds.Add( sKey,oLoadedSound );
			RestoreLoadedSound( sKey );

			return( oLoadedSound );
		}


		public void RestoreLoadedSound( string sKey )
		{
			System.Reflection.Assembly oAssembly = null;
			System.IO.Stream oStream = null;
			LoadedSound oLoadedSound = null;


			//Remove the texture from the collection
			oLoadedSound = (LoadedSound)m_oLoadedSounds.GetByKey( sKey );
			m_oLoadedSounds.Remove( sKey );


			//Get the sound
			if( oLoadedSound.bIsSystemResource == false )
			{
				if( oLoadedSound.oStream == null )
				{
					oLoadedSound.oStream = DSMisc.GetMemStreamFromFile( oLoadedSound.sFileName );
				}
			}
			else
			{
				oAssembly = System.Reflection.Assembly.GetExecutingAssembly();
				oStream = oAssembly.GetManifestResourceStream( oLoadedSound.sFileName );
				oLoadedSound.oStream = oStream;
			}


			//Save the texture
			m_oLoadedSounds.Add( sKey,oLoadedSound );		
		}
		public LoadedSound GetLoadedSound( string sKey )
		{
			LoadedSound oLoadedSound = null;
			
			oLoadedSound = (LoadedSound)m_oLoadedSounds.GetByKey( sKey );

			return( oLoadedSound );
		}
		public SecondaryBuffer GetSoundBuffer( string sKey,int nIndex )
		{
			LoadedSound oLoadedSound = null;
			SecondaryBuffer oSoundBuffer = null;

			
			oLoadedSound = (LoadedSound)m_oLoadedSounds.GetByKey( sKey );

			if( oLoadedSound != null && oLoadedSound.m_oSoundInstances.Count > nIndex )
			{
				oSoundBuffer = (SecondaryBuffer)oLoadedSound.m_oSoundInstances[ nIndex ];
			}
			/*else
			{
				throw new System.Exception( "Sound <" + sKey + "> in index <" + nIndex.ToString() + "> cannot be found." );
				oSoundBuffer = null;
			}*/


			return( oSoundBuffer );
		}
		#endregion

		#region Gob Functions
		public void LoadGob( DSGobFile oGobFile )
		{
			DSGobTable oLoopTable = null;


			if( m_oLoadedGobFiles.Contains( oGobFile.FileName ) == true )
			{
				m_oLoadedGobFiles.Remove( oGobFile.FileName );
			}
			m_oLoadedGobFiles.Add( oGobFile.FileName,oGobFile );

			for( int i=0 ; i<oGobFile.Tables.Count ; i++ )
			{
				oLoopTable = (DSGobTable)oGobFile.Tables.GetByIndex( i );

				if( oLoopTable.TableType == enumTableType.Graphic )
				{
					LoadTexture( oLoopTable.TableName,oLoopTable.File,
								 System.Drawing.Color.FromArgb( oLoopTable.AlphaColor ) );
				}
				else if( oLoopTable.TableType == enumTableType.Sound )
				{
					LoadSound( oLoopTable.TableName,oLoopTable.File,oLoopTable.MaxSoundInstances,oLoopTable.SecondsBetweenPlays );
				}
			}
		}
		public void RemoveGob( string sFileName )
		{
			m_oLoadedGobFiles.Remove( sFileName );
		}
		public DSGobTable GetGobTable( string sBorderName )
		{
			DSGobTable oBorder = null;
			DSGobFile oGobFile = null;


			//Go through all the GOB files looking for our border
			for( int i=0 ; i<m_oLoadedGobFiles.Count && oBorder == null ; i++ )
			{
				oGobFile = (DSGobFile)m_oLoadedGobFiles.GetByIndex( i );
				oBorder = oGobFile.GetTable( sBorderName );
				if( oBorder != null )
				{
					break;
				}
			}


			return( oBorder );
		}
		#endregion

		#region Font Functions
		public void AddFont( string sKey,Microsoft.DirectX.Direct3D.Font oNewFont )
		{
			if( m_oLoadedFonts.ContainsKey( sKey ) == false )
			{
				m_oLoadedFonts.Add( sKey,oNewFont );
			}
		}
		public Microsoft.DirectX.Direct3D.Font GetFont( string sKey )
		{
			return( (Microsoft.DirectX.Direct3D.Font)m_oLoadedFonts.GetByKey( sKey ) );
		}
		public Microsoft.DirectX.Direct3D.Font GetFont()
		{
			return( GetFont( m_cSTANDARDFONT_KEY ) );
		}
		public Vector2 GetFontSize()
		{
			Vector2 vFontSize = new Vector2( 8,14 );
			return( vFontSize );
		}
		public Vector2 GetFontSize( string sKey )
		{
			//ToDo:  Figure out how to calculate the font size
			throw new System.Exception( "Font size calculations currently only work for the default font." );
		}
		#endregion


		#region Properties
		public Microsoft.DirectX.Direct3D.Device oDirect3DDevice
		{
			get
			{
				return( m_oDirect3DDevice );
			}
		}
		public DSSortedList GobFiles
		{
			get
			{
				return( m_oLoadedGobFiles );
			}
		}
		public DSSortedList LoadedSounds
		{
			get
			{
				return( m_oLoadedSounds );
			}
		}
		public DSSortedList LoadedFonts
		{
			get
			{
				return( m_oLoadedFonts );
			}
		}
		#endregion
	}
}