/*
 * This is the source code of Telegram for Android v. 1.7.x.
 * It is licensed under GNU GPL v. 2 or later.
 * You should have received a copy of the license in this archive (see LICENSE).
 *
 * Copyright Nikolai Kudashov, 2013-2014.
 */

package org.telegram.ui;

import android.content.res.Configuration;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;

import com.coremedia.iso.boxes.Container;
import com.googlecode.mp4parser.authoring.Movie;
import com.googlecode.mp4parser.authoring.Track;
import com.googlecode.mp4parser.authoring.builder.DefaultMp4Builder;
import com.googlecode.mp4parser.authoring.container.mp4.MovieCreator;
import com.googlecode.mp4parser.authoring.tracks.CroppedTrack;

import org.telegram.android.AndroidUtilities;
import org.telegram.android.LocaleController;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.R;
import org.telegram.messenger.UserConfig;
import org.telegram.messenger.Utilities;
import org.telegram.ui.Views.ActionBar.ActionBarLayer;
import org.telegram.ui.Views.ActionBar.ActionBarMenu;
import org.telegram.ui.Views.ActionBar.BaseFragment;
import org.telegram.ui.Views.VideoSeekBarView;
import org.telegram.ui.Views.VideoTimelineView;

import java.io.File;
import java.io.FileOutputStream;
import java.nio.channels.FileChannel;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

public class VideoEditorActivity extends BaseFragment implements SurfaceHolder.Callback {

    private MediaPlayer videoPlayer = null;
    private SurfaceHolder surfaceHolder = null;
    private VideoTimelineView videoTimelineView = null;
    private View videoContainerView = null;
    private TextView originalSizeTextView = null;
    private TextView editedSizeTextView = null;
    private View textContainerView = null;
    private ImageView playButton = null;
    private VideoSeekBarView videoSeekBarView = null;

    private boolean initied = false;
    private String videoPath = null;
    private int videoWidth;
    private int videoHeight;
    private float lastProgress = 0;
    private boolean needSeek = false;
    private VideoEditorActivityDelegate delegate;

    public interface VideoEditorActivityDelegate {
        public abstract void didFinishedVideoConverting(String videoPath);
    }

    private Runnable progressRunnable = new Runnable() {
        @Override
        public void run() {
            while (videoPlayer.isPlaying()) {
                Utilities.RunOnUIThread(new Runnable() {
                    @Override
                    public void run() {
                        if (videoPlayer.isPlaying()) {
                            float startTime = videoTimelineView.getLeftProgress() * videoPlayer.getDuration();
                            float endTime = videoTimelineView.getRightProgress() * videoPlayer.getDuration();
                            if (startTime == endTime) {
                                startTime = endTime - 0.01f;
                            }
                            float progress = (videoPlayer.getCurrentPosition() - startTime) / (endTime - startTime);
                            if (progress > lastProgress) {
                                videoSeekBarView.setProgress(progress);
                                lastProgress = progress;
                            }
                            if (videoPlayer.getCurrentPosition() >= endTime) {
                                try {
                                    videoPlayer.pause();
                                    onPlayComplete();
                                } catch (Exception e) {
                                    FileLog.e("tmessages", e);
                                }
                            }
                        }
                    }
                });
                try {
                    Thread.sleep(50);
                } catch (Exception e) {
                    FileLog.e("tmessages", e);
                }
            }
        }
    };

    public VideoEditorActivity(Bundle args) {
        super(args);
        videoPath = args.getString("videoPath");
    }

    @Override
    public boolean onFragmentCreate() {
        if (videoPath == null) {
            return false;
        }
        videoPlayer = new MediaPlayer();
        videoPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                Utilities.RunOnUIThread(new Runnable() {
                    @Override
                    public void run() {
                        onPlayComplete();
                    }
                });
            }
        });
        return super.onFragmentCreate();
    }

    @Override
    public void onFragmentDestroy() {
        if (videoTimelineView != null) {
            videoTimelineView.destroy();
        }
        super.onFragmentDestroy();
    }

    @Override
    public View createView(LayoutInflater inflater, ViewGroup container) {
        if (fragmentView == null) {
            actionBarLayer.setBackgroundColor(0xff333333);
            actionBarLayer.setItemsBackground(R.drawable.bar_selector_white);
            actionBarLayer.setDisplayHomeAsUpEnabled(true, R.drawable.photo_back);
            actionBarLayer.setTitle(LocaleController.getString("EditVideo", R.string.EditVideo));
            actionBarLayer.setActionBarMenuOnItemClick(new ActionBarLayer.ActionBarMenuOnItemClick() {
                @Override
                public void onItemClick(int id) {
                    if (id == -1) {
                        finishFragment();
                    } else if (id == 1) {
                        try {
                            startConvert();
                        } catch (Exception e) {
                            FileLog.e("tmessages", e);
                        }
                    }
                }
            });

            ActionBarMenu menu = actionBarLayer.createMenu();
            View doneItem = menu.addItemResource(1, R.layout.group_create_done_layout);

            TextView doneTextView = (TextView)doneItem.findViewById(R.id.done_button);
            doneTextView.setText(LocaleController.getString("Done", R.string.Done).toUpperCase());

            fragmentView = inflater.inflate(R.layout.video_editor_layout, container, false);
            originalSizeTextView = (TextView)fragmentView.findViewById(R.id.original_size);
            editedSizeTextView = (TextView)fragmentView.findViewById(R.id.edited_size);
            videoContainerView = fragmentView.findViewById(R.id.video_container);
            textContainerView = fragmentView.findViewById(R.id.info_container);

            videoTimelineView = (VideoTimelineView)fragmentView.findViewById(R.id.video_timeline_view);
            videoTimelineView.setVideoPath(videoPath);
            videoTimelineView.setDelegate(new VideoTimelineView.VideoTimelineViewDelegate() {
                @Override
                public void onLeftProgressChanged(float progress) {
                    try {
                        if (videoPlayer.isPlaying()) {
                            videoPlayer.pause();
                            playButton.setImageResource(R.drawable.video_play);
                        }
                        videoPlayer.setOnSeekCompleteListener(null);
                        videoPlayer.seekTo((int)(videoPlayer.getDuration() * progress));
                    } catch (Exception e) {
                        FileLog.e("tmessages", e);
                    }
                    needSeek = true;
                    videoSeekBarView.setProgress(0);
                    updateVideoEditedInfo();
                }

                @Override
                public void onRifhtProgressChanged(float progress) {
                    try {
                        if (videoPlayer.isPlaying()) {
                            videoPlayer.pause();
                            playButton.setImageResource(R.drawable.video_play);
                        }
                        videoPlayer.setOnSeekCompleteListener(null);
                        videoPlayer.seekTo((int)(videoPlayer.getDuration() * progress));
                    } catch (Exception e) {
                        FileLog.e("tmessages", e);
                    }
                    needSeek = true;
                    videoSeekBarView.setProgress(0);
                    updateVideoEditedInfo();
                }
            });

            videoSeekBarView = (VideoSeekBarView)fragmentView.findViewById(R.id.video_seekbar);
            videoSeekBarView.delegate = new VideoSeekBarView.SeekBarDelegate() {
                @Override
                public void onSeekBarDrag(float progress) {
                    if (videoPlayer.isPlaying()) {
                        try {
                            float prog = videoTimelineView.getLeftProgress() + (videoTimelineView.getRightProgress() - videoTimelineView.getLeft()) * progress;
                            videoPlayer.seekTo((int)(videoPlayer.getDuration() * prog));
                            lastProgress = progress;
                        } catch (Exception e) {
                            FileLog.e("tmessages", e);
                        }
                    } else {
                        lastProgress = progress;
                        needSeek = true;
                    }
                }
            };

            playButton = (ImageView)fragmentView.findViewById(R.id.play_button);
            playButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (surfaceHolder.isCreating()) {
                        return;
                    }
                    play();
                }
            });

            SurfaceView surfaceView = (SurfaceView) fragmentView.findViewById(R.id.video_view);
            surfaceHolder = surfaceView.getHolder();
            surfaceHolder.addCallback(this);
            surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
            surfaceHolder.setFixedSize(270, 480);

            updateVideoOriginalInfo();
            updateVideoEditedInfo();
        } else {
            ViewGroup parent = (ViewGroup)fragmentView.getParent();
            if (parent != null) {
                parent.removeView(fragmentView);
            }
        }
        return fragmentView;
    }

    @Override
    public void onResume() {
        super.onResume();
        fixLayout();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        fixLayout();
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        videoPlayer.setDisplay(holder);
        try {
            videoPlayer.setDataSource(videoPath);
            videoPlayer.prepare();
            videoWidth = videoPlayer.getVideoWidth();
            videoHeight = videoPlayer.getVideoHeight();
            fixVideoSize();
            videoPlayer.seekTo((int) (videoTimelineView.getLeftProgress() * videoPlayer.getDuration()));
            initied = true;
        } catch (Exception e) {
            FileLog.e("tmessages", e);
        }
        updateVideoOriginalInfo();
        updateVideoEditedInfo();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        videoPlayer.setDisplay(null);
    }

    private void onPlayComplete() {
        playButton.setImageResource(R.drawable.video_play);
        videoSeekBarView.setProgress(0);
        try {
            videoPlayer.seekTo((int) (videoTimelineView.getLeftProgress() * videoPlayer.getDuration()));
        } catch (Exception e) {
            FileLog.e("tmessages", e);
        }
    }

    private void updateVideoOriginalInfo() {
        if (!initied || originalSizeTextView == null) {
            return;
        }
        File file = new File(videoPath);
        String videoDimension = String.format("%dx%d", videoPlayer.getVideoWidth(), videoPlayer.getVideoHeight());
        int minutes = videoPlayer.getDuration() / 1000 / 60;
        int seconds = (int)Math.ceil(videoPlayer.getDuration() / 1000) - minutes * 60;
        String videoTimeSize = String.format("%d:%02d, %s", minutes, seconds, Utilities.formatFileSize(file.length()));
        originalSizeTextView.setText(String.format("%s: %s, %s", LocaleController.getString("OriginalVideo", R.string.OriginalVideo), videoDimension, videoTimeSize));
    }

    private void updateVideoEditedInfo() {
        if (!initied || editedSizeTextView == null) {
            return;
        }
        File file = new File(videoPath);
        long size = file.length();
        float videoWidth = videoPlayer.getVideoWidth();
        float videoHeight = videoPlayer.getVideoHeight();
        if (videoWidth > 640 || videoHeight > 640) {
            float scale = videoWidth > videoHeight ? 640.0f / videoWidth : 640.0f / videoHeight;
            videoWidth *= scale;
            videoHeight *= scale;
            size *= (scale * scale);
        }
        String videoDimension = String.format("%dx%d", (int)videoWidth, (int)videoHeight);
        int minutes = videoPlayer.getDuration() / 1000 / 60;
        int seconds = (int)Math.ceil(videoPlayer.getDuration() / 1000) - minutes * 60;
        String videoTimeSize = String.format("%d:%02d, ~%s", minutes, seconds, Utilities.formatFileSize(size));
        editedSizeTextView.setText(String.format("%s: %s, %s", LocaleController.getString("EditedVideo", R.string.EditedVideo), videoDimension, videoTimeSize));
    }

    private void fixVideoSize() {
        if (videoWidth == 0 || videoHeight == 0 || fragmentView == null || getParentActivity() == null) {
            return;
        }
        int viewHeight = 0;
        if (!Utilities.isTablet(getParentActivity()) && getParentActivity().getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
            viewHeight = AndroidUtilities.displaySize.y - AndroidUtilities.statusBarHeight - AndroidUtilities.dp(40);
        } else {
            viewHeight = AndroidUtilities.displaySize.y - AndroidUtilities.statusBarHeight - AndroidUtilities.dp(48);
        }

        int width = 0;
        int height = 0;
        if (getParentActivity().getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
            width = AndroidUtilities.displaySize.x - AndroidUtilities.displaySize.x / 2 - AndroidUtilities.dp(24);
            height = viewHeight - AndroidUtilities.dp(32);
        } else {
            width = AndroidUtilities.displaySize.x;
            height = viewHeight - AndroidUtilities.dp(176);
        }

        float wr = (float)width / (float)videoWidth;
        float hr = (float)height / (float)videoHeight;
        float ar = (float)videoWidth / (float)videoHeight;

        if (wr > hr) {
            width = (int) (height * ar);
        } else {
            height = (int) (width / ar);
        }

        surfaceHolder.setFixedSize(width, height);
    }

    private void fixLayout() {
        if (originalSizeTextView == null) {
            return;
        }
        originalSizeTextView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            @Override
            public boolean onPreDraw() {
                originalSizeTextView.getViewTreeObserver().removeOnPreDrawListener(this);
                if (getParentActivity().getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
                    FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)videoContainerView.getLayoutParams();
                    layoutParams.topMargin = AndroidUtilities.dp(16);
                    layoutParams.bottomMargin = AndroidUtilities.dp(16);
                    layoutParams.width = AndroidUtilities.displaySize.x / 2 - AndroidUtilities.dp(24);
                    layoutParams.leftMargin = AndroidUtilities.dp(16);
                    videoContainerView.setLayoutParams(layoutParams);

                    layoutParams = (FrameLayout.LayoutParams)textContainerView.getLayoutParams();
                    layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT;
                    layoutParams.width = AndroidUtilities.displaySize.x / 2 - AndroidUtilities.dp(24);
                    layoutParams.leftMargin = AndroidUtilities.displaySize.x / 2 + AndroidUtilities.dp(8);
                    layoutParams.rightMargin = AndroidUtilities.dp(16);
                    layoutParams.topMargin = AndroidUtilities.dp(16);
                    textContainerView.setLayoutParams(layoutParams);
                } else {
                    FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)videoContainerView.getLayoutParams();
                    layoutParams.topMargin = AndroidUtilities.dp(16);
                    layoutParams.bottomMargin = AndroidUtilities.dp(160);
                    layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT;
                    layoutParams.leftMargin = 0;
                    videoContainerView.setLayoutParams(layoutParams);

                    layoutParams = (FrameLayout.LayoutParams)textContainerView.getLayoutParams();
                    layoutParams.height = AndroidUtilities.dp(143);
                    layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT;
                    layoutParams.leftMargin = 0;
                    layoutParams.rightMargin = 0;
                    layoutParams.topMargin = 0;
                    textContainerView.setLayoutParams(layoutParams);
                }
                fixVideoSize();
                videoTimelineView.clearFrames();
                return false;
            }
        });
    }

    private void play() {
        if (videoPlayer.isPlaying()) {
            videoPlayer.pause();
            playButton.setImageResource(R.drawable.video_play);
        } else {
            try {
                playButton.setImageDrawable(null);
                lastProgress = 0;
                if (needSeek) {
                    float prog = videoTimelineView.getLeftProgress() + (videoTimelineView.getRightProgress() - videoTimelineView.getLeft()) * videoSeekBarView.getProgress();
                    videoPlayer.seekTo((int)(videoPlayer.getDuration() * prog));
                    needSeek = false;
                }
                videoPlayer.setOnSeekCompleteListener(new MediaPlayer.OnSeekCompleteListener() {
                    @Override
                    public void onSeekComplete(MediaPlayer mp) {
                        float startTime = videoTimelineView.getLeftProgress() * videoPlayer.getDuration();
                        float endTime = videoTimelineView.getRightProgress() * videoPlayer.getDuration();
                        if (startTime == endTime) {
                            startTime = endTime - 0.01f;
                        }
                        lastProgress = (videoPlayer.getCurrentPosition() - startTime) / (endTime - startTime);
                        videoSeekBarView.setProgress(lastProgress);
                    }
                });
                videoPlayer.start();
                new Thread(progressRunnable).start();
            } catch (Exception e) {
                FileLog.e("tmessages", e);
            }
        }
    }

    public void setDelegate(VideoEditorActivityDelegate delegate) {
        this.delegate = delegate;
    }

    private void startConvert() throws Exception {
        Movie movie = MovieCreator.build(videoPath);

        List<Track> tracks = movie.getTracks();
        movie.setTracks(new LinkedList<Track>());

        double startTime = videoTimelineView.getLeftProgress() * videoPlayer.getDuration() / 1000.0;
        double endTime = videoTimelineView.getRightProgress() * videoPlayer.getDuration() / 1000.0;

        boolean timeCorrected = false;
        for (Track track : tracks) {
            if (track.getSyncSamples() != null && track.getSyncSamples().length > 0) {
                if (timeCorrected) {
                    throw new RuntimeException("The startTime has already been corrected by another track with SyncSample. Not Supported.");
                }
                startTime = correctTimeToSyncSample(track, startTime, false);
                endTime = correctTimeToSyncSample(track, endTime, true);
                timeCorrected = true;
            }
        }

        for (Track track : tracks) {
            long currentSample = 0;
            double currentTime = 0;
            double lastTime = 0;
            long startSample = -1;
            long endSample = -1;

            for (int i = 0; i < track.getSampleDurations().length; i++) {
                long delta = track.getSampleDurations()[i];
                if (currentTime > lastTime && currentTime <= startTime) {
                    startSample = currentSample;
                }
                if (currentTime > lastTime && currentTime <= endTime) {
                    endSample = currentSample;
                }
                lastTime = currentTime;
                currentTime += (double) delta / (double) track.getTrackMetaData().getTimescale();
                currentSample++;
            }
            movie.addTrack(new CroppedTrack(track, startSample, endSample));
        }
        long start1 = System.currentTimeMillis();
        Container out = new DefaultMp4Builder().build(movie);
        long start2 = System.currentTimeMillis();

        String fileName = Integer.MIN_VALUE + "_" + UserConfig.lastLocalId + ".mp4";
        UserConfig.lastLocalId--;
        File cacheFile = new File(AndroidUtilities.getCacheDir(), fileName);
        UserConfig.saveConfig(false);

        FileOutputStream fos = new FileOutputStream(cacheFile);
        FileChannel fc = fos.getChannel();
        out.writeContainer(fc);

        fc.close();
        fos.close();
        if (delegate != null) {
            delegate.didFinishedVideoConverting(cacheFile.getAbsolutePath());
            finishFragment();
        }
    }

    private static double correctTimeToSyncSample(Track track, double cutHere, boolean next) {
        double[] timeOfSyncSamples = new double[track.getSyncSamples().length];
        long currentSample = 0;
        double currentTime = 0;
        for (int i = 0; i < track.getSampleDurations().length; i++) {
            long delta = track.getSampleDurations()[i];
            if (Arrays.binarySearch(track.getSyncSamples(), currentSample + 1) >= 0) {
                timeOfSyncSamples[Arrays.binarySearch(track.getSyncSamples(), currentSample + 1)] = currentTime;
            }
            currentTime += (double) delta / (double) track.getTrackMetaData().getTimescale();
            currentSample++;
        }
        double previous = 0;
        for (double timeOfSyncSample : timeOfSyncSamples) {
            if (timeOfSyncSample > cutHere) {
                if (next) {
                    return timeOfSyncSample;
                } else {
                    return previous;
                }
            }
            previous = timeOfSyncSample;
        }
        return timeOfSyncSamples[timeOfSyncSamples.length - 1];
    }
}
