package cn.i2edu.dubbing_lib.audioUtils.compose;

import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

/**
 * Created by Administrator on 2016/12/20.
 */

public class AudioComposer {
    private static final int SAMPLERATE = 44100;
    private static final int CHANNEL = 2;
    private static final int AUDIO_FORMAT = 16;
    private static final int BYTE_SIZE = 8;

    public interface ComposeAudioInterface {

        void composeSuccess(String result);

        void composeFail();
    }

    public static void composeAudio(String firstAudioFilePath, List<String> secondAudioFilePaths,
                                    final String composeAudioFilePath, boolean deleteSource,
                                    List<Long> millSeconds,
                                    List<Long> durations,
                                    final ComposeAudioInterface composeAudioInterface) {
        final int byteBufferSize = 512;
        // 待合成音频1的缓冲
        byte[] firstAudioByteBuffer = new byte[byteBufferSize];
        // 待合成音频2的缓冲
        byte[] secondAudioByteBuffer = new byte[byteBufferSize];
        // 由待合成音频1的缓冲和待合成音频2的缓冲组成的缓冲，用作合成时的原数据
        byte[][] allAudioBytes = new byte[2][];
        // 每次合成的结果缓冲数据
        byte[] mixedAudioByte;
        // 待合成音频1的已读取总数，用于判断是否合成结束
        int firstAudioReadNumber;
        // 待合成音频2的已读取总数，用于判断是否合成结束
        int secondAudioReadNumber;
        Handler handler = new Handler(Looper.getMainLooper());
        FileInputStream firstAudioInputStream = FileFunction.GetFileInputStreamFromFile
                (firstAudioFilePath);
        FileInputStream secondAudioInputStream;
        FileOutputStream composeAudioOutputStream = FileFunction.GetFileOutputStreamFromFile(composeAudioFilePath);
        for (int i = 0; i < secondAudioFilePaths.size(); i++) {
            // 由于录制的音频段时长不一定很准确，所以就以音频结束的时间点为基准向前来计算，
            // 这样就能够保证录制的音频能够和字幕比较准确的对应，避免那种字幕与音频错开的情况
            // 上一段录音结尾到本段录音结尾的总字节数
            long audioSegSize = (long) (SAMPLERATE * 2.0f * 2.0f * (float) ((Number)millSeconds.get(i)).longValue() / 1000);
            System.out.println("audioSegSize: " + audioSegSize);

            // 每段录音准确的字节数
            long desiredFileByteNum = (long) (SAMPLERATE * 2.0f * 2.0f * (float)  ((Number)durations.get(i)).longValue() / 1000);
            System.out.println("desiredFileByteNum: " + desiredFileByteNum);

            // 分段音频的长度与准确音频长度的差值
            long audioOffset = audioSegSize - desiredFileByteNum;
            System.out.println("audioOffset: " + audioOffset);
            try {
                // 写入背景音乐的空隙部分
                //noinspection LoopConditionNotUpdatedInsideLoop
                while (firstAudioInputStream != null) {
                    if (audioOffset <= 0) {
                        break;
                    }

                    firstAudioReadNumber = firstAudioInputStream.read(firstAudioByteBuffer);
                    if (firstAudioReadNumber <= 0) {
                        break;
                    }
                    composeAudioOutputStream.write(firstAudioByteBuffer);
                    audioOffset -= firstAudioReadNumber;

                }
                secondAudioInputStream = FileFunction.GetFileInputStreamFromFile(secondAudioFilePaths.get(i));
                // 合成并写入录音段
                //noinspection LoopConditionNotUpdatedInsideLoop
                while (secondAudioInputStream!=null) {

                    secondAudioReadNumber = secondAudioInputStream.read(secondAudioByteBuffer);
                    if (secondAudioReadNumber <= 0 || desiredFileByteNum <= 0) {
                        break;
                    }
                    if (firstAudioInputStream != null){
                        firstAudioInputStream.read(firstAudioByteBuffer);
                        allAudioBytes[0] = firstAudioByteBuffer;
                        allAudioBytes[1] = secondAudioByteBuffer;
                        mixedAudioByte = mixRawAudioBytes(allAudioBytes);
                    }else {
                        mixedAudioByte = secondAudioByteBuffer;
                    }

                    composeAudioOutputStream.write(mixedAudioByte);
                    desiredFileByteNum -= secondAudioReadNumber;
                }
                if (secondAudioInputStream != null)secondAudioInputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
//                CustomBugReporter.reportError(AudioComposer.class.getName(), "mux audio and audio failed", e);
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        if (composeAudioInterface != null) {
                            composeAudioInterface.composeFail();
                        }
                    }
                });
                return;
            }
        }
        //  写入背景音乐剩余部分数据
        try {
            //noinspection LoopConditionNotUpdatedInsideLoop
            while (firstAudioInputStream!= null) {
                firstAudioReadNumber = firstAudioInputStream.read(firstAudioByteBuffer);
                if (firstAudioReadNumber < 0) break;
                composeAudioOutputStream.write(firstAudioByteBuffer);
            }
        } catch (IOException e) {
            e.printStackTrace();
            //            CustomBugReporter.reportError(AudioComposer.class.getName(), "write left bgm data error", e);
            if (composeAudioInterface != null) {
                composeAudioInterface.composeFail();
            }
        }

        try {
            composeAudioOutputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
//            CustomBugReporter.reportError(AudioComposer.class.getName(), "close composeAudioOutputStream failed1", e);
        }

        if (deleteSource) {
            FileFunction.DeleteFile(firstAudioFilePath);
            for (int i = 0; i < secondAudioFilePaths.size(); i++) {
                FileFunction.DeleteFile(secondAudioFilePaths.get(i));
            }
        }
        try {
            if (firstAudioInputStream != null)firstAudioInputStream.close();
            composeAudioOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
//            CustomBugReporter.reportError(AudioComposer.class.getName(), "close firstAudioInputStream or composeAudioOutputStream failed!", e);
        }
        handler.post(new Runnable() {
            @Override
            public void run() {
                if (composeAudioInterface != null) {
                    composeAudioInterface.composeSuccess(composeAudioFilePath);
                }
            }
        });
    }

    /**
     * 合成对应的byte
     *
     * @param bMulRoadAudioes 需要被合成的byte数组
     */
    private static byte[] mixRawAudioBytes(byte[][] bMulRoadAudioes) {

        if (bMulRoadAudioes == null || bMulRoadAudioes.length == 0)
            return null;

        byte[] realMixAudio = bMulRoadAudioes[0];

        if (bMulRoadAudioes.length == 1)
            return realMixAudio;

        for (int rw = 0; rw < bMulRoadAudioes.length; ++rw) {
            if (bMulRoadAudioes[rw].length != realMixAudio.length) {
                Log.e("app", "column of the road of audio + " + rw + " is diffrent.");
                return null;
            }
        }

        int row = bMulRoadAudioes.length;
        int coloum = realMixAudio.length / 2;
        short[][] sMulRoadAudioes = new short[row][coloum];

        for (int r = 0; r < row; ++r) {
            for (int c = 0; c < coloum; ++c) {
                sMulRoadAudioes[r][c] = (short) ((bMulRoadAudioes[r][c * 2] & 0xff) | (bMulRoadAudioes[r][c * 2 + 1] & 0xff) << 8);
            }
        }

        short[] sMixAudio = new short[coloum];
        int mixVal;
        int sr;
        for (int sc = 0; sc < coloum; ++sc) {
            mixVal = 0;
            sr = 0;
            for (; sr < row; ++sr) {
                mixVal += sMulRoadAudioes[sr][sc];
            }
            sMixAudio[sc] = (short) (mixVal / row);
        }

        for (sr = 0; sr < coloum; ++sr) {
            realMixAudio[sr * 2] = (byte) (sMixAudio[sr] & 0x00FF);
            realMixAudio[sr * 2 + 1] = (byte) ((sMixAudio[sr] & 0xFF00) >> 8);
        }

        return realMixAudio;
    }
}
