package us.shandian.giga.postprocessing;

import android.media.MediaCodec.BufferInfo;
import android.media.MediaExtractor;
import android.media.MediaMuxer;
import android.media.MediaMuxer.OutputFormat;
import android.util.Log;

import static org.schabi.newpipe.BuildConfig.DEBUG;

import org.schabi.newpipe.streams.io.SharpStream;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

import us.shandian.giga.get.DownloadMission;


class Mp4Muxer extends Postprocessing {
    private static final String TAG = "Mp4Muxer";
    private static final int NOTIFY_BYTES_INTERVAL = 128 * 1024;// 128 KiB

    Mp4Muxer(DownloadMission mission) {
        super(mission, 0, false);
    }

    @Override
    int process(SharpStream out, SharpStream... sources) throws IOException {
        File dlFile = mission.getDownloadedFile();
        File tmpFile = new File(mission.location, mission.name.concat(".tmp"));

        if (tmpFile.exists())
            if (!tmpFile.delete()) return DownloadMission.ERROR_FILE_CREATION;

        if (!tmpFile.createNewFile()) return DownloadMission.ERROR_FILE_CREATION;

        FileInputStream source = null;
        MediaMuxer muxer = null;

        //noinspection TryFinallyCanBeTryWithResources
        try {
            source = new FileInputStream(dlFile);
            MediaExtractor tracks[] = {
                    getMediaExtractor(source, mission.offsets[0], mission.offsets[1] - mission.offsets[0]),
                    getMediaExtractor(source, mission.offsets[1], mission.length - mission.offsets[1])
            };

            muxer = new MediaMuxer(tmpFile.getAbsolutePath(), OutputFormat.MUXER_OUTPUT_MPEG_4);

            int tracksIndex[] = {
                    muxer.addTrack(tracks[0].getTrackFormat(0)),
                    muxer.addTrack(tracks[1].getTrackFormat(0))
            };

            ByteBuffer buffer = ByteBuffer.allocate(512 * 1024);// 512 KiB
            BufferInfo info = new BufferInfo();

            long written = 0;
            long nextReport = NOTIFY_BYTES_INTERVAL;

            muxer.start();

            while (true) {
                int done = 0;

                for (int i = 0; i < tracks.length; i++) {
                    if (tracksIndex[i] < 0) continue;

                    info.set(0,
                            tracks[i].readSampleData(buffer, 0),
                            tracks[i].getSampleTime(),
                            tracks[i].getSampleFlags()
                    );

                    if (info.size >= 0) {
                        muxer.writeSampleData(tracksIndex[i], buffer, info);
                        written += info.size;
                        done++;
                    }
                    if (!tracks[i].advance()) {
                        // EOF reached
                        tracks[i].release();
                        tracksIndex[i] = -1;
                    }

                    if (written > nextReport) {
                        nextReport = written + NOTIFY_BYTES_INTERVAL;
                        super.progressReport(written);
                    }
                }

                if (done < 1) break;
            }

            // this part should not fail
            if (!dlFile.delete()) return DownloadMission.ERROR_FILE_CREATION;
            if (!tmpFile.renameTo(dlFile)) return DownloadMission.ERROR_FILE_CREATION;

            return OK_RESULT;
        } finally {
            try {
                if (muxer != null) {
                    muxer.stop();
                    muxer.release();
                }
            } catch (Exception err) {
                if (DEBUG)
                    Log.e(TAG, "muxer stop/release failed", err);
            }

            if (source != null) {
                try {
                    source.close();
                } catch (IOException e) {
                    // nothing to do
                }
            }

            // if the operation fails, delete the temporal file
            if (tmpFile.exists()) {
                //noinspection ResultOfMethodCallIgnored
                tmpFile.delete();
            }
        }
    }

    private MediaExtractor getMediaExtractor(FileInputStream source, long offset, long length) throws IOException {
        MediaExtractor extractor = new MediaExtractor();
        extractor.setDataSource(source.getFD(), offset, length);
        extractor.selectTrack(0);

        return extractor;
    }
}
