package com.lingju.audio;

import android.content.Context;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * pcm音频播放类
 */
public class PcmPlayer implements AudioTrack.OnPlaybackPositionUpdateListener {
    private final static String TAG = "PcmPlayer";

    //播放采样率
    protected static final int sampleRate = 16000;
    //播放最小缓存大小
    protected static final int wBufferSize = 2 * AudioTrack.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
    //Android播放组件
    protected final AudioTrack audioTrack;
    protected Handler handler;
    private Context mContext;
    //存放PCM音频的临时文件夹
    private File cacheDir;
    //PCM实体类的队列
    private final LinkedBlockingQueue<PcmMsg> pcms = new LinkedBlockingQueue<>();
    //是否允许播放
    private final AtomicBoolean play = new AtomicBoolean(false);
    //是否缓存中
    private final AtomicBoolean pending = new AtomicBoolean(false);
    //播放锁
    private final Lock playLock = new ReentrantLock();
    //播放锁的Condition
    private final Condition playCondition = playLock.newCondition();

    //播放监听类
    private PlayListener playListener;
    //当次播放的pcm文件的当前已播放长度
    private int pcmLength;

    public PcmPlayer(Context context, Handler handler) {
        this.mContext = context;
        this.audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, wBufferSize, AudioTrack.MODE_STREAM);
        this.handler = handler;
        audioTrack.setPlaybackPositionUpdateListener(this, handler);
        cacheDir = context.getExternalFilesDir(Environment.DIRECTORY_MUSIC);
    }

    /**
     * 停止播放
     */
    public void stop() {
        Log.i(TAG, "stop............");
        this.pcms.clear();
        this.pending.set(false);
        this.play.set(false);
        //this.audioTrack.stop();
        notifyPlay();
    }

    public File getCacheDir() {
        return cacheDir;
    }

    /**
     * 是否允许播放
     *
     * @return
     */
    public boolean playAllowed() {
        return play.get();
    }

    /**
     * 清空等待播放队列中的pcm文件
     */
    private void clearPcmFile() {
        for (File f : cacheDir.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                return pathname.getName().endsWith(".pcm");
            }
        })) {
            f.delete();
        }
    }

    /**
     * 添加PcmMsg到播放队列
     *
     * @param pcmMsg
     * @param last   是否最后一个,如果是最后一个,pending设置为false,没有需要缓存的pcm文件了
     */
    public void append(PcmMsg pcmMsg, boolean last) {
        pcms.offer(pcmMsg);
        this.pending.set(!last);
    }

    /**
     * 通知等待中的播放器开始播放
     */
    public void notifyPlay() {
        if (playLock.tryLock()) {
            try {
                playCondition.signalAll();
            } finally {
                playLock.unlock();
            }
        }
    }

    /**
     * 异步播放
     *
     * @param playListener
     */
    public void play(PlayListener playListener) {
        this.playListener = playListener;
        clearPcmFile();
        pcmLength = 0;
        pending.set(true);
        play.set(true);

        new Thread(new Runnable() {
            @Override
            public void run() {
                playPoll();
            }
        }).start();
    }

    /**
     * 同步播放,切记不要在主线程调用该方法
     *
     * @param playListener
     */
    public void playSync(PlayListener playListener) {
        this.playListener = playListener;
        clearPcmFile();
        pcmLength = 0;
        pending.set(true);
        play.set(true);
        playPoll();
    }

    /**
     * 遍历队列中的PCM文件并依队列顺序播放
     */
    private void playPoll() {
        PcmMsg pcmMsg = null;
        int i = 0;
        boolean paused = false;
        if (playLock.tryLock()) {
            try {
                while (play.get()) {
                    pcmMsg = pcms.peek();
                    if (pcmMsg != null && pcmMsg.getProgress() == 100) {
                        pcms.poll();
                        if (i == 0) {
                            playListener.onSpeakBegin();
                        } else if (paused) {
                            playListener.onSpeakResumed();
                        }
                        i++;
                        paused = false;
                        playListener.onSpeakProgress(-1);
                        try {
                            if (audioTrack.getPlayState() != AudioTrack.PLAYSTATE_PLAYING) {
                                Log.i(TAG, "audioTrack play.............");
                                audioTrack.play();
                                audioTrack.setNotificationMarkerPosition(0);
                            }
                            play(pcmMsg);
                        } catch (FileNotFoundException e) {
                            e.printStackTrace();
                            playListener.onError("文件没有找到");
                            break;
                        }
                    } else if (pending.get()) {
                        Log.i(TAG, "playPoll await..............");
                        if (i > 0) {
                            paused = true;
                            playListener.onSpeakPaused();
                        }
                        playCondition.await(3000, TimeUnit.MILLISECONDS);
                    }
                }
                if (audioTrack.getPlayState() != AudioTrack.PLAYSTATE_STOPPED) {
                    audioTrack.stop();
                }
            } catch (Exception e) {
                if (e instanceof InterruptedException)
                    Thread.interrupted();
                e.printStackTrace();
            } finally {
                Log.i(TAG, "play end.............");
                playLock.unlock();
            }
        }
    }

    private void play(PcmMsg pcmMsg) throws FileNotFoundException {
        File f = new File(cacheDir, pcmMsg.getPath());
        if (!f.exists()) {
            int i = 100;
            while (i-- > 0 && play.get()) {
                try {
                    Thread.sleep(50);
                    f = new File(cacheDir, pcmMsg.getPath());
                    if (f.exists())
                        break;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        BufferedInputStream in = new BufferedInputStream(new FileInputStream(f));
        try {
            byte[] buffer = new byte[wBufferSize];
            int l;
            //Log.i(TAG,"file size="+l);
            //audioTrack.setNotificationMarkerPosition((int) (l / 2));
            //audioTrack.play();

            while ((l = in.read(buffer)) != -1 && play.get()) {
                audioTrack.write(buffer, 0, l);
                pcmLength += l;
            }
            if (!pending.get() && pcms.size() == 0) {
                Log.i(TAG, "2mark......." + (pcmLength / 2));
                audioTrack.setNotificationMarkerPosition(pcmLength / 2);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        /*if(!isPrecision){
            onMarkerReached(audioTrack);
        }*/
    }

    /**
     * 获取播放器的状态
     *
     * @return
     */
    public int getPlayState() {
        return audioTrack.getPlayState();
    }


    @Override
    public void onMarkerReached(AudioTrack track) {
        Log.i(TAG, "onMarkerReached>>>" + track.getNotificationMarkerPosition());
        if (playLock.tryLock()) {
            try {
                playCondition.signalAll();
            } finally {
                playLock.unlock();
            }
        }
        Log.i(TAG, "PCM SIZE=" + pcms.size());
        if (!pending.get() && pcms.size() == 0) {
            play.set(false);
            playListener.onCompleted();
        }
    }

    @Override
    public void onPeriodicNotification(AudioTrack track) {
        //Log.i(TAG,"onPeriodicNotification>>>"+(++i)+"-"+track.getPositionNotificationPeriod());
    }

    /**
     * PCM文件实体类
     */
    public static class PcmMsg {
        //pcm文件路径
        protected String path;
        //pcm文件的缓存百分比,0-100,100代表该文件已经确保所有数据均有效
        protected int progress;

        public PcmMsg() {
        }

        public PcmMsg(String path, int progress) {
            this.path = path;
            this.progress = progress;
        }

        public String getPath() {
            return path;
        }

        public PcmMsg setPath(String path) {
            this.path = path;
            return this;
        }

        public int getProgress() {
            return progress;
        }

        public PcmMsg setProgress(int progress) {
            this.progress = progress;
            return this;
        }
    }

    /**
     * 播放监听器
     */
    public interface PlayListener {
        /**
         * 播放过程中出错回调
         *
         * @param msg
         */
        public void onError(String msg);

        /**
         * 播放结束回调
         */
        public void onCompleted();

        /**
         * 播放开始回调
         */
        public void onSpeakBegin();

        /**
         * 播放暂停回调
         */
        public void onSpeakPaused();

        /**
         * @param p
         */
        @Deprecated
        public void onSpeakProgress(int p);

        /**
         * 恢复播放
         */
        public void onSpeakResumed();
    }
}