/*
 * 11/19/04 1.0 moved to LGPL.
 * 02/23/99 JavaConversion by E.B
 * Don Cross, April 1993.
 * RIFF file format classes.
 * See Chapter 8 of "Multimedia Programmer's Reference" in
 * the Microsoft Windows SDK.
 *  
 *-----------------------------------------------------------------------
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Library General Public License as published
 *   by the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *----------------------------------------------------------------------
 */

package javazoom.jl.converter;

/**
 * Class allowing WaveFormat Access
 */
public class WaveFile extends RiffFile
{
    public static final int 	MAX_WAVE_CHANNELS = 2;
    
	class WaveFormat_ChunkData
	{
   	   public short         wFormatTag = 0;       // Format category (PCM=1)
   	   public short         nChannels = 0;        // Number of channels (mono=1, stereo=2)
   	   public int         	nSamplesPerSec = 0;   // Sampling rate [Hz]
   	   public int         	nAvgBytesPerSec = 0;
   	   public short         nBlockAlign = 0;
   	   public short         nBitsPerSample = 0;
	   
	   public WaveFormat_ChunkData()
	   {
	      wFormatTag = 1;     // PCM
	      Config(44100,(short)16,(short)1);
   		}

   	   public void Config (int NewSamplingRate, short NewBitsPerSample, short NewNumChannels)
       {
      	  nSamplesPerSec  = NewSamplingRate;
      	  nChannels       = NewNumChannels;
      	  nBitsPerSample  = NewBitsPerSample;
      	  nAvgBytesPerSec = (nChannels * nSamplesPerSec * nBitsPerSample) / 8;
      	  nBlockAlign     = (short) ((nChannels * nBitsPerSample) / 8);
       }
   }


	class WaveFormat_Chunk
	{
   		public RiffChunkHeader         header;
   		public WaveFormat_ChunkData    data;

   		public WaveFormat_Chunk()
   		{
   		    header = new RiffChunkHeader();
   		    data = new WaveFormat_ChunkData();
      		header.ckID     =   FourCC("fmt ");
      		header.ckSize   =   16;
   		}

   		public int VerifyValidity()
   		{
      	    boolean ret = header.ckID == FourCC("fmt ") &&

            (data.nChannels == 1 || data.nChannels == 2) &&

             data.nAvgBytesPerSec == ( data.nChannels *
                                       data.nSamplesPerSec *
                                       data.nBitsPerSample    ) / 8   &&

             data.nBlockAlign == ( data.nChannels *
                                   data.nBitsPerSample ) / 8;
            if (ret == true) return 1;
            else return 0;
   		}
	}

	public class WaveFileSample
	{
   		public short[] 				chan;
		
		public WaveFileSample()
		{chan = new short[WaveFile.MAX_WAVE_CHANNELS];}
	}

   private WaveFormat_Chunk   	 wave_format;
   private RiffChunkHeader    	 pcm_data;
   private long            		 pcm_data_offset = 0;  // offset of 'pcm_data' in output file
   private int 	           		 num_samples = 0;


   /**
    * Constructs a new WaveFile instance. 
	*/
   public WaveFile()
   {
       pcm_data = new RiffChunkHeader();
       wave_format = new WaveFormat_Chunk();
   	   pcm_data.ckID = FourCC("data");
	   pcm_data.ckSize = 0;
   	   num_samples = 0;
   }

   /**
    *
	*
   public int OpenForRead (String Filename)
   {
      // Verify filename parameter as best we can...
      if (Filename == null)
      {
   	  	return DDC_INVALID_CALL;
      }
      int retcode = Open ( Filename, RFM_READ );
   
      if ( retcode == DDC_SUCCESS )
      {
   	  	retcode = Expect ( "WAVE", 4 );
   
   	  	if ( retcode == DDC_SUCCESS )
   	    {
   		 	retcode = Read(wave_format,24);
   
   		 	if ( retcode == DDC_SUCCESS && !wave_format.VerifyValidity() )
   		    {
   				// This isn't standard PCM, so we don't know what it is!
   				retcode = DDC_FILE_ERROR;
   		    }
   
   		    if ( retcode == DDC_SUCCESS )
   		    {
   			  pcm_data_offset = CurrentFilePosition();
   
   			  // Figure out number of samples from
   			  // file size, current file position, and
   			  // WAVE header.
   			  retcode = Read (pcm_data, 8 );
   			  num_samples = filelength(fileno(file)) - CurrentFilePosition();
   			  num_samples /= NumChannels();
   			  num_samples /= (BitsPerSample() / 8);
   		    }
   	    }
     }
     return retcode;
   }*/

   /**
    *
	*/
   public int OpenForWrite (String Filename, int SamplingRate, short BitsPerSample, short NumChannels)
   {
      // Verify parameters...
      if ( (Filename==null) ||
   		(BitsPerSample != 8 && BitsPerSample != 16) ||
   		NumChannels < 1 || NumChannels > 2 )
      {
   	  	return DDC_INVALID_CALL;
      }
   
      wave_format.data.Config ( SamplingRate, BitsPerSample, NumChannels );
   
      int retcode = Open ( Filename, RFM_WRITE );
   
      if ( retcode == DDC_SUCCESS )
      {
        byte [] theWave = {(byte)'W',(byte)'A',(byte)'V',(byte)'E'};
   	  	retcode = Write ( theWave, 4 );
   
   	  	if ( retcode == DDC_SUCCESS )
   	  	{
            // Ecriture de wave_format
            retcode = Write (wave_format.header, 8);   
            retcode = Write (wave_format.data.wFormatTag, 2);
            retcode = Write (wave_format.data.nChannels, 2);
            retcode = Write (wave_format.data.nSamplesPerSec, 4);
            retcode = Write (wave_format.data.nAvgBytesPerSec, 4);
            retcode = Write (wave_format.data.nBlockAlign, 2);
            retcode = Write (wave_format.data.nBitsPerSample, 2);
            /* byte[] br = new byte[16];
	        br[0] = (byte) ((wave_format.data.wFormatTag >> 8) & 0x00FF);
        	br[1] = (byte) (wave_format.data.wFormatTag & 0x00FF);
	  
        	br[2] = (byte) ((wave_format.data.nChannels >> 8) & 0x00FF);
	        br[3] = (byte) (wave_format.data.nChannels & 0x00FF);
	  
	        br[4] = (byte) ((wave_format.data.nSamplesPerSec >> 24)& 0x000000FF);
	        br[5] = (byte) ((wave_format.data.nSamplesPerSec >> 16)& 0x000000FF);
	        br[6] = (byte) ((wave_format.data.nSamplesPerSec >> 8)& 0x000000FF);
	        br[7] = (byte) (wave_format.data.nSamplesPerSec & 0x000000FF);
	  
	        br[8] = (byte) ((wave_format.data.nAvgBytesPerSec>> 24)& 0x000000FF);
	        br[9] = (byte) ((wave_format.data.nAvgBytesPerSec >> 16)& 0x000000FF);
	        br[10] = (byte) ((wave_format.data.nAvgBytesPerSec >> 8)& 0x000000FF);
	        br[11] = (byte) (wave_format.data.nAvgBytesPerSec & 0x000000FF);

	        br[12] = (byte) ((wave_format.data.nBlockAlign >> 8) & 0x00FF);
	        br[13] = (byte) (wave_format.data.nBlockAlign & 0x00FF);
	  
	        br[14] = (byte) ((wave_format.data.nBitsPerSample >> 8) & 0x00FF);
	        br[15] = (byte) (wave_format.data.nBitsPerSample & 0x00FF);   		 	
   		 	retcode = Write (br, 16); */
   		 	
   
   		 	if ( retcode == DDC_SUCCESS )
   		 	{
   				pcm_data_offset = CurrentFilePosition();
   				retcode = Write ( pcm_data, 8 );
   		 	}
   	  	}
      }
   
   	return retcode;
   }

   /**
    *
	*
   public int ReadSample ( short[] Sample )
   {
   
   }*/
   
   /**
    *
	*
   public int WriteSample( short[] Sample )
   {
      int retcode = DDC_SUCCESS;
      switch ( wave_format.data.nChannels )
      {
   	  	case 1:
   		   switch ( wave_format.data.nBitsPerSample )
   		   {
   			  case 8:
   				   pcm_data.ckSize += 1;
   				   retcode = Write ( Sample, 1 );
   				   break;
   
   			  case 16:
   				   pcm_data.ckSize += 2;
   				   retcode = Write ( Sample, 2 );
   				   break;
   
   			  default:
   				   retcode = DDC_INVALID_CALL;
   		   }
   		   break;
   
   	  	case 2:
   		   switch ( wave_format.data.nBitsPerSample )
   		   {
   			  case 8:
   				   retcode = Write ( Sample, 1 );
   				   if ( retcode == DDC_SUCCESS )
   				   {
   				      // &Sample[1]
   					  retcode = Write (Sample, 1 );
   					  if ( retcode == DDC_SUCCESS )
   					  {
   						 pcm_data.ckSize += 2;
   					  }
   				   }
   				   break;
   
   			  case 16:
   				   retcode = Write ( Sample, 2 );
   				   if ( retcode == DDC_SUCCESS )
   				   {
                      // &Sample[1]
   					  retcode = Write (Sample, 2 );
   					  if ( retcode == DDC_SUCCESS )
   					  {
   						 pcm_data.ckSize += 4;
   					  }
   				   }
   				   break;
   
   			  default:
   				   retcode = DDC_INVALID_CALL;
   		   }
   		   break;
   
   	  	default:
   		   retcode = DDC_INVALID_CALL;
      }
   
   	  return retcode;
	}*/
	
	/**
	 *
	 *
   public int SeekToSample ( long SampleIndex )
   {
      if ( SampleIndex >= NumSamples() )
      {
   	    return DDC_INVALID_CALL;
      }
      int SampleSize = (BitsPerSample() + 7) / 8;
      int rc = Seek ( pcm_data_offset + 8 +
   					  SampleSize * NumChannels() * SampleIndex );
   	  return rc;
   }*/

   /**
    * Write 16-bit audio
	*/
   public int WriteData ( short[] data, int numData )
   {
   	  int extraBytes = numData * 2;
   	  pcm_data.ckSize += extraBytes;
	  return super.Write ( data, extraBytes );
   }

   /**
    * Read 16-bit audio.
	*
   public int ReadData  (short[] data, int numData)
   {return super.Read ( data, numData * 2);} */

   /**
    * Write 8-bit audio.
	*
   public int WriteData ( byte[] data, int numData )
   {
   	  pcm_data.ckSize += numData;
	  return super.Write ( data, numData );
   }*/

   /**
    * Read 8-bit audio.
	*
   public int ReadData ( byte[] data, int numData )
   {return super.Read ( data, numData );} */
   
   
   /**
    *
	*
   public int ReadSamples  (int num, int [] WaveFileSample)
   {
   
   }*/

   /**
    *
	*
   public int WriteMonoSample ( short[] SampleData )
   {
      switch ( wave_format.data.nBitsPerSample )
      {
   	  	case 8:
   		   pcm_data.ckSize += 1;
   		   return Write ( SampleData, 1 );
   
   	  	case 16:
   		   pcm_data.ckSize += 2;
   		   return Write ( SampleData, 2 );
      }
   	  return DDC_INVALID_CALL;
   }*/

   /**
    *
	*
   public int WriteStereoSample  ( short[] LeftSample, short[] RightSample )
   {
      int retcode = DDC_SUCCESS;
      switch ( wave_format.data.nBitsPerSample )
      {
   	  	case 8:
   		   retcode = Write ( LeftSample, 1 );
   		   if ( retcode == DDC_SUCCESS )
   		   {
   			  retcode = Write ( RightSample, 1 );
   			  if ( retcode == DDC_SUCCESS )
   			  {
   				 pcm_data.ckSize += 2;
   			  }
   		   }
   		   break;
   
   	  	case 16:
   		   retcode = Write ( LeftSample, 2 );
   		   if ( retcode == DDC_SUCCESS )
   		   {
   			  retcode = Write ( RightSample, 2 );
   			  if ( retcode == DDC_SUCCESS )
   			  {
   				 pcm_data.ckSize += 4;
   			  }
   		   }
   		   break;
   
   	  	default:
   		   retcode = DDC_INVALID_CALL;
      }   
      return retcode;
   }*/
   
   /**
    *
	*
   public int ReadMonoSample ( short[] Sample )
   {
      int retcode = DDC_SUCCESS;
      switch ( wave_format.data.nBitsPerSample )
      {
   	  	case 8:
   		   byte[] x = {0};
   		   retcode = Read ( x, 1 );
   		   Sample[0] = (short)(x[0]);
   		   break;
   
   	  	case 16:
   		   retcode = Read ( Sample, 2 );
   		   break;
   
   	  default:
   		   retcode = DDC_INVALID_CALL;
      }
   	return retcode;
   }*/

   /**
    *
	*
   public int ReadStereoSample ( short[] LeftSampleData, short[] RightSampleData )
   {
      int retcode = DDC_SUCCESS;
      byte[] x = new byte[2];
      short[] y = new short[2];
      switch ( wave_format.data.nBitsPerSample )
      {
   	  	case 8:
   		   retcode = Read ( x, 2 );
   		   L[0] = (short) ( x[0] );
   		   R[0] = (short) ( x[1] );
   		   break;
   
   	    case 16:
   		   retcode = Read ( y, 4 );
   		   L[0] = (short) ( y[0] );
   		   R[0] = (short) ( y[1] );
   		   break;
   
   	    default:
   		   retcode = DDC_INVALID_CALL;
      }
   	 return retcode;
   }*/

   
   /**
    *
	*/
   public int Close()
   {
      int rc = DDC_SUCCESS;
      
      if ( fmode == RFM_WRITE )
         rc = Backpatch ( pcm_data_offset, pcm_data, 8 );
      if ( rc == DDC_SUCCESS )
   	  rc = super.Close();
      return rc;
   }

   // [Hz]
   public int SamplingRate()
   {return wave_format.data.nSamplesPerSec;}

   public short BitsPerSample()
   {return wave_format.data.nBitsPerSample;}

   public short NumChannels()
   {return wave_format.data.nChannels;}

   public int NumSamples()
   {return num_samples;}

 
   /**
    * Open for write using another wave file's parameters...
	*/
   public int OpenForWrite (String Filename, WaveFile OtherWave )
   {
      return OpenForWrite ( Filename,
                            OtherWave.SamplingRate(),
                            OtherWave.BitsPerSample(),
                            OtherWave.NumChannels() );
   }

   /**
    *
	*/
   public long CurrentFilePosition()
   {
      return super.CurrentFilePosition();
   }

   /* public int FourCC(String ChunkName)
   {
      byte[] p = {0x20,0x20,0x20,0x20};
	  ChunkName.getBytes(0,4,p,0);
	  int ret = (((p[0] << 24)& 0xFF000000) | ((p[1] << 16)&0x00FF0000) | ((p[2] << 8)&0x0000FF00) | (p[3]&0x000000FF));
      return ret;
   }*/

}