/**************************************************************************
 * Copyright (C) 2008 Cocha                                               *
 * http://sourceforge.jp/projects/ecodecotool/                            *
 *                                                                        *
 *  This Program is free software; you can redistribute it and/or modify  *
 *  it under the terms of the GNU General Public License as published by  *
 *  the Free Software Foundation; either version 2, 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 General Public License for more details.                          *
 *                                                                        *
 *  You should have received a copy of the GNU General Public License     *
 *  along with GNU Make; see the file COPYING.  If not, write to          *
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. *
 *                                                                        *
 **************************************************************************/

#include "EcoDecoTooL.h"


CConvertToAac::CConvertToAac()
{
   m_pEcoDecoWriterInterface = NULL;
   ::ZeroMemory(&m_awTmpFileName, sizeof(m_awTmpFileName));
   ::ZeroMemory(&m_awOutputFileName, sizeof(m_awOutputFileName));
   ::ZeroMemory(&m_pi, sizeof(m_pi));
}

CConvertToAac::~CConvertToAac()
{
   SAFE_RELEASE(m_pEcoDecoWriterInterface);
}

void CConvertToAac::Release(void)
{
   
   SAFE_RELEASE(m_pEcoDecoWriterInterface);

   if(m_pi.hProcess != NULL)
   {
      ::CloseHandle(m_pi.hProcess);
      m_pi.hProcess = NULL;
   }
}

void CConvertToAac::GetFormat(WCHAR *pwInputFormat, WCHAR *pwOutputFormat)
{
   if(m_pi.hProcess != NULL)
   {  
      ::lstrcpy(pwInputFormat, L"-");
      ::lstrcpy(pwOutputFormat, L"-");
   }
   else 
   {  

      HRESULT hr;
      WAVEFORMATEX inputFormat, outputFormat;

      hr = GetDirectShowFormat(&inputFormat, &outputFormat);

      if(FAILED(hr))
      {
         ::lstrcpy(pwInputFormat, L"-");
         ::lstrcpy(pwOutputFormat, L"-");
      }
      else
      {
         ::wsprintf(pwInputFormat, L"%dHz %2dbit %dch", inputFormat.nSamplesPerSec, inputFormat.wBitsPerSample, inputFormat.nChannels);
         ::wsprintf(pwOutputFormat, L"%dHz %2dbit %dch", outputFormat.nSamplesPerSec, outputFormat.wBitsPerSample, outputFormat.nChannels);
      }
   }
}

HRESULT CConvertToAac::GetReplayGain(double *pdPeak, double *pdGain)
{
   if(m_pEcoDecoWriterInterface == NULL)
      return E_FAIL;

   m_pEcoDecoWriterInterface->GetReplayGain(pdPeak, pdGain);

   return S_OK;
}

int CConvertToAac::ConvertStart(HWND hWnd, WCHAR *pwInputFileName, WCHAR *pwOutputFolder, CONVERTDATA2 *pConvertData, int nWait)
{  

   HRESULT hr;
   bool bUseNeroAacEnc = false;
   bool bDirectConvert = false;
   WCHAR awOutputFileName[MAX_PATH];

   if(pConvertData->bNormalize == true)
   {  
      
      pConvertData->bUseFrontEnd = false;

      hr = Render(hWnd, pwInputFileName, NULL, pConvertData, nWait);
      if(FAILED(hr)) goto error;
   }
   else if( pConvertData->nPass == 1 ||
           (pConvertData->nGainMode == GAIN_TRACK_NORMALIZE && pConvertData->nPass == 2) ||
           (pConvertData->nGainMode == GAIN_ALBUM_NORMALIZE && pConvertData->nPass == 2) )
   {  
      

      
      HANDLE hFile = NULL;
      int nSignature = 0;
      WAVEFORMATEX wf;
      DWORD dwRead;

      
      hFile = ::CreateFile(pwInputFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, (DWORD)0, NULL);
      if(hFile != NULL && hFile != (HANDLE)0xffffffff)
      {
         ::ZeroMemory(&wf, sizeof(wf));

         ::ReadFile(hFile, &nSignature, 4, &dwRead, NULL);
         if(nSignature == 0x46464952) 
         {
            ::SetFilePointer(hFile, 16, NULL, FILE_CURRENT);

            ::ReadFile(hFile, &wf, sizeof(wf), &dwRead, NULL);
            SAFE_CLOSEHANDLE(hFile);

            if( (wf.wFormatTag == 1 || wf.wFormatTag == 3) && wf.nSamplesPerSec <= 96000 && wf.wBitsPerSample <= 32)
            {  

               
               hr = GetOutputFileName(awOutputFileName, pwInputFileName, pwOutputFolder, L".m4a", true);
               if(FAILED(hr)) goto error;

               
               hr = NeroAacEnc(pwInputFileName, awOutputFileName, pConvertData);
               if(FAILED(hr)) goto error;

               pConvertData->bUseFrontEnd = true;

               
               ::lstrcpy(m_awOutputFileName, awOutputFileName);

               return STATE_CONVERTING;
            }
         }

         SAFE_CLOSEHANDLE(hFile);
      }

      

      
      hr = GetOutputFileName(awOutputFileName, pwInputFileName, pwOutputFolder, L".wav", true);
      if(FAILED(hr)) goto error;

      pConvertData->bUseFrontEnd = false;

      
      hr = Render(hWnd, pwInputFileName, awOutputFileName, pConvertData, nWait);
      if(FAILED(hr)) goto error;

      
      ::lstrcpy(m_awOutputFileName, awOutputFileName);
   }
   else
   {  

      
      hr = GetOutputFileName(awOutputFileName, m_awOutputFileName, pwOutputFolder, L".m4a", true);
      if(FAILED(hr)) goto error;

      
      hr = NeroAacEnc(m_awOutputFileName, awOutputFileName, pConvertData);
      if(FAILED(hr)) goto error;

      pConvertData->bUseFrontEnd = true;

      
      ::lstrcpy(m_awTmpFileName, m_awOutputFileName);

      
      ::lstrcpy(m_awOutputFileName, awOutputFileName);
   }

   return STATE_CONVERTING;

error:

   return STATE_END_ERROR;
}

int CConvertToAac::Converting(int *pnPercent)
{  

   if(m_pi.hProcess != NULL)
   {  
      DWORD dwResult;
      dwResult = ::WaitForSingleObject(m_pi.hProcess, 50);

      if(dwResult == WAIT_OBJECT_0)
      {  
         *pnPercent = -1;
         return STATE_END_SUCCESS;
      }

      *pnPercent = -1;
      return STATE_CONVERTING;
   }

   
   *pnPercent = GetPercent();
   return STATE_CONVERTING;
}

int CConvertToAac::ConvertEnd(CONVERTDATA2 *pConvertData)
{  

   if(m_pi.hProcess != NULL)
   {  

      ::CloseHandle(m_pi.hProcess);
      m_pi.hProcess = NULL;

      
      if(::lstrlen(m_awTmpFileName) > 0)
      {
         if(::PathFileExists(m_awTmpFileName) != FALSE)
            ::DeleteFile(m_awTmpFileName);

         ::lstrcpy(m_awTmpFileName, L"");
      }

      
      ::lstrcpy(m_awTmpFileName, L"");

      
      ::lstrcpy(m_awOutputFileName, L"");

      return STATE_START_NEXTFILE;
   }

   if(pConvertData->bNormalize == true)
   {
      HRESULT hr;
      double dPeak, dGain;

      
      hr = GetReplayGain(&dPeak, &dGain);

      
      dGain = dGain + (pConvertData->dTargetGain - 89.0);

      
      ReleaseDirectShow();

      if(SUCCEEDED(hr))
      {
         if(pConvertData->nGainMode == GAIN_ALBUM_NORMALIZE)
         {
            if(pConvertData->dAlbumPeak < dPeak)
               pConvertData->dAlbumPeak = dPeak;

            if(dGain < pConvertData->dAlbumGain) 
               pConvertData->dAlbumGain = dGain;
         }
         else 
            pConvertData->dNormalize = ::pow(10.0, dGain / 20.0);
      }
   }
   else
   {
      
      ReleaseDirectShow();
   }

   
   if(pConvertData->nGainMode == GAIN_ALBUM_NORMALIZE && pConvertData->bNormalize == true)
      return STATE_START_NEXTFILE;

   return STATE_START_SAMEFILE;
}

void CConvertToAac::ConvertExit(void)
{  

   if(m_pi.hProcess != NULL)
   {  

      
      ::TerminateProcess(m_pi.hProcess, 0);
      ::WaitForSingleObject(m_pi.hProcess, 500);
      ::CloseHandle(m_pi.hProcess);
      m_pi.hProcess = NULL;

      
      if(::lstrlen(m_awTmpFileName) > 0)
      {
         if(::PathFileExists(m_awTmpFileName) != FALSE)
            ::DeleteFile(m_awTmpFileName);
      }
   }
   else
   {  
      
      ReleaseDirectShow();
   }

   
   if(::PathFileExists(m_awOutputFileName) != FALSE)
      ::DeleteFile(m_awOutputFileName);
}

HRESULT CConvertToAac::NeroAacEnc(WCHAR *pwInputFileName, WCHAR *pwOutputFileName, CONVERTDATA2 *pConvertData)
{
   WCHAR awText[32], awCommandLine[1024];

   
   ::GetModuleFileName(NULL, awCommandLine, 1024);
   ::PathRemoveFileSpec(awCommandLine);
   ::lstrcat(awCommandLine, L"\\NeroAacEnc.exe ");

   if(pConvertData->nEncodeMode == 0)
      swprintf_s(awText, 32, L"-br %d ", pConvertData->nABRCBR);
   else if(pConvertData->nEncodeMode == 1)
      swprintf_s(awText, 32, L"-cbr %d ", pConvertData->nABRCBR);
   else 
      swprintf_s(awText, 32, L"-q %1.1f ", pConvertData->fVBR);

   ::lstrcat(awCommandLine, awText);

   
   if(pConvertData->nEncodeMode == 0)
      ::lstrcat(awCommandLine, L"-2pass ");

   if(pConvertData->nAdvancedMode == 1)
      ::lstrcat(awCommandLine, L"-lc ");
   else if(pConvertData->nAdvancedMode == 2)
      ::lstrcat(awCommandLine, L"-he ");
   else if(pConvertData->nAdvancedMode == 3)
      ::lstrcat(awCommandLine, L"-hev2 ");

   
   ::lstrcat(awCommandLine, L"-if ");
   ::lstrcat(awCommandLine, L"\"");
   ::lstrcat(awCommandLine, pwInputFileName);
   ::lstrcat(awCommandLine, L"\"");

   
   ::lstrcat(awCommandLine, L" -of ");
   ::lstrcat(awCommandLine, L"\"");
   ::lstrcat(awCommandLine, pwOutputFileName);
   ::lstrcat(awCommandLine, L"\"");

   
   STARTUPINFO si;
   ::ZeroMemory(&si, sizeof(STARTUPINFO));
   si.cb          = sizeof(STARTUPINFO);
   si.dwFlags     = STARTF_USESHOWWINDOW;
   si.wShowWindow = SW_HIDE;

   
   ::CreateProcess(NULL, awCommandLine, NULL, NULL, FALSE, DETACHED_PROCESS,NULL, NULL, &si, &m_pi);
   ::CloseHandle(m_pi.hThread);

   if(m_pi.hThread == NULL)
      return E_FAIL;

   return S_OK;
}

HRESULT CConvertToAac::RenderGraph(IGraphBuilder *pGraphBuilder, WCHAR *pwInputFileName, WCHAR *pwOutputFileName, IBaseFilter *pConvertWav, IConvertWavInterface *pConvertWavInterface , CONVERTDATA2 *pConvertData)
{
   HRESULT hr;
   WAVEFORMATEX outFormat;

   IBaseFilter *pWriter = NULL;

   
   hr = RenderByFileExt(pGraphBuilder, pwInputFileName, pConvertWav, false); 

   if(FAILED(hr))
   {  
      hr = RenderByAllFilter(pGraphBuilder, pwInputFileName, pConvertWav, false); 
   }

   if(FAILED(hr)) goto error;

   
   pConvertWavInterface->GetInFormat(&outFormat);

   
   
   if(outFormat.wFormatTag == 3 && outFormat.wBitsPerSample == 64)
      outFormat.wBitsPerSample = 32;

   if(outFormat.nSamplesPerSec == 176400)
      outFormat.nSamplesPerSec = 88200;
   else if(outFormat.nSamplesPerSec == 192000)
      outFormat.nSamplesPerSec = 96000;

   outFormat.nBlockAlign = outFormat.nChannels * (outFormat.wBitsPerSample / 8);
   outFormat.nAvgBytesPerSec = outFormat.nSamplesPerSec * outFormat.nBlockAlign;

   pConvertWavInterface->SetOutFormat(&outFormat);

   
   if(pConvertData->bNormalize == false)
      pWriter = AddWriteFilter(pGraphBuilder, CLSID_EcoDecoWriter, pwOutputFileName);
   else
      pWriter = AddFilter(pGraphBuilder, CLSID_EcoDecoWriter);

   
   pWriter->QueryInterface(IID_IEcoDecoWriterInterface, (void **)&m_pEcoDecoWriterInterface);
   if(m_pEcoDecoWriterInterface == NULL) goto error;

   
   m_pEcoDecoWriterInterface->SetOutputMode(101);

   
   if(pConvertData->bNormalize == true)
      m_pEcoDecoWriterInterface->CheckVolumeMode(true);

   
   hr = ConnectDirect(pGraphBuilder, pConvertWav, pWriter, NULL);
   if(FAILED(hr)) goto error;

   SAFE_RELEASE(pWriter);
   return S_OK;

error:
   SAFE_RELEASE(pWriter);
   return E_FAIL;
}

