// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.arexperiments.justaline.view;

import android.animation.Animator;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.SurfaceTexture;
import android.media.AudioAttributes;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.constraint.ConstraintLayout;
import android.support.v4.content.FileProvider;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.widget.Toast;

import com.arexperiments.justaline.PermissionHelper;
import com.arexperiments.justaline.R;
import com.arexperiments.justaline.analytics.AnalyticsEvents;
import com.arexperiments.justaline.analytics.Fa;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class PlaybackView extends ConstraintLayout implements View.OnClickListener,
        TextureView.SurfaceTextureListener, MediaPlayer.OnPreparedListener {

    private static final String TAG = "PlaybackView";

    private static final String FILE_PROVIDER_AUTHORITY
            = "com.arexperiments.justaline.fileprovider";

    private File mVideoFile;

    private Fa mAnalytics;

    private Uri mUri;

    private MediaPlayer mMediaPlayer;

    private Listener mListener;

    private Surface mSurface;

    private boolean mIsOpen;

    private boolean mPaused = true;

    private AudioAttributes mAudioAttributes;

    public PlaybackView(Context context) {
        super(context);
        init();
    }

    public PlaybackView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public PlaybackView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {

        inflate(getContext(), R.layout.view_playback, this);

        setBackgroundColor(Color.BLACK);

        mAnalytics = Fa.get();

        TextureView mVideoTextureView = findViewById(R.id.video);
        mVideoTextureView.setSurfaceTextureListener(this);

        findViewById(R.id.close_button).setOnClickListener(this);
        findViewById(R.id.layout_share).setOnClickListener(this);
        findViewById(R.id.layout_save).setOnClickListener(this);

        // set margin of bottom icons to be appropriate size for screen
        View saveLayout = findViewById(R.id.layout_save);
        ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) saveLayout
                .getLayoutParams();
        layoutParams.bottomMargin = getHeightOfNavigationBar();
        saveLayout.setLayoutParams(layoutParams);

        View shareLayout = findViewById(R.id.layout_share);
        layoutParams = (ConstraintLayout.LayoutParams) shareLayout.getLayoutParams();
        layoutParams.bottomMargin = getHeightOfNavigationBar();
        shareLayout.setLayoutParams(layoutParams);

        mAudioAttributes = new AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_MEDIA)
                .setContentType(AudioAttributes.CONTENT_TYPE_MOVIE)
                .build();

    }

    public void setFilePath(String absoluteFilePath) {

        mVideoFile = new File(absoluteFilePath);
        mUri = FileProvider.getUriForFile(getContext(), FILE_PROVIDER_AUTHORITY, mVideoFile);

        if (mSurface != null) {
            prepareMediaPlayer(mUri);
        }

    }

    public void resume() {
        mPaused = false;
        if (mIsOpen && mMediaPlayer != null) {
            mMediaPlayer.start();
        }
    }

    public void pause() {
        Log.d(TAG, "pause: media player " + mMediaPlayer);
        mPaused = true;
        if (mMediaPlayer != null && (mIsOpen && mMediaPlayer.isPlaying())) {
            mMediaPlayer.pause();
        }
    }

    public void destroy() {
        // clean up temp video file
        if (mUri != null) {
            File file = new File(mUri.getPath());
            if (file.exists()) {
                file.delete();
            }
            mUri = null;
        }

        mMediaPlayer.reset();
        mMediaPlayer.release();
    }

    private void share(Uri contentUri) {
        Intent shareIntent = new Intent();
        shareIntent.setAction(Intent.ACTION_SEND);
        shareIntent.setType("video/mp4");
        shareIntent
                .putExtra(Intent.EXTRA_TEXT, getContext().getString(R.string.playback_share_text));
        shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
        shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        getContext().startActivity(
                Intent.createChooser(shareIntent, getContext().getString(R.string.share_with)));
    }

    private void save(File file) {

        if (PermissionHelper.hasStoragePermission(getContext())) {
            String directoryName = Environment.DIRECTORY_PICTURES;
            File picDirectory = Environment.getExternalStoragePublicDirectory(directoryName);

            File jalPicDirectory = new File(picDirectory, "Just a Line");

            // make our directory if it doesn't exist
            if (!jalPicDirectory.exists()) {
                jalPicDirectory.mkdirs();
            }

            File newFile = new File(jalPicDirectory, file.getName());
            boolean success = copyFile(file, newFile);

            if (success) {
                Toast.makeText(getContext().getApplicationContext(),
                        getContext().getString(R.string.saved, directoryName), Toast.LENGTH_SHORT)
                        .show();
            } else {
                Toast.makeText(getContext().getApplicationContext(), R.string.could_not_save,
                        Toast.LENGTH_SHORT).show();
            }
        } else {
            // Request permissions
            if (mListener != null) {
                mListener.requestStoragePermission();
            }
        }
    }

    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {

        PermissionHelper.sendPermissionsAnalytics(mAnalytics, permissions, grantResults);

        if (PermissionHelper.hasStoragePermission(getContext())) {
            save(mVideoFile);
        } else {
            Toast.makeText(getContext().getApplicationContext(), R.string.action_unavailable,
                    Toast.LENGTH_SHORT).show();
        }
    }

    private boolean copyFile(File sourceLocation, File endLocation) {

        boolean success = true;

        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            inputStream = new FileInputStream(sourceLocation);
            outputStream = new FileOutputStream(endLocation);

            byte[] buffer = new byte[1024];
            int length;
            while ((length = inputStream.read(buffer)) > 0) {
                outputStream.write(buffer, 0, length);
            }
        } catch (IOException e) {
            Log.e(TAG, "Failed to copy file to new location", e);
            success = false;
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                Log.e(TAG, "Failed to close input stream", e);
            }
            try {
                if (outputStream != null) {
                    outputStream.close();
                }
            } catch (IOException e) {
                Log.e(TAG, "Failed to close output stream", e);
            }
        }

        return success;
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.close_button:
                close();
                break;
            case R.id.layout_share:
                share(mUri);
                mAnalytics.send(AnalyticsEvents.EVENT_TAPPED_SHARE_RECORDING);
                break;
            case R.id.layout_save:
                save(mVideoFile);
                mAnalytics.send(AnalyticsEvents.EVENT_TAPPED_SAVE);
                break;
        }
    }

    public boolean isOpen() {
        return mIsOpen;
    }

    public void open(File file) {

        mIsOpen = true;

        setFilePath(file.getAbsolutePath());

        // animate in
        if (getTranslationX() == 0) {
            setTranslationX(getWidth());
        }

        animate().translationX(0).setListener(null);
        setVisibility(View.VISIBLE);
    }

    public void close() {
        mIsOpen = false;

        animate().translationX(getWidth()).setListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {

            }

            @Override
            public void onAnimationEnd(Animator animator) {
                setVisibility(View.GONE);
                destroy();
            }

            @Override
            public void onAnimationCancel(Animator animator) {

            }

            @Override
            public void onAnimationRepeat(Animator animator) {

            }
        });

        if (mListener != null) {
            mListener.onPlaybackClosed();
        }
    }

    public void setListener(Listener listener) {
        mListener = listener;
    }

    private void prepareMediaPlayer(Uri uri) {
        Log.d(TAG, "prepareMediaPlayer: " + uri);
        try {
            mMediaPlayer = new MediaPlayer();
            mMediaPlayer.setOnPreparedListener(this);
            mMediaPlayer.setDataSource(getContext(), uri);
            mMediaPlayer.setSurface(mSurface);
            mMediaPlayer.setAudioAttributes(mAudioAttributes);
            mMediaPlayer.setLooping(true);
            mMediaPlayer.prepare();
        } catch (IOException | IllegalStateException e) {
            Log.e(TAG, "prepareMediaPlayer: ", e);
        }
    }

    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i1) {
        mSurface = new Surface(surfaceTexture);
        if (mUri != null) {
            prepareMediaPlayer(mUri);
        }
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) {
    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
        mSurface = null;
        return false;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
    }

    public int getHeightOfNavigationBar() {
        Resources resources = getResources();
        int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
        if (resourceId > 0) {
            return resources.getDimensionPixelSize(resourceId);
        }
        return 0;
    }

    @Override
    public void onPrepared(MediaPlayer mediaPlayer) {
        if (!mPaused) {
            mMediaPlayer.start();
        }
    }

    public interface Listener {

        void onPlaybackClosed();

        void requestStoragePermission();
    }
}