package com.mobify.astro.plugins;

import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.design.widget.TabLayout;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;

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.LocaleChangedListener;
import com.mobify.astro.utilities.LocalizationUtilities;

import org.json.JSONArray;
import org.json.JSONObject;

import java.util.HashMap;
import java.util.Map;

public class TabBarPlugin extends AstroPlugin implements LocaleChangedListener {
    private static final String TAG = TabBarPlugin.class.getName();

    private static final String HEX_IDENTIFIER = "#";

    static final String ID_KEY = "id";
    static final String TITLE_KEY = "title";
    static final String IMAGE_KEY = "imageUrl";
    static final String SELECTED_IMAGE_KEY = "selectedImageUrl";

    protected RelativeLayout containerView;
    protected TabLayout tabLayout;
    protected ImageView border;
    protected LocalizationUtilities localizationUtilities;

    protected String selectedColor = "";
    protected String unselectedColor = "";

    private Map<String, TabData> tabs = new HashMap<>();

    class TabData {
        String id;
        String title;

        Drawable image;
        String imageUrl;

        Drawable selectedImage;
        String selectedImageUrl;

        TabLayout.Tab tab;

        TabData(JSONObject data) throws Exception {
            id = data.getString(ID_KEY);
            title = data.getString(TITLE_KEY);

            imageUrl = data.getString(IMAGE_KEY);

            if (unselectedColor.isEmpty()) {
                image = getDrawableFromURL(imageUrl);
            } else {
                image = createTintedDrawable(imageUrl, convertColor(unselectedColor));
            }

            selectedImageUrl = data.getString(SELECTED_IMAGE_KEY);

            if (selectedColor.isEmpty()) {
                selectedImage = getDrawableFromURL(selectedImageUrl);
            } else {
                selectedImage = createTintedDrawable(selectedImageUrl, convertColor(selectedColor));
            }

            if (id.isEmpty() || title.isEmpty()) {
                throw new Exception("Key 'id' and 'title' is mandatory");
            }

            tab = tabLayout.newTab();
        }
    }

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

        localizationUtilities = activity.getLocalizationUtilities();
        localizationUtilities.addLocaleChangedListener(this);
        // We are inflating the tabLayout here so that we can define a style on it
        // TabLayout default for text appearance is all caps, we want it to be default to normal casing
        // To set styles programmatically, we would need to define the AttributeSet in its
        // entirely for TabLayout. This is not ideal as we would be going away from
        // system default
        LayoutInflater inflater = activity.getLayoutInflater();
        View layout = inflater.inflate(R.layout.tabbar_layout, (ViewGroup) activity.findViewById(R.id.tabLayout_viewGroup));


        containerView = (RelativeLayout) layout.findViewById(R.id.tabLayout_viewGroup);
        border = (ImageView) layout.findViewById(R.id.tabLayout_border);

        tabLayout = (TabLayout) layout.findViewById(R.id.tabLayout);
        tabLayout.setSelectedTabIndicatorHeight(0);

        tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                TabData item = (TabData) tab.getTag();
                if (item.selectedImage != null) {
                    tab.setIcon(item.selectedImage);
                }
                triggerTabSelectedEvent(tab);
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {
                TabData item = (TabData) tab.getTag();
                if (item.image != null) {
                    tab.setIcon(item.image);
                }

                // Consider replacing this snippet of code below in the next release of astro
                // since it is a bit hacky way of fixing tab bar text bug
                ViewGroup vg = (ViewGroup) tabLayout.getChildAt(0);
                ViewGroup vgTab = (ViewGroup) vg.getChildAt(tab.getPosition());
                int tabChildsCount = vgTab.getChildCount();
                for (int i = 0; i < tabChildsCount; i++) {
                    View tabViewChild = vgTab.getChildAt(i);
                    if (tabViewChild instanceof TextView) {
                        int color = Color.GRAY;
                        if (!unselectedColor.isEmpty()) {
                            color = Color.parseColor(unselectedColor);
                        }
                        ((TextView) tabViewChild).setTextColor(color);
                    }
                }
            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {
                triggerTabSelectedEvent(tab);
            }

            private void triggerTabSelectedEvent(TabLayout.Tab tab) {
                TabData item = (TabData) tab.getTag();
                // Trigger tab selected event
                JSONObject params = new JSONObject();
                try {
                    params.put("id", item.id);
                } catch (Exception e) {
                    Log.d(TAG, "Error adding id to JSON object", e);
                }
                triggerEvent("itemSelect", params);
            }
        });
    }

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

    @RpcMethod(methodName = "setColor", parameterNames = {"color", "inactiveColor"})
    public void setColor(String color, String inactiveColor) throws Exception {
        unselectedColor = HEX_IDENTIFIER + Integer.toHexString(Color.GRAY);
        if (inactiveColor != null) {
            unselectedColor = inactiveColor;
        }
        selectedColor = color;

        int selectedColorValue = convertColor(selectedColor);
        int unselectedColorValue = convertColor(unselectedColor);
        tabLayout.setTabTextColors(unselectedColorValue, selectedColorValue);

        for (TabData tabData: tabs.values()) {
            tabData.selectedImage = createTintedDrawable(tabData.selectedImageUrl, selectedColorValue);
            tabData.image = createTintedDrawable(tabData.imageUrl, unselectedColorValue);
            tabData.tab.setTag(tabData);
            if (tabData.tab.isSelected()) {
                tabData.tab.setIcon(tabData.selectedImage);
            } else {
                tabData.tab.setIcon(tabData.image);
            }

        }
    }

    @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 = "showBorder")
    public void showBorder() {
        border.setVisibility(View.VISIBLE);
    }

    @RpcMethod(methodName = "hideBorder")
    public void hideBorder() {
        border.setVisibility(View.GONE);
    }

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

    @RpcMethod(methodName = "selectItem", parameterNames = {"itemId"})
    public void selectItem(String itemId) {
        tabs.get(itemId).tab.select();
    }

    @RpcMethod(methodName = "setItems", parameterNames = {"items"})
    public void setItems(JSONArray items) throws Exception{
        String selectedTabId = "";
        if (tabLayout.getTabCount() > 0) {
            TabData selectedTab = (TabData) tabLayout.getTabAt(tabLayout.getSelectedTabPosition()).getTag();
            selectedTabId = selectedTab.id;
        }

        tabLayout.removeAllTabs();

        for(int i = 0; i < items.length(); i++) {
            TabData item = new TabData(items.getJSONObject(i));

            tabs.put(item.id, item);
            setTabTitleDescription(item);

            if (item.image != null) {
                item.tab.setIcon(item.image);
            }

            item.tab.setTag(item);

            tabLayout.addTab(item.tab);

            if (!selectedTabId.isEmpty() && item.id.equals(selectedTabId)) {
                item.tab.select();
            }
        }
    }

    private Drawable getDrawableFromURL(String url) throws Exception {
        Uri drawableUri = Uri.parse(url);

        // Note that the following call blocks, this is especially noticeable if loading
        // a networked image.
        DrawableUriResolver drawableUriResolver = new DrawableUriResolver(activity);
        return drawableUriResolver.getLocalDrawable(drawableUri);
    }

    private void setTabTitleDescription(TabData item) {
        if (!item.title.isEmpty()) {
            item.tab.setText(localizationUtilities.translate(item.title));
            item.tab.setContentDescription(localizationUtilities.translate(item.title));
        }
    }

    @Override
    public void localeDidChange() {
        for(TabData item: tabs.values()) {
            setTabTitleDescription(item);
        }
        int direction = localizationUtilities.leftToRight() ? View.LAYOUT_DIRECTION_LTR : View.LAYOUT_DIRECTION_RTL;
        tabLayout.setLayoutDirection(direction);
    }

    protected Drawable createTintedDrawable(String imageUrl, int tintColor) throws Exception {
        Drawable image = getDrawableFromURL(imageUrl);

        // tinting does not work on lower api levels unless image is first wrapped
        Drawable wrappedImage = DrawableCompat.wrap(image);
        DrawableCompat.setTint(wrappedImage.mutate(), tintColor);
        return wrappedImage;
    }

    private int convertColor(String color) {
        return Color.parseColor(color);
    }
}
