package com.mobify.astro.plugins.headerbarplugin;

import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.content.res.ResourcesCompat;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;

import com.mobify.astro.AstroActivity;
import com.mobify.astro.AstroPlugin;
import com.mobify.astro.PluginResolver;
import com.mobify.astro.R;
import com.mobify.astro.messaging.EventRegistrar;
import com.mobify.astro.messaging.MessageSender;
import com.mobify.astro.messaging.annotations.RpcMethod;
import com.mobify.astro.utilities.DrawableUriResolver;
import com.mobify.astro.utilities.HeaderUtilities;
import com.mobify.astro.utilities.LocaleChangedListener;
import com.mobify.astro.utilities.LocalizationUtilities;

import java.util.Stack;

public class HeaderBarPlugin extends AstroPlugin implements HeaderContentCoordinator, LocaleChangedListener {
    private static String TAG = HeaderBarPlugin.class.getName();
    private static String BACK_ID = "back";

    RelativeLayout containerView;
    HeaderItemView leftIcon;
    HeaderItemView centerIcon;
    HeaderItemView rightIcon;

    boolean showingBottomBorder = false;

    Stack<HeaderContent> headerContentStack = new Stack<>();
    boolean isCoordinating = false;
    LocalizationUtilities localizationUtilities;

    class HeaderItemClickListener implements View.OnClickListener {
        HeaderItemView headerItemView;

        HeaderItemClickListener(HeaderItemView headerItemView) {
            this.headerItemView = headerItemView;
        }

        @Override
        public void onClick(View v) {
            if (headerItemView.currentItem != null) {
                triggerEvent("click:" + headerItemView.currentItem.id, null);
            }
        }
    }

    //region HeaderContentCoordinator implementation.

    public void pushHeaderContent(HeaderContent headerContent) {
        // Remove default header content when we first set up coordination using
        // the HeaderContentCoordinator protocol
        if (!isCoordinating) {
            // "Pull forward" the header bar content that was set up before
            // coordination began (first call to this method) if the incoming
            // `headerContent` doesn't have a value for each icon.

            HeaderContent preCoordinationHeaderContent = headerContentStack.pop();
            if (headerContent.leftIcon == null) {
                headerContent.leftIcon = preCoordinationHeaderContent.leftIcon;
            }
            if (headerContent.centerIcon == null) {
                headerContent.centerIcon = preCoordinationHeaderContent.centerIcon;
            }
            if (headerContent.rightIcon == null) {
                headerContent.rightIcon = preCoordinationHeaderContent.rightIcon;
            }

            isCoordinating = true;
        }

        // since on pwa-navigate headerContent is copied over, the previous back button could be hidden
        // by default new headerContent's back buttons are not hidden
        if (headerContent.leftIcon != null && headerContent.leftIcon.id.equals(BACK_ID)) {
            headerContent.leftIcon.hidden = false;
        }
        headerContentStack.push(headerContent);
        setHeaderContent(headerContent);
    }

    public void popHeaderContent() {
        // Pop the current header content.
        if (!headerContentStack.empty()) {
            headerContentStack.pop();
        }

        if (headerContentStack.empty()) {
            return;
        }

        HeaderContent headerContent = headerContentStack.peek();
        // hide the back button if the navigation is at its root
        if (headerContent.leftIcon != null) {
            headerContent.leftIcon.hidden = headerContentStack.size() == 1 && headerContent.leftIcon.id.equals(BACK_ID);
        }
        setHeaderContent(headerContent);
    }

    public void popToRootHeaderContent() {
        if (headerContentStack.empty()) {
            return;
        }

        HeaderContent firstItem = headerContentStack.firstElement();
        while (!headerContentStack.empty()) {
            headerContentStack.pop();
        }

        headerContentStack.push(firstItem);
        // hide the back button if the navigation is at its root
        if (firstItem.leftIcon != null) {
            firstItem.leftIcon.hidden = firstItem.leftIcon.id.equals(BACK_ID);
        }
        setHeaderContent(firstItem);
    }

    public HeaderContent getLatestHeaderContent() {
        if (headerContentStack.isEmpty()) {
            return null;
        }
        return headerContentStack.lastElement();
    }

    //endregion

    public HeaderBarPlugin(@NonNull AstroActivity activity, @NonNull PluginResolver pluginResolver,
                           @NonNull EventRegistrar eventRegistrar, @NonNull MessageSender messageSender) {
        super(activity, pluginResolver, eventRegistrar, messageSender);

        final int minItemSize = activity.getResources().getDimensionPixelSize(R.dimen.header_bar_item_min_size);
        final int headerBarPadding = activity.getResources().getDimensionPixelSize(R.dimen.header_bar_padding);
        final int headerBarHeight = HeaderUtilities.getActionBarHeight(activity);
        localizationUtilities = activity.getLocalizationUtilities();
        localizationUtilities.addLocaleChangedListener(this);

        //region Container.

        RelativeLayout.LayoutParams containerParams = new RelativeLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT, headerBarHeight
        );

        containerView = new RelativeLayout(activity);
        containerView.setMinimumHeight(headerBarHeight);
        containerView.setLayoutParams(containerParams);
        containerView.setPadding(headerBarPadding, headerBarPadding, headerBarPadding, headerBarPadding);
        containerView.setBackgroundColor(Color.WHITE);

        //endregion

        //region Left icon.

        leftIcon = new HeaderItemView(activity, Gravity.CENTER);
        setLeftIconLayout();
        leftIcon.setId(R.id.header_bar_left_item_id);
        leftIcon.setMinimumWidth(minItemSize);
        leftIcon.setOnClickListener(new HeaderItemClickListener(leftIcon));

        //endregion

        //region Right icon.

        rightIcon = new HeaderItemView(activity, Gravity.CENTER);
        setRightIconLayout();
        rightIcon.setId(R.id.header_bar_right_item_id);
        rightIcon.setMinimumWidth(minItemSize);
        rightIcon.setOnClickListener(new HeaderItemClickListener(rightIcon));

        //endregion

        //region Center icon.

        centerIcon = new HeaderItemView(activity, Gravity.LEFT);
        setCenterIconLayout();
        centerIcon.setMinimumWidth(minItemSize);
        centerIcon.setOnClickListener(new HeaderItemClickListener(centerIcon));

        //endregion

        containerView.addView(leftIcon);
        containerView.addView(centerIcon);
        containerView.addView(rightIcon);

        // Set the initial header content.
        headerContentStack.push(new HeaderContent());
    }

    private void setCenterIconLayout() {
        RelativeLayout.LayoutParams centerIconParams = new RelativeLayout.LayoutParams(
                LinearLayout.LayoutParams.WRAP_CONTENT,
                LinearLayout.LayoutParams.WRAP_CONTENT
        );
        centerIconParams.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE);
        centerIcon.setLayoutParams(centerIconParams);
    }

    private void setLeftIconLayout() {
        final int minItemSize = activity.getResources().getDimensionPixelSize(R.dimen.header_bar_item_min_size);

        //region Left icon layout.

        RelativeLayout.LayoutParams leftIconParams = new RelativeLayout.LayoutParams(
                LinearLayout.LayoutParams.WRAP_CONTENT,
                minItemSize
        );

        // The proper way to align the icons for ltr and rtl is to use start/end instead of left/right
        // The problem with this is an activity restart is required for the layout to change after
        // the locale changes. Using left/right allows us to change the layout on the fly.
        int leftAlign = localizationUtilities.leftToRight() ?
                RelativeLayout.ALIGN_PARENT_LEFT :
                RelativeLayout.ALIGN_PARENT_RIGHT;
        leftIconParams.addRule(leftAlign, RelativeLayout.TRUE);

        leftIcon.setLayoutParams(leftIconParams);

        //endregion
    }

    private void setRightIconLayout() {
        final int minItemSize = activity.getResources().getDimensionPixelSize(R.dimen.header_bar_item_min_size);

        //region Right icon layout.

        RelativeLayout.LayoutParams rightIconParams = new RelativeLayout.LayoutParams(
                LinearLayout.LayoutParams.WRAP_CONTENT,
                minItemSize
        );

        // The proper way to align the icons for ltr and rtl is to use start/end instead of left/right
        // The problem with this is an activity restart is required for the layout to change after
        // the locale changes. Using left/right allows us to change the layout on the fly.
        int rightAlign = localizationUtilities.leftToRight() ?
                RelativeLayout.ALIGN_PARENT_RIGHT :
                RelativeLayout.ALIGN_PARENT_LEFT;
        rightIconParams.addRule(rightAlign, RelativeLayout.TRUE);

        rightIcon.setLayoutParams(rightIconParams);

        //endregion
    }

    public void setHeaderContent(HeaderContent headerContent) {
        HeaderContentItem leftIconContentItem = headerContent.leftIcon;
        HeaderContentItem centerIconContentItem = headerContent.centerIcon;

        if (leftIconContentItem == null && headerContentStack.size() > 1) {
            DrawableUriResolver drawableResolver = new DrawableUriResolver(activity);

            try {
                leftIconContentItem = new HeaderContentItem();
                leftIconContentItem.id = BACK_ID;
                leftIconContentItem.contentImage = activity.getResources().getDrawable(R.drawable.abc_ic_ab_back_material);
                leftIconContentItem.isSystemBackIcon = true;
                headerContent.leftIcon = leftIconContentItem;
            } catch (Exception e) {
                Log.e(TAG, "Could not load back button drawable.", e);
            }
        }

        boolean isLeftToRight = localizationUtilities.leftToRight();
        if (centerIconContentItem != null) {
            if (centerIconContentItem.contentImage != null) {
                centerIcon.contentGravity = Gravity.CENTER;
            } else {
                centerIcon.contentGravity = isLeftToRight ?
                        Gravity.LEFT :
                        Gravity.RIGHT;
            }
        }

        RelativeLayout.LayoutParams centerIconLayoutParams = (RelativeLayout.LayoutParams)centerIcon.getLayoutParams();

        // In Astro Android,
        //    Center icon images are centered aligned
        //    Center titles are left aligned to conform to material design
        //        See https://material.google.com/layout/structure.html#structure-app-bar
        //   Note: In rtl layouts, center titles are right aligned
        if (centerIconContentItem != null && centerIconContentItem.contentImage != null) {
            centerIconLayoutParams.leftMargin = 0;
            centerIconLayoutParams.rightMargin = 0;

            // Remove both rules for both ltr and rtl
            centerIconLayoutParams.removeRule(RelativeLayout.LEFT_OF);
            centerIconLayoutParams.removeRule(RelativeLayout.ALIGN_PARENT_LEFT);
            centerIconLayoutParams.removeRule(RelativeLayout.RIGHT_OF);
            centerIconLayoutParams.removeRule(RelativeLayout.ALIGN_PARENT_RIGHT);

            centerIconLayoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
        } else {
            int leftMarginResourceId = R.dimen.header_bar_item_left_padding_with_left_item;
            if (leftIconContentItem == null || leftIconContentItem.hidden) {
                leftMarginResourceId = R.dimen.header_bar_item_left_padding;
            }

            centerIconLayoutParams.removeRule(RelativeLayout.ALIGN_PARENT_RIGHT);
            centerIconLayoutParams.removeRule(RelativeLayout.ALIGN_PARENT_LEFT);
            centerIconLayoutParams.removeRule(RelativeLayout.CENTER_HORIZONTAL);

            // Align to different sides depending if left-to-right or right-to-left
            if (isLeftToRight) {
                centerIconLayoutParams.removeRule(RelativeLayout.RIGHT_OF);
                centerIconLayoutParams.addRule(RelativeLayout.LEFT_OF, rightIcon.getId());

                centerIconLayoutParams.leftMargin = activity.getResources().getDimensionPixelSize(leftMarginResourceId);
                centerIconLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT, RelativeLayout.TRUE);
            } else {
                centerIconLayoutParams.removeRule(RelativeLayout.LEFT_OF);
                centerIconLayoutParams.addRule(RelativeLayout.RIGHT_OF, rightIcon.getId());

                centerIconLayoutParams.rightMargin = activity.getResources().getDimensionPixelSize(leftMarginResourceId);
                centerIconLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE);
            }
        }

        HeaderBarPlugin.setIconAlignment(leftIcon, leftIconContentItem, isLeftToRight, Gravity.LEFT,
                activity.getResources().getDimensionPixelSize(R.dimen.header_bar_item_default_left_padding),
                activity.getResources().getDimensionPixelSize(R.dimen.header_bar_item_left_text_padding) );

        HeaderBarPlugin.setIconAlignment(rightIcon, headerContent.rightIcon, isLeftToRight, Gravity.RIGHT,
                activity.getResources().getDimensionPixelSize(R.dimen.header_bar_item_default_right_padding),
                activity.getResources().getDimensionPixelSize(R.dimen.header_bar_item_right_text_padding) );

        leftIcon.setHeaderContentItem(leftIconContentItem);
        centerIcon.setHeaderContentItem(centerIconContentItem);
        rightIcon.setHeaderContentItem(headerContent.rightIcon);
    }

    private static void setIconAlignment(HeaderItemView icon, HeaderContentItem iconItem, boolean isLeftToRight, int ltrGravity, int defaultMargin, int textMargin) {
        RelativeLayout.LayoutParams iconLayoutParams = (RelativeLayout.LayoutParams)icon.getLayoutParams();
        iconLayoutParams.leftMargin = 0;
        iconLayoutParams.rightMargin = 0;
        // If we are in Ltr mode, just use the incoming alignment. If Rtl, flip alignment.
        final int gravity = isLeftToRight ? ltrGravity : (ltrGravity == Gravity.LEFT ? Gravity.RIGHT : Gravity.LEFT);
        if (iconItem != null && iconItem.contentTitle != null ) {
            if (gravity == Gravity.LEFT) {
                iconLayoutParams.leftMargin = textMargin;
            } else {
                iconLayoutParams.rightMargin = textMargin;
            }
            icon.contentGravity = Gravity.CENTER_VERTICAL | gravity;
        } else {
            if (gravity == Gravity.LEFT) {
                iconLayoutParams.leftMargin = defaultMargin;
            } else {
                iconLayoutParams.rightMargin = defaultMargin;
            }
            icon.contentGravity = Gravity.CENTER;
        }
    }

    @Override
    public View getView() {
        return containerView;
    }

    @RpcMethod(methodName = "setBackgroundColor", parameterNames = {"color"})
    public void setBackgroundColor(String color) {
        containerView.setBackgroundColor(Color.parseColor(color));
    }

    @RpcMethod(methodName = "showBottomBorder")
    public void showBottomBorder() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            // Use setTranslationZ instead of setElevation because setting elevation seems to get reset when we
            // add the view. To use elevation we would have to call showBottomBorder always after the view has
            // been added. Calculated elevation is made up of setElevation + setTranslationZ so this works.
            containerView.setTranslationZ(activity.getResources().getDimensionPixelSize(R.dimen.header_bar_translation_z));
        } else {
            // shadows aren't supported in lower version of Android, just show a line
            containerView.setBackground(ResourcesCompat.getDrawable(activity.getResources(), R.drawable.bottom_border, null));
        }
        showingBottomBorder = true;
    }

    @RpcMethod(methodName = "hideBottomBorder")
    public void hideBottomBorder() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            containerView.setTranslationZ(0.0f);
        } else {
            containerView.setBackground(null);
        }
        showingBottomBorder = false;
    }

    @RpcMethod(methodName = "isShowingBottomBorder")
    public boolean isShowingBottomBorder() {
        return showingBottomBorder;
    }

    @RpcMethod(methodName = "setTextColor", parameterNames = {"color"})
    public void setTextColor(String color) {
        int textColor = Color.parseColor(color);

        leftIcon.setTextColor(textColor);
        centerIcon.setTextColor(textColor);
        rightIcon.setTextColor(textColor);
    }

    @RpcMethod(methodName = "setOpaque")
    public void setOpaque() {
        Log.w(TAG, "setOpaque is not implemented on Android");
    }

    @RpcMethod(methodName = "setTranslucent")
    public void setTranslucent() {
        Log.w(TAG, "setTranslucent is not implemented on Android");
    }

    @RpcMethod(methodName = "showBackButtonText")
    public void showBackButtonText() {
        Log.w(TAG, "showBackButtonText is not implemented on Android");
    }

    @RpcMethod(methodName = "hideBackButtonText")
    public void hideBackButtonText() {
        Log.w(TAG, "hideBackButtonText is not implemented on Android");
    }

    //region Left icon.

    @RpcMethod(methodName = "showLeftIcon")
    public void showLeftIcon() {
        HeaderContent headerContent = headerContentStack.peek();
        if (headerContent.leftIcon != null) {
            headerContent.leftIcon.hidden = false;
            setHeaderContent(headerContent);
        }
    }

    @RpcMethod(methodName = "hideLeftIcon")
    public void hideLeftIcon() {
        HeaderContent headerContent = headerContentStack.peek();
        if (headerContent.leftIcon != null) {
            headerContent.leftIcon.hidden = true;
            setHeaderContent(headerContent);
        }

    }

    @RpcMethod(methodName = "setLeftTitle", parameterNames = {"title", "id"})
    public void setLeftTitle(String title, String id) throws Exception {
        HeaderContent headerContent = headerContentStack.peek();
        headerContent.leftIcon = HeaderContentItem.fromTitle(pluginResolver, activity, id, title);
        setHeaderContent(headerContent);
    }

    @RpcMethod(methodName = "setLeftIcon", parameterNames = {"url", "id", "shouldFlipOnRtl"})
    public void setLeftIcon(String url, String id, boolean shouldFlipOnRtl) throws Exception {
        HeaderContent headerContent = headerContentStack.peek();
        headerContent.leftIcon = HeaderContentItem.fromImageUrl(pluginResolver, activity, id, url, shouldFlipOnRtl);
        setHeaderContent(headerContent);
    }

    @RpcMethod(methodName = "setLeftPlugin", parameterNames = {"address", "id"})
    public void setLeftPlugin(String address, String id) throws Exception {
        HeaderContent headerContent = headerContentStack.peek();
        headerContent.leftIcon = HeaderContentItem.fromPlugin(pluginResolver, activity, id, address);
        setHeaderContent(headerContent);
    }

    //endregion

    //region Center icon.

    @RpcMethod(methodName = "showCenterIcon")
    public void showCenterIcon() {
        HeaderContent headerContent = headerContentStack.peek();
        if (headerContent.centerIcon != null) {
            headerContent.centerIcon.hidden = false;
            setHeaderContent(headerContent);
        }
    }

    @RpcMethod(methodName = "hideCenterIcon")
    public void hideCenterIcon() {
        HeaderContent headerContent = headerContentStack.peek();
        if (headerContent.centerIcon != null) {
            headerContent.centerIcon.hidden = true;
            setHeaderContent(headerContent);
        }
    }

    @RpcMethod(methodName = "setCenterTitle", parameterNames = {"title", "id"})
    public void setCenterTitle(String title, String id) throws Exception {
        HeaderContent headerContent = headerContentStack.peek();
        headerContent.centerIcon = HeaderContentItem.fromTitle(pluginResolver, activity, id, title);
        setHeaderContent(headerContent);
    }

    @RpcMethod(methodName = "setCenterIcon", parameterNames = {"url", "id", "shouldFlipOnRtl"})
    public void setCenterIcon(String url, String id, boolean shouldFlipOnRtl) throws Exception {
        HeaderContent headerContent = headerContentStack.peek();
        headerContent.centerIcon = HeaderContentItem.fromImageUrl(pluginResolver, activity, id, url, shouldFlipOnRtl);
        setHeaderContent(headerContent);
    }

    @RpcMethod(methodName = "setCenterPlugin", parameterNames = {"address", "id"})
    public void setCenterPlugin(String address, String id) throws Exception {
        HeaderContent headerContent = headerContentStack.peek();
        headerContent.centerIcon = HeaderContentItem.fromPlugin(pluginResolver, activity, id, address);
        setHeaderContent(headerContent);
    }

    //endregion

    //region Right icon.

    @RpcMethod(methodName = "showRightIcon")
    public void showRightIcon() {
        HeaderContent headerContent = headerContentStack.peek();
        if (headerContent.rightIcon != null) {
            headerContent.rightIcon.hidden = false;
            setHeaderContent(headerContent);
        }
    }

    @RpcMethod(methodName = "hideRightIcon")
    public void hideRightIcon() {
        HeaderContent headerContent = headerContentStack.peek();
        if (headerContent.rightIcon != null) {
            headerContent.rightIcon.hidden = true;
            setHeaderContent(headerContent);
        }
    }

    @RpcMethod(methodName = "setRightTitle", parameterNames = {"title", "id"})
    public void setRightTitle(String title, String id) throws Exception {
        HeaderContent headerContent = headerContentStack.peek();
        headerContent.rightIcon = HeaderContentItem.fromTitle(pluginResolver, activity, id, title);
        setHeaderContent(headerContent);
    }

    @RpcMethod(methodName = "setRightIcon", parameterNames = {"url", "id", "shouldFlipOnRtl"})
    public void setRightIcon(String url, String id, boolean shouldFlipOnRtl) throws Exception {
        HeaderContent headerContent = headerContentStack.peek();
        headerContent.rightIcon = HeaderContentItem.fromImageUrl(pluginResolver, activity, id, url, shouldFlipOnRtl);
        setHeaderContent(headerContent);
    }

    @RpcMethod(methodName = "setRightPlugin", parameterNames = {"address", "id"})
    public void setRightPlugin(String address, String id) throws Exception {
        HeaderContent headerContent = headerContentStack.peek();
        headerContent.rightIcon = HeaderContentItem.fromPlugin(pluginResolver, activity, id, address);
        setHeaderContent(headerContent);
    }

    //endregion

    @Override
    public void localeDidChange() {
        HeaderContent headerContent = headerContentStack.peek();

        // Re-set layout params for icons to adjust for ltr<->rtl
        setLeftIconLayout();
        setCenterIconLayout();
        setRightIconLayout();
        setHeaderContent(headerContent);
    }
}
