/********************************************************************
 *                                                                  *
 * THIS FILE IS PART OF THE aoTuvWriter SOURCE CODE.                *
 * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     *
 * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
 * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
 *                                                                  *
 * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007             *
 * by the Xiph.Org Foundation http://www.xiph.org/                  *
 *                                                                  *
 * THE aoTuv SOURCE CODE IS (C) COPYRIGHT 2003-2008 Aoyumi          *
 * http://www.geocities.jp/aoyoume/aotuv/                           *
 *                                                                  *
 * THE aoTuvWriter SOURCE CODE IS (C) COPYRIGHT 2008 Cocha          *
 * http://sourceforge.jp/projects/directshow/                       *
 *                                                                  *
 ********************************************************************/

#include "aoTuvWriter.h"

// クラスファクトリ用テンプレート
const AMOVIESETUP_MEDIATYPE sudPinTypes =
{
    &MEDIATYPE_NULL,    // Major type
    &MEDIASUBTYPE_NULL  // Minor type
};

const AMOVIESETUP_PIN sudPins =
{
    L"Input",            // Pin string name
    FALSE,               // Is it rendered
    FALSE,               // Is it an output
    FALSE,               // Allowed none
    FALSE,               // Likewise many
    &CLSID_NULL,         // Connects to filter
    L"Output",           // Connects to pin
    1,                   // Number of types
    &sudPinTypes         // Pin information
};

const AMOVIESETUP_FILTER filterInfo =
{
    &CLSID_aoTuvWriter,  // Filter CLSID
    FILTER_NAME,         // String name
    MERIT_DO_NOT_USE,    // Filter merit
    1,                   // Number pins
    &sudPins             // Pin details
};


CFactoryTemplate g_Templates [] = {
   { FILTER_NAME,      &CLSID_aoTuvWriter,     CaoTuvWriter::CreateInstance, NULL, &filterInfo },
   { FILTER_PROP_NAME, &CLSID_aoTuvWriterProp, CaoTuvProperties::CreateInstance, NULL, NULL}
};

int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);
//int g_cTemplates = 2;

// -----------------------------------------------------------------------------------------------------------------------------------
int CaoTuvWriter::GetPinCount()
{  // ピンの数を返す(当フィルターは入力ピンが1つ)

   if(m_pInputPin == NULL)
      return 0;

   return 1;
}
// -----------------------------------------------------------------------------------------------------------------------------------
CBasePin *CaoTuvWriter::GetPin(int n)
{  // n番目のピンのポインタを返す(当フィルターは入力ピンが1つ)

   if(n == 0)
      return m_pInputPin;

   return NULL;
}
// -----------------------------------------------------------------------------------------------------------------------------------
STDMETHODIMP CaoTuvWriter::NonDelegatingQueryInterface(REFIID riid, void ** ppv)
{  // インターフェイスを取得し、参照カウントをインクリメントする

   CheckPointer(ppv, E_POINTER);
   CAutoLock lock(&m_Lock);

   if (riid == IID_IFileSinkFilter)
   {
      return GetInterface((IFileSinkFilter *)this, ppv);
   }
   else if(riid == IID_ISpecifyPropertyPages)
   {
      return GetInterface((ISpecifyPropertyPages *)this, ppv);
   }
   else if(riid == IID_IaoTuvInterface)
   {
      return GetInterface((IaoTuvInterface *)this, ppv);
   }
   else if (riid == IID_IMediaPosition || riid == IID_IMediaSeeking)
   {
      if (m_pPosition == NULL) 
      {
         HRESULT hr = S_OK;
         m_pPosition = new CPosPassThru(NAME("aoTuvWriter Pass Through"), (IUnknown *) GetOwner(), (HRESULT *) &hr, m_pInputPin);

         if (m_pPosition == NULL) 
            return E_OUTOFMEMORY;

         if (FAILED(hr)) 
         {
            delete m_pPosition;
            m_pPosition = NULL;
            return hr;
         }
      }

      return m_pPosition->NonDelegatingQueryInterface(riid, ppv);
   } 

   return CBaseFilter::NonDelegatingQueryInterface(riid, ppv);
}
// -----------------------------------------------------------------------------------------------------------------------------------
CaoTuvWriter::CaoTuvWriter(LPUNKNOWN pUnk, HRESULT *phr)
   : CBaseFilter(FILTER_NAME, pUnk, &m_Lock, CLSID_aoTuvWriter) , m_pPosition(NULL)
{  // CaoTuvWriterのコンストラクタ
   ASSERT(phr);

   // 入力ピンの作成
   m_pInputPin = new CInputPin(this, GetOwner(), &m_Lock, &m_ReceiveLock, phr);
   if (m_pInputPin == NULL)
   {
      if(phr)
         *phr = E_OUTOFMEMORY;

      return;
   }

   // 変数の初期化
   ::ZeroMemory(m_awFileName, sizeof(m_awFileName));

   if(phr)
      *phr = S_OK;
}
// -----------------------------------------------------------------------------------------------------------------------------------
CaoTuvWriter::~CaoTuvWriter()
{  // CaoTuvWriterのデコンストラクタ

   // 入力ピンの解放
   if(m_pInputPin != NULL)
   {
      delete m_pInputPin;
      m_pInputPin = NULL;
   }

   if(m_pPosition != NULL)
   {
      delete m_pPosition;
      m_pPosition = NULL;
   }
}
// -----------------------------------------------------------------------------------------------------------------------------------
STDMETHODIMP CaoTuvWriter::SetFileName(LPCOLESTR pszFileName,const AM_MEDIA_TYPE *pmt)
{  // 出力ファイル名を設定する

   CheckPointer(pszFileName,E_POINTER);
   if(wcslen(pszFileName) > MAX_PATH)
      return ERROR_FILENAME_EXCED_RANGE;

   // すでにファイルが存在するなら、ファイルを削除する
   if(::PathFileExists(pszFileName) != FALSE)
   {
      if(::DeleteFile(pszFileName) == FALSE)
         return E_FAIL; // ファイル削除に失敗
   }

   // ファイルを作成できるかチェック
   HANDLE hFile = NULL;
   hFile = ::CreateFile(pszFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, (DWORD)0, NULL);
   if(hFile == INVALID_HANDLE_VALUE)
      return E_FAIL; // ファイル作成に失敗

   ::CloseHandle(hFile);

   // ファイル名を格納しておく
   lstrcpyW(m_awFileName, pszFileName);
   m_pInputPin->SetFileName(pszFileName);

   return S_OK;
}
// -----------------------------------------------------------------------------------------------------------------------------------
STDMETHODIMP CaoTuvWriter::GetCurFile(LPOLESTR * ppszFileName,AM_MEDIA_TYPE *pmt)
{  // 出力ファイル名を取得する

   CheckPointer(ppszFileName, E_POINTER);
   *ppszFileName = NULL;

   if (m_awFileName != NULL) 
   {
      *ppszFileName = (LPOLESTR)
      QzTaskMemAlloc(sizeof(WCHAR) * (1+lstrlenW(m_awFileName)));

      if (*ppszFileName != NULL) 
         lstrcpyW(*ppszFileName, m_awFileName);
   }

   if(pmt != NULL)
   {
      ZeroMemory(pmt, sizeof(*pmt));
      pmt->majortype = MEDIATYPE_NULL;
      pmt->subtype = MEDIASUBTYPE_NULL;
   }

   return S_OK;
}
// -----------------------------------------------------------------------------------------------------------------------------------
STDMETHODIMP CaoTuvWriter::SetVorbisFormat(void *pVorbisFormat)
{  // エンコード設定をする

   CheckPointer(pVorbisFormat, E_POINTER);
   CheckPointer(m_pInputPin, E_POINTER);

   m_pInputPin->SetVorbisFormat((VORBISFORMAT *)pVorbisFormat);

   return S_OK;
}
// -----------------------------------------------------------------------------------------------------------------------------------
STDMETHODIMP CaoTuvWriter::GetVorbisFormat(void *pVorbisFormat)
{  // エンコード設定を取得する

   CheckPointer(pVorbisFormat, E_POINTER);
   CheckPointer(m_pInputPin, E_POINTER);

   m_pInputPin->GetVorbisFormat((VORBISFORMAT *)pVorbisFormat);

   return S_OK;
}
// -----------------------------------------------------------------------------------------------------------------------------------
STDMETHODIMP CaoTuvWriter::SetVorbisTag(void *pVorbisTag)
{  // タグを設定をする

   CheckPointer(pVorbisTag, E_POINTER);
   CheckPointer(m_pInputPin, E_POINTER);

   m_pInputPin->SetVorbisTag((VORBISTAG *)pVorbisTag);

   return S_OK;
}
// -----------------------------------------------------------------------------------------------------------------------------------
STDMETHODIMP CaoTuvWriter::GetPages(CAUUID *pPages)
{  // プロパティページの設定をする

   CheckPointer(pPages, E_POINTER);

   pPages->cElems = 1;
   pPages->pElems = (GUID*)CoTaskMemAlloc(sizeof(GUID));

   if(pPages->pElems == NULL)
      return E_OUTOFMEMORY;

   pPages->pElems[0] = CLSID_aoTuvWriterProp;

   return S_OK;
}
// -----------------------------------------------------------------------------------------------------------------------------------
CUnknown * WINAPI CaoTuvWriter::CreateInstance(LPUNKNOWN punk, HRESULT *phr)
{  // オブジェクトを作成する

   ASSERT(phr);
    
   CaoTuvWriter *pNewObject = new CaoTuvWriter(punk, phr);
   if (pNewObject == NULL)
   {
      if(phr != NULL)
         *phr = E_OUTOFMEMORY;
   }

   return dynamic_cast<CUnknown *>(pNewObject);
}
// -----------------------------------------------------------------------------------------------------------------------------------
STDAPI DllRegisterServer()
{
   return AMovieDllRegisterServer2(TRUE);
}
// -----------------------------------------------------------------------------------------------------------------------------------
STDAPI DllUnregisterServer()
{
   return AMovieDllRegisterServer2(FALSE);
}
// -----------------------------------------------------------------------------------------------------------------------------------
// DllEntryPoint
extern "C" {
	BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID);
};

BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
{
   return DllEntryPoint((HINSTANCE)(hModule), dwReason, lpReserved);
}
