package net.coding.program.common.audio;

import android.media.AudioRecord;
import android.util.Log;

import net.coding.program.common.Global;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.RandomAccessFile;

/**
 * Created by Carlos2015 on 2015/8/28.
 */
public class AmrAudioRecorder {
    private final static int[] sampleRates = {44100, 22050, 11025, 8000};
    // Used only in uncompressed mode
    private static final int BUFFER_FRAME_SIZE = 960;
    private final Object mLock = new Object();
    private String outPath;
    private int sampleRate;
    private int bufferSize;
    private AudioRecord audioRecorder;
    private State state;
    // Number of frames written to file on each output(only in uncompressed
    // mode)
    private int framePeriod;
    private byte[] buffer;
    private boolean isAddAmrFileHead;
    private boolean isRecording;
    private long duration = 0l;
    private String TAG = "AmrAudioRecorder";
    private int audioSource;
    private int channelConfig;
    private int audioFormat;
    private VoiceRecordingCallBack mVoiceRecordingCallBack;

    public AmrAudioRecorder(int audioSource, int channelConfig, int audioFormat, String outPath) {
        this.outPath = outPath;
        this.audioSource = audioSource;
        this.channelConfig = channelConfig;
        this.audioFormat = audioFormat;
        try {
            sampleRate = sampleRates[3];
            bufferSize = BUFFER_FRAME_SIZE;

//			bufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig,
//	                audioFormat);
            bufferSize = 1024 * 2;
            if (bufferSize == AudioRecord.ERROR_BAD_VALUE) {
                Log.e("AmrAudioRecorder", "bufferSize error");
                return;
            }
            initAudioRecoder(audioSource, channelConfig, audioFormat);
        } catch (Exception e) {
            Global.errorLog(e);
        }
    }

    private void initAudioRecoder(int audioSource, int channelConfig, int audioFormat) throws Exception {
        audioRecorder = new AudioRecord(audioSource, sampleRate, channelConfig, audioFormat, bufferSize);
        if (audioRecorder.getState() != AudioRecord.STATE_INITIALIZED)
            throw new Exception("AudioRecord initialization failed");
        buffer = new byte[bufferSize];
        state = State.INITIALIZING;
        Log.w("AmrAudioRecorder", "sampleRate=" + sampleRate + ",bufferSize=" + bufferSize);
    }

    public State getState() {
        return state;
    }

    public void setState(State state) {
        this.state = state;
    }

    public void prepare() {
        if (state == State.INITIALIZING) {
            if ((audioRecorder.getState() == AudioRecord.STATE_INITIALIZED)
                    & (outPath != null)) {
                File file = new File(outPath).getParentFile();
                if (file != null && !file.exists()) {
                    file.mkdirs();
                }
                isAddAmrFileHead = true;
                duration = 0l;
                state = State.READY;
            } else {
                state = State.ERROR;
            }
        }
    }

    public void setVoiceRecordingCallBack(VoiceRecordingCallBack mVoiceRecordingCallBack) {
        this.mVoiceRecordingCallBack = mVoiceRecordingCallBack;
    }

    public void pause() {
        stop();
    }

    public long getDuration() {
        return duration;
    }

    public void continueRecord() throws Exception {
        if (state != State.ERROR) {
            initAudioRecoder(audioSource, channelConfig, audioFormat);
            state = State.READY;
            isAddAmrFileHead = false;
            start();
        }
    }

    /**
     * Starts the recording, and sets the state to RECORDING. Call after
     * prepare().
     */
    public void start() {
        if (state == State.READY) {
            isRecording = true;
            audioRecorder.startRecording();
            state = State.RECORDING;
            new Thread() {
                public void run() {
                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                    long startTime = System.currentTimeMillis();
                    long endTime = 0l;
                    int len = 0;
                    synchronized (mLock) {
                        mLock.notify();
                    }
                    while (isRecording && state == AmrAudioRecorder.State.RECORDING && (AudioRecord.ERROR_INVALID_OPERATION != (len = audioRecorder.read(buffer, 0, buffer.length)))) {
                        endTime = System.currentTimeMillis();
                        try {
                            bos.write(buffer, 0, len);
                            long v = 0;
                            // 将 buffer 内容取出，进行平方和运算
                            for (int i = 0; i < len; i++) {
                                v += buffer[i] * buffer[i];
                            }
                            // 平方和除以数据总长度，得到音量大小。
                            double mean = v / (double) len;
                            double volume = 10 * Math.log10(mean);
                            duration += (endTime - startTime);
                            startTime = endTime;
                            Log.w(TAG, "duration:" + duration + ",分贝值:" + volume);
                            if (mVoiceRecordingCallBack != null) {
                                mVoiceRecordingCallBack.onRecord(duration, volume);
                            }
                            if (isRecording) {
                                // 大概一秒十次
                                synchronized (mLock) {
                                    try {
                                        mLock.wait(100);
                                    } catch (InterruptedException e) {
                                        e.printStackTrace();
                                    }
                                }
                            }

                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    try {
                        byte[] pcm = bos.toByteArray();
//							VoiceDenoiseUtils mVoiceDenoiseUtils = new VoiceDenoiseUtils();
//							mVoiceDenoiseUtils.initAudioDeNose();
//							mVoiceDenoiseUtils.audioDeNose8K(pcm);
//							mVoiceDenoiseUtils.exitAudioDeNose();
                        ByteArrayInputStream bis = new ByteArrayInputStream(pcm);
                        bos.close();

                        if (bis.available() != 0) {
                            byte[] data = AmrUtils.convertToAmr(bis, isAddAmrFileHead);
                            File f = new File(outPath);
                            if (!f.exists()) {
                                f.createNewFile();
                            }
                            RandomAccessFile raf = new RandomAccessFile(f, "rw");
                            if (!isAddAmrFileHead) {
                                raf.seek(f.length());
                            }
                            raf.write(data, 0, data.length);
                            raf.close();
//								if(isAddAmrFileHead){
//									FileOutputStream fos = new FileOutputStream(outPath,true);
//									Log.w(TAG,"fileSize:"+data.length);
//									fos.write(data);
//									fos.flush();
//									fos.close();
//								}else{
//									File f = new File(outPath);
//									if(!f.exists()){
//										f.createNewFile();
//									}
//									RandomAccessFile raf =new RandomAccessFile(f,"rw");
//									raf.seek(f.length());
//									raf.write(data,0,data.length);
//									raf.close();
//
//								}

                        }
                    } catch (Exception e) {
                        Global.errorLog(e);
                    } finally {
                        synchronized (mLock) {
                            mLock.notify();
                        }
                        if (audioRecorder != null) {
                            audioRecorder.stop();
                            audioRecorder.release();
                            audioRecorder = null;
                        }
                    }
                }
            }.start();
        } else {
            Log.e("start()",
                    "start() called on illegal state");
            state = State.ERROR;
        }
    }

    public void stop() {
        state = State.STOPPED;
        isRecording = false;
        synchronized (mLock) {
            try {
                mLock.wait(1800);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * INITIALIZING : recorder is initializing; READY : recorder has been
     * initialized, recorder not yet started RECORDING : recording ERROR :
     * reconstruction needed STOPPED: reset needed
     */
    public enum State {
        INITIALIZING, READY, RECORDING, ERROR, STOPPED
    }


    public interface VoiceRecordingCallBack {
        void onRecord(long duration, double volume);
    }

}
