#include "WWPcmStream.h"
#include "WWCommonUtil.h"
#include <assert.h>

WWPcmStream::WWPcmStream(void)
        : m_nowPlayingPcmData(nullptr),
          m_pauseResumePcmData(nullptr),
          m_streamType(WWStreamPcm),
          m_zeroFlushMillisec(0)
{
}

void
WWPcmStream::SetStreamType(WWStreamType t)
{
    m_streamType = t;
}

void
WWPcmStream::SetZeroFlushMillisec(int zeroFlushMillisec)
{
    assert(0 <= zeroFlushMillisec);
    m_zeroFlushMillisec = zeroFlushMillisec;
}

void
WWPcmStream::PrepareSilenceBuffers(DWORD latencyMillisec, WWPcmDataSampleFormatType deviceSampleFormat,
        int deviceSampleRate, int deviceNumChannels, int deviceBytesPerFrame)
{

    {
        DWORD startZeroFlushMillisec = m_zeroFlushMillisec;
        if (startZeroFlushMillisec < latencyMillisec) {
            startZeroFlushMillisec = latencyMillisec;
        }
        m_startSilenceBuffer.Init(-1, deviceSampleFormat, deviceNumChannels,
                (1 * (int)((int64_t)deviceSampleRate * startZeroFlushMillisec / 1000) + 1) & (~1),
                deviceBytesPerFrame, WWPcmDataContentSilenceForTrailing, WWStreamPcm);
    }

    m_unpauseSilenceBuffer.Init(-1, deviceSampleFormat, deviceNumChannels,
            (1 * (int)((int64_t)deviceSampleRate * latencyMillisec / 1000) + 1) & (~1),
            deviceBytesPerFrame, WWPcmDataContentSilenceForPause, WWStreamPcm);

    m_endSilenceBuffer.Init(-1, deviceSampleFormat, deviceNumChannels,
            (4 * (int)((int64_t)deviceSampleRate * latencyMillisec / 1000) + 1) & (~1),
            deviceBytesPerFrame, WWPcmDataContentSilenceForEnding, WWStreamPcm);
    m_endSilenceBuffer.SetNext(nullptr);

    m_spliceBuffer.Init(-1, deviceSampleFormat, deviceNumChannels,
            (deviceSampleRate / 100 + 1) & (~1),
            deviceBytesPerFrame, WWPcmDataContentSplice, WWStreamPcm);

    m_pauseBuffer.Init(-1, deviceSampleFormat, deviceNumChannels,
            (deviceSampleRate / 100 + 1) & (~1),
            deviceBytesPerFrame, WWPcmDataContentSplice, WWStreamPcm);

    switch (m_streamType) {
    case WWStreamPcm:

        break;
    case WWStreamDop:
        m_startSilenceBuffer.FillDopSilentData();
        m_unpauseSilenceBuffer.FillDopSilentData();
        m_endSilenceBuffer.FillDopSilentData();
        m_spliceBuffer.FillDopSilentData();
        m_pauseBuffer.FillDopSilentData();
        break;
    default:
        assert(0);
        break;
    }
}

void
WWPcmStream::ReleaseBuffers(void)
{
    m_spliceBuffer.Term();
    m_pauseBuffer.Term();
    m_startSilenceBuffer.Term();
    m_unpauseSilenceBuffer.Term();
    m_endSilenceBuffer.Term();

    m_nowPlayingPcmData = nullptr;

}

void
WWPcmStream::Paused(WWPcmData *pauseResume)
{
    m_pauseResumePcmData = pauseResume;

    m_pauseBuffer.SetPosFrame(0);
    m_pauseBuffer.SetNext(&m_endSilenceBuffer);

    m_endSilenceBuffer.SetPosFrame(0);
    m_endSilenceBuffer.SetNext(nullptr);

    m_pauseBuffer.UpdateSpliceDataWithStraightLine(
        *m_nowPlayingPcmData, m_nowPlayingPcmData->PosFrame(),
        m_endSilenceBuffer,   m_endSilenceBuffer.PosFrame());

    m_nowPlayingPcmData = &m_pauseBuffer;
}

bool
WWPcmStream::IsSilenceBuffer(WWPcmData *p) const
{
    return p == &m_spliceBuffer ||
           p == &m_startSilenceBuffer ||
           p == &m_unpauseSilenceBuffer ||
           p == &m_endSilenceBuffer ||
           p == &m_pauseBuffer;
}

WWPcmData *
WWPcmStream::UnpausePrepare(void)
{

    assert(m_pauseResumePcmData);

    dprintf("%s resume=%p posFrame=%d\n", __FUNCTION__, m_pauseResumePcmData, m_pauseResumePcmData->PosFrame());

    m_unpauseSilenceBuffer.SetPosFrame(0);
    m_unpauseSilenceBuffer.SetNext(&m_pauseBuffer);

    m_pauseBuffer.SetPosFrame(0);
    m_pauseBuffer.SetNext(m_pauseResumePcmData);

    m_pauseBuffer.UpdateSpliceDataWithStraightLine(
            m_unpauseSilenceBuffer, m_unpauseSilenceBuffer.PosFrame(),
            *m_pauseResumePcmData, m_pauseResumePcmData->PosFrame());

    return &m_unpauseSilenceBuffer;
}

void
WWPcmStream::UnpauseDone(void)
{
    m_pauseResumePcmData = nullptr;
}

void
WWPcmStream::UpdateStartPcm(WWPcmData *startPcm)
{
    m_nowPlayingPcmData = &m_startSilenceBuffer;
    m_nowPlayingPcmData->SetNext(startPcm);
}

void
WWPcmStream::UpdatePlayRepeat(bool repeat, WWPcmData *startPcmData, WWPcmData *endPcmData)
{
    assert(!IsSilenceBuffer(startPcmData));
    assert(!IsSilenceBuffer(endPcmData));

    if (!repeat) {

        endPcmData->SetNext(&m_endSilenceBuffer);
    } else {

        endPcmData->SetNext(startPcmData);
    }
}

WWPcmData *
WWPcmStream::GetPcm(WWPcmDataUsageType t)
{
    WWPcmData *pcm = nullptr;

    switch (t) {
    case WWPDUNowPlaying:
        pcm = m_nowPlayingPcmData;
        break;
    case WWPDUPauseResumeToPlay:
        pcm = m_pauseResumePcmData;
        break;
    case WWPDUSplice:
        pcm = &m_spliceBuffer;
        break;
    case WWPDUSpliceNext:
        pcm = m_spliceBuffer.Next();
        break;
    case WWPDUCapture:
        assert(0);
        break;
    default:
        assert(0);
        break;
    }

    return pcm;
}

WWPcmData *
WWPcmStream::GetSilenceBuffer(WWPcmDataContentType t)
{
    WWPcmData *pcm = nullptr;

    switch (t) {
    case WWPcmDataContentSilenceForTrailing:
        pcm = &m_startSilenceBuffer;
        break;
    case WWPcmDataContentSilenceForPause:
        pcm = &m_unpauseSilenceBuffer;
        break;
    case WWPcmDataContentSilenceForEnding:
        pcm = &m_endSilenceBuffer;
        break;
    default:
        assert(0);
        break;
    }

    return pcm;
}

int
WWPcmStream::GetPcmDataId(WWPcmDataUsageType t)
{
    WWPcmData *pcm = GetPcm(t);

    if (!pcm) {
        return -1;
    }
    return pcm->Id();
}

int64_t
WWPcmStream::TotalFrameNum(WWPcmDataUsageType t)
{
    int64_t result = 0;

    WWPcmData *pcm = GetPcm(t);

    if (pcm) {
        result = pcm->Frames();
    }

    return result;
}

int64_t
WWPcmStream::PosFrame(WWPcmDataUsageType t)
{
    int64_t result = 0;

    WWPcmData *pcm = GetPcm(t);

    if (pcm) {
        result = pcm->PosFrame();
    }

    return result;
}
// 日本語 UTF-8

#include "WWPcmStream.h"
#include "WWCommonUtil.h"
#include <assert.h>

WWPcmStream::WWPcmStream(void)
        : m_nowPlayingPcmData(nullptr),
          m_pauseResumePcmData(nullptr),
          m_streamType(WWStreamPcm),
          m_zeroFlushMillisec(0)
{
}

void
WWPcmStream::SetStreamType(WWStreamType t)
{
    m_streamType = t;
}

void
WWPcmStream::SetZeroFlushMillisec(int zeroFlushMillisec)
{
    assert(0 <= zeroFlushMillisec);
    m_zeroFlushMillisec = zeroFlushMillisec;
}

void
WWPcmStream::PrepareSilenceBuffers(DWORD latencyMillisec, WWPcmDataSampleFormatType deviceSampleFormat,
        int deviceSampleRate, int deviceNumChannels, int deviceBytesPerFrame)
{
    // 再生前無音0(初回再生用) 再生前無音1(一時停止再開用)、再生後無音の準備。
    // DoPマーカーが2フレームを1周期として繰り返されているため、これらのバッファのフレーム数は2の倍数になるようにする。
    {
        DWORD startZeroFlushMillisec = m_zeroFlushMillisec;
        if (startZeroFlushMillisec < latencyMillisec) {
            startZeroFlushMillisec = latencyMillisec;
        }
        m_startSilenceBuffer.Init(-1, deviceSampleFormat, deviceNumChannels,
                (1 * (int)((int64_t)deviceSampleRate * startZeroFlushMillisec / 1000) + 1) & (~1),
                deviceBytesPerFrame, WWPcmDataContentSilenceForTrailing, WWStreamPcm);
    }

    m_unpauseSilenceBuffer.Init(-1, deviceSampleFormat, deviceNumChannels,
            (1 * (int)((int64_t)deviceSampleRate * latencyMillisec / 1000) + 1) & (~1),
            deviceBytesPerFrame, WWPcmDataContentSilenceForPause, WWStreamPcm);

    // endSilenceBufferは最後に再生される無音。
    m_endSilenceBuffer.Init(-1, deviceSampleFormat, deviceNumChannels,
            (4 * (int)((int64_t)deviceSampleRate * latencyMillisec / 1000) + 1) & (~1),
            deviceBytesPerFrame, WWPcmDataContentSilenceForEnding, WWStreamPcm);
    m_endSilenceBuffer.SetNext(nullptr);

    // spliceバッファー。サイズは100分の1秒=10ms 適当に選んだ。
    m_spliceBuffer.Init(-1, deviceSampleFormat, deviceNumChannels,
            (deviceSampleRate / 100 + 1) & (~1),
            deviceBytesPerFrame, WWPcmDataContentSplice, WWStreamPcm);

    // pauseバッファー。ポーズ時の波形つなぎに使われる。spliceバッファーと同様。
    m_pauseBuffer.Init(-1, deviceSampleFormat, deviceNumChannels,
            (deviceSampleRate / 100 + 1) & (~1),
            deviceBytesPerFrame, WWPcmDataContentSplice, WWStreamPcm);

    switch (m_streamType) {
    case WWStreamPcm:
        // Init()でPCMとして初期化し、0フィルされているので処理不要。
        break;
    case WWStreamDop:
        m_startSilenceBuffer.FillDopSilentData();
        m_unpauseSilenceBuffer.FillDopSilentData();
        m_endSilenceBuffer.FillDopSilentData();
        m_spliceBuffer.FillDopSilentData();
        m_pauseBuffer.FillDopSilentData();
        break;
    default:
        assert(0);
        break;
    }
}

void
WWPcmStream::ReleaseBuffers(void)
{
    m_spliceBuffer.Term();
    m_pauseBuffer.Term();
    m_startSilenceBuffer.Term();
    m_unpauseSilenceBuffer.Term();
    m_endSilenceBuffer.Term();

    m_nowPlayingPcmData = nullptr;

}

void
WWPcmStream::Paused(WWPcmData *pauseResume)
{
    m_pauseResumePcmData = pauseResume;

    m_pauseBuffer.SetPosFrame(0);
    m_pauseBuffer.SetNext(&m_endSilenceBuffer);

    m_endSilenceBuffer.SetPosFrame(0);
    m_endSilenceBuffer.SetNext(nullptr);

    m_pauseBuffer.UpdateSpliceDataWithStraightLine(
        *m_nowPlayingPcmData, m_nowPlayingPcmData->PosFrame(),
        m_endSilenceBuffer,   m_endSilenceBuffer.PosFrame());

    m_nowPlayingPcmData = &m_pauseBuffer;
}

bool
WWPcmStream::IsSilenceBuffer(WWPcmData *p) const
{
    return p == &m_spliceBuffer ||
           p == &m_startSilenceBuffer ||
           p == &m_unpauseSilenceBuffer ||
           p == &m_endSilenceBuffer ||
           p == &m_pauseBuffer;
}

WWPcmData *
WWPcmStream::UnpausePrepare(void)
{
    // 再生するPCMデータへフェードインするPCMデータをpauseBufferにセットして
    // 再生開始する。
    assert(m_pauseResumePcmData);

    dprintf("%s resume=%p posFrame=%d\n", __FUNCTION__, m_pauseResumePcmData, m_pauseResumePcmData->PosFrame());

    m_unpauseSilenceBuffer.SetPosFrame(0);
    m_unpauseSilenceBuffer.SetNext(&m_pauseBuffer);

    m_pauseBuffer.SetPosFrame(0);
    m_pauseBuffer.SetNext(m_pauseResumePcmData);

    m_pauseBuffer.UpdateSpliceDataWithStraightLine(
            m_unpauseSilenceBuffer, m_unpauseSilenceBuffer.PosFrame(),
            *m_pauseResumePcmData, m_pauseResumePcmData->PosFrame());

    return &m_unpauseSilenceBuffer;
}

void
WWPcmStream::UnpauseDone(void)
{
    m_pauseResumePcmData = nullptr;
}

void
WWPcmStream::UpdateStartPcm(WWPcmData *startPcm)
{
    m_nowPlayingPcmData = &m_startSilenceBuffer;
    m_nowPlayingPcmData->SetNext(startPcm);
}

void
WWPcmStream::UpdatePlayRepeat(bool repeat, WWPcmData *startPcmData, WWPcmData *endPcmData)
{
    assert(!IsSilenceBuffer(startPcmData));
    assert(!IsSilenceBuffer(endPcmData));

    if (!repeat) {
        // リピートなし。endPcmData→endSilence→nullptr
        endPcmData->SetNext(&m_endSilenceBuffer);
    } else {
        // リピートあり。endPcmData→startPcmData
        endPcmData->SetNext(startPcmData);
    }
}

WWPcmData *
WWPcmStream::GetPcm(WWPcmDataUsageType t)
{
    WWPcmData *pcm = nullptr;

    switch (t) {
    case WWPDUNowPlaying:
        pcm = m_nowPlayingPcmData;
        break;
    case WWPDUPauseResumeToPlay:
        pcm = m_pauseResumePcmData;
        break;
    case WWPDUSplice:
        pcm = &m_spliceBuffer;
        break;
    case WWPDUSpliceNext:
        pcm = m_spliceBuffer.Next();
        break;
    case WWPDUCapture:
        assert(0);
        break;
    default:
        assert(0);
        break;
    }

    return pcm;
}

WWPcmData *
WWPcmStream::GetSilenceBuffer(WWPcmDataContentType t)
{
    WWPcmData *pcm = nullptr;

    switch (t) {
    case WWPcmDataContentSilenceForTrailing:
        pcm = &m_startSilenceBuffer;
        break;
    case WWPcmDataContentSilenceForPause:
        pcm = &m_unpauseSilenceBuffer;
        break;
    case WWPcmDataContentSilenceForEnding:
        pcm = &m_endSilenceBuffer;
        break;
    default:
        assert(0);
        break;
    }

    return pcm;
}

int
WWPcmStream::GetPcmDataId(WWPcmDataUsageType t)
{
    WWPcmData *pcm = GetPcm(t);

    if (!pcm) {
        return -1;
    }
    return pcm->Id();
}

int64_t
WWPcmStream::TotalFrameNum(WWPcmDataUsageType t)
{
    int64_t result = 0;

    WWPcmData *pcm = GetPcm(t);

    if (pcm) {
        result = pcm->Frames();
    }

    return result;
}

int64_t
WWPcmStream::PosFrame(WWPcmDataUsageType t)
{
    int64_t result = 0;

    WWPcmData *pcm = GetPcm(t);

    // assert(m_mutex);
    // WaitForSingleObject(m_mutex, INFINITE);
    if (pcm) {
        result = pcm->PosFrame();
    }
    //ReleaseMutex(m_mutex);

    return result;
}