/* Copyright 2017 Andrew Dawson
 *
 * This file is a part of lecjoa.
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of the
 * GNU General Public License as published by the Free Software Foundation; either version 3 of the
 * License, or (at your option) any later version.
 *
 * lecjoa is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 * Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with lecjoa; if not,
 * see <http://www.gnu.org/licenses>. */

package net.letime.lecjoa.fragment;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.ViewCompat;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.github.chrisbanes.photoview.PhotoView;
import com.github.chrisbanes.photoview.PhotoViewAttacher;
import net.letime.lecjoa.R;
import net.letime.lecjoa.ViewMediaActivity;
import net.letime.lecjoa.entity.Attachment;
import com.squareup.picasso.Callback;
import com.squareup.picasso.NetworkPolicy;
import com.squareup.picasso.Picasso;

import java.util.Objects;

import kotlin.jvm.functions.Function0;

public final class ViewMediaFragment extends BaseFragment {
    public interface PhotoActionsListener {
        void onBringUp();

        void onDismiss();

        void onPhotoTap();
    }

    private PhotoViewAttacher attacher;
    private PhotoActionsListener photoActionsListener;
    private View rootView;
    private PhotoView photoView;
    private TextView descriptionView;

    private boolean showingDescription;
    private boolean isDescriptionVisible;
    private Function0 toolbarVisibiltyDisposable;

    private static final String ARG_START_POSTPONED_TRANSITION = "startPostponedTransition";
    private static final String ATTACH_ARG = "attach";

    public static ViewMediaFragment newInstance(@NonNull Attachment attachment,
                                                boolean shouldStartPostponedTransition) {
        Bundle arguments = new Bundle();
        ViewMediaFragment fragment = new ViewMediaFragment();
        arguments.putParcelable(ATTACH_ARG, attachment);
        arguments.putBoolean(ARG_START_POSTPONED_TRANSITION, shouldStartPostponedTransition);

        fragment.setArguments(arguments);
        return fragment;
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        photoActionsListener = (PhotoActionsListener) context;
    }

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, final ViewGroup container,
                             Bundle savedInstanceState) {
        rootView = inflater.inflate(R.layout.fragment_view_media, container, false);
        photoView = rootView.findViewById(R.id.view_media_image);
        descriptionView = rootView.findViewById(R.id.tv_media_description);

        final Bundle arguments = Objects.requireNonNull(getArguments(), "Empty arguments");
        final Attachment attachment = arguments.getParcelable(ATTACH_ARG);
        final String url = attachment.getUrl();
        @Nullable final String description = attachment.getDescription();

        descriptionView.setText(description);
        showingDescription = !TextUtils.isEmpty(description);
        isDescriptionVisible = showingDescription;

        // Setting visibility without animations so it looks nice when you scroll images
        //noinspection ConstantConditions
        descriptionView.setVisibility(showingDescription
        && (((ViewMediaActivity) getActivity())).isToolbarVisible()
                ? View.VISIBLE : View.GONE);

        attacher = new PhotoViewAttacher(photoView);

        // Clicking outside the photo closes the viewer.
        attacher.setOnOutsidePhotoTapListener(imageView -> photoActionsListener.onDismiss());

        attacher.setOnClickListener(v -> onMediaTap());

        /* A vertical swipe motion also closes the viewer. This is especially useful when the photo
         * mostly fills the screen so clicking outside is difficult. */
        attacher.setOnSingleFlingListener((e1, e2, velocityX, velocityY) -> {
            if (Math.abs(velocityY) > Math.abs(velocityX)) {
                photoActionsListener.onDismiss();
                return true;
            }
            return false;
        });

        ViewCompat.setTransitionName(photoView, url);

        // If we are the view to be shown initially...
        if (arguments.getBoolean(ARG_START_POSTPONED_TRANSITION)) {
            // Try to load image from disk.
            Picasso.with(getContext())
                    .load(url)
                    .noFade()
                    .networkPolicy(NetworkPolicy.OFFLINE)
                    .into(photoView, new Callback() {
                        @Override
                        public void onSuccess() {
                            // if we loaded image from disk, we should check that view is attached.
                            if (ViewCompat.isAttachedToWindow(photoView)) {
                                finishLoadingSuccessfully();
                            } else {
                                // if view is not attached yet, wait for an attachment and
                                // start transition when it's finally ready.
                                photoView.addOnAttachStateChangeListener(
                                        new View.OnAttachStateChangeListener() {
                                            @Override
                                            public void onViewAttachedToWindow(View v) {
                                                finishLoadingSuccessfully();
                                                photoView.removeOnAttachStateChangeListener(this);
                                            }

                                            @Override
                                            public void onViewDetachedFromWindow(View v) {
                                            }
                                        });
                            }
                        }

                        @Override
                        public void onError() {
                            // if there's no image in cache, load from network and start transition
                            // immediately.
                            photoActionsListener.onBringUp();

                            loadImageFromNetwork(url, photoView);
                        }
                    });
        } else {
            // if we're not initial page, don't bother.
            loadImageFromNetwork(url, photoView);
        }

        toolbarVisibiltyDisposable = ((ViewMediaActivity) getActivity())
                .addToolbarVisibilityListener(this::onToolbarVisibilityChange);

        return rootView;
    }

    @Override
    public void onDestroyView() {
        if (toolbarVisibiltyDisposable != null) toolbarVisibiltyDisposable.invoke();
        super.onDestroyView();
    }

    private void onMediaTap() {
        photoActionsListener.onPhotoTap();
    }

    private void onToolbarVisibilityChange(boolean visible) {
        isDescriptionVisible = showingDescription && visible;
        final int visibility = isDescriptionVisible ? View.VISIBLE : View.INVISIBLE;
        int alpha = isDescriptionVisible ? 1 : 0;
        descriptionView.animate().alpha(alpha)
                .setListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        descriptionView.setVisibility(visibility);
                        animation.removeListener(this);
                    }
                })
                .start();
    }

    @Override
    public void onDetach() {
        super.onDetach();
        Picasso.with(getContext())
                .cancelRequest(photoView);
    }

    private void loadImageFromNetwork(String url, ImageView photoView) {
        Picasso.with(getContext())
                .load(url)
                .noPlaceholder()
                .into(photoView, new Callback() {
                    @Override
                    public void onSuccess() {
                        finishLoadingSuccessfully();
                    }

                    @Override
                    public void onError() {
                        rootView.findViewById(R.id.view_media_progress).setVisibility(View.GONE);
                    }
                });
    }

    private void finishLoadingSuccessfully() {
        rootView.findViewById(R.id.view_media_progress).setVisibility(View.GONE);
        attacher.update();
        photoActionsListener.onBringUp();
    }
}
