/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
#include <Log.h>
#include "audio/Buffer.h"
#include "audio/AudioLocal.h"

bool AudioLocal::prepare(AudioHardware::SamplingRate samplingRate,  int gain, int /*mode*/)
{
    LOGV("prepare");
    // gain control not necessary in MobilePre as there is no control.
    // This means audio source itself should be adjusted to control volume
    if (mState == EStNone) {
        if (run() != android::NO_ERROR) {
            LOGE("AudioLocal cannot run");
            // cannot run thread
            return false;
        }
        mState = EStCreated;
    } else if (mState == EStRunning) {
        // wrong usage. first stop!
        return false;
    }
    mClientCompletionWait.tryWait(); // this will reset semaphore to 0 if it is 1.
    mSamplingRate = samplingRate;
    return issueCommandAndWaitForCompletion(ECmInitialize);
}

bool AudioLocal::startPlaybackOrRecord(android::sp<Buffer>& buffer, int numberRepetition)
{
    LOGV("startPlaybackOrRecord");
    if (mState != EStInitialized) {
        LOGE("startPlaybackOrRecord while not initialized");
        // wrong state
        return false;
    }
    mBuffer = buffer;
    mNumberRepetition = numberRepetition;
    mCurrentRepeat = 0;
    return issueCommandAndWaitForCompletion(ECmRun);
}

bool AudioLocal::waitForCompletion()
{
    int waitTimeInMsec = mBuffer->getSamples() / (mSamplingRate/1000);
    waitTimeInMsec += COMMAND_WAIT_TIME_MSEC;
    LOGD("waitForCompletion will wait for %d", waitTimeInMsec);
    if (!mClientCompletionWait.timedWait(waitTimeInMsec)) {
        LOGE("waitForCompletion time-out");
        return false;
    }
    return mCompletionResult;
}

void AudioLocal::stopPlaybackOrRecord()
{
    LOGV("stopPlaybackOrRecord");
    if (mState == EStRunning) {
        issueCommandAndWaitForCompletion(ECmStop);
    }

    if (mState != EStNone) { // thread alive
        requestExit();
        mCurrentCommand = ECmThreadStop;
        mAudioThreadWait.post();
        requestExitAndWait();
        mState = EStNone;
    }
}

bool AudioLocal::issueCommandAndWaitForCompletion(AudioCommand command)
{
    mCurrentCommand = command;
    mAudioThreadWait.post();
    if (!mClientCommandWait.timedWait(COMMAND_WAIT_TIME_MSEC)) {
        LOGE("issueCommandAndWaitForCompletion timeout cmd %d", command);
        return false;
    }
    return mCommandResult;
}

AudioLocal::~AudioLocal()
{
    LOGV("~AudioLocal");
}

AudioLocal::AudioLocal()
    : mState(EStNone),
      mCurrentCommand(ECmNone),
      mClientCommandWait(0),
      mClientCompletionWait(0),
      mAudioThreadWait(0),
      mCompletionResult(false)
{
    LOGV("AudioLocal");
}


bool AudioLocal::threadLoop()
{
    if (mCurrentCommand == ECmNone) {
        if (mState == EStRunning) {
            if (doPlaybackOrRecord(mBuffer)) {
                // check exit condition
                if (mBuffer->bufferHandled()) {
                    mCurrentRepeat++;
                    LOGV("repeat %d - %d", mCurrentRepeat, mNumberRepetition);
                    if (mCurrentRepeat == mNumberRepetition) {
                        LOGV("AudioLocal complete command");
                        mState = EStInitialized;
                        mCompletionResult = true;
                        mClientCompletionWait.post();
                    } else {
                        mBuffer->restart();
                    }
                }
            } else {
                mState = EStInitialized;
                //notify error
                mCompletionResult = false;
                mClientCompletionWait.post();
            }
            return true;
        }
        //LOGV("audio thread waiting");
        mAudioThreadWait.wait();
        //LOGV("audio thread waken up");
        if (mCurrentCommand == ECmNone) {
            return true; // continue to check exit condition
        }
    }

    int pendingCommand = mCurrentCommand;
    // now there is a command
    switch (pendingCommand) {
    case ECmInitialize:
        mCommandResult = doPrepare(mSamplingRate, AudioHardware::SAMPLES_PER_ONE_GO);
        if (mCommandResult) {
            mState = EStInitialized;
        }
        break;
    case ECmRun: {
        mCommandResult = doPlaybackOrRecord(mBuffer);
        if (mCommandResult) {
            mState = EStRunning;
        }
    }
        break;
    case ECmStop:
        doStop();
        mState = EStCreated;
        mCommandResult = true;
        break;
    case ECmThreadStop:
        return false;
        break;
    default:
        // this should not happen
        ASSERT(false);
        break;
    }

    mCurrentCommand = ECmNone;
    mClientCommandWait.post();

    return true;
}