/*
 * Copyright (C) 2013 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.
 */

package com.mobileer.oboetester;


import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;

/**
 * Abstract class for recording.
 * Call processBuffer(buffer) when data is read.
 */
class AudioRecordThread implements Runnable {
    private static final String TAG = "AudioRecordThread";

    private final int mSampleRate;
    private final int mChannelCount;
    private Thread mThread;
    protected boolean mGo;
    private AudioRecord mRecorder;
    private CircularCaptureBuffer mCaptureBuffer;
    protected float[] mBuffer = new float[256];
    private static int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_FLOAT;
    private Runnable mTask;
    private int mTaskCountdown;
    private boolean mCaptureEnabled = true;

    public AudioRecordThread(int frameRate, int channelCount, int maxFrames) {
        mSampleRate = frameRate;
        mChannelCount = channelCount;
        mCaptureBuffer = new CircularCaptureBuffer(maxFrames);
    }

    private void createRecorder() {
        int channelConfig = (mChannelCount == 1)
                ? AudioFormat.CHANNEL_IN_MONO : AudioFormat.CHANNEL_IN_STEREO;
        int audioFormat = AudioFormat.ENCODING_PCM_FLOAT;
        int minRecordBuffSizeInBytes = AudioRecord.getMinBufferSize(mSampleRate,
                channelConfig,
                audioFormat);
        mRecorder = new AudioRecord(
                MediaRecorder.AudioSource.VOICE_RECOGNITION,
                mSampleRate,
                channelConfig,
                audioFormat,
                2 * minRecordBuffSizeInBytes);
        if (mRecorder.getState() == AudioRecord.STATE_UNINITIALIZED) {
            throw new RuntimeException("Could not make the AudioRecord - UNINITIALIZED");
        }
    }

    @Override
    public void run() {
        startAudioRecording();

        while (mGo) {
            int result = handleAudioPeriod();
            if (result < 0) {
                mGo = false;
            }
        }

        stopAudioRecording();
    }

    public void startAudio() {
        if (mThread == null) {
            mGo = true;
            mThread = new Thread(this);
            mThread.start();
        }
    }

    public void stopAudio() {
        mGo = false;
        if (mThread != null) {
            try {
                mThread.join(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            mThread = null;
        }
    }

    public int getSampleRate() {
        return mSampleRate;
    }

    /**
     * @return number of samples read or negative error
     */
    private int handleAudioPeriod() {
        int numSamplesRead = mRecorder.read(mBuffer, 0, mBuffer.length,
                AudioRecord.READ_BLOCKING);
        if (numSamplesRead <= 0) {
            return numSamplesRead;
        } else {
            if (mTaskCountdown > 0) {
                mTaskCountdown -= numSamplesRead;
                if (mTaskCountdown <= 0) {
                    mTaskCountdown = 0;
                    new Thread(mTask).start(); // run asynchronously with audio thread
                }
            }
            if (mCaptureEnabled) {
                return mCaptureBuffer.write(mBuffer, 0, numSamplesRead);
            } else {
                return numSamplesRead;
            }
        }
    }

    private void startAudioRecording() {
        stopAudioRecording();
        createRecorder();
        mRecorder.startRecording();
    }

    private void stopAudioRecording() {
        if (mRecorder != null) {
            mRecorder.stop();
            mRecorder.release();
            mRecorder = null;
        }
    }

    /**
     * Schedule task to be run on its own thread when numSamples more samples have been recorded.
     *
     * @param numSamples
     * @param task
     */
    public void scheduleTask(int numSamples, Runnable task) {
        mTask = task;
        mTaskCountdown = numSamples;
    }

    public void setCaptureEnabled(boolean captureEnabled) {
        mCaptureEnabled = captureEnabled;
    }

    public int readMostRecent(float[] buffer) {
        return mCaptureBuffer.readMostRecent(buffer);
    }

}
