/*
 * Part of Phonk http://www.phonk.io
 * A prototyping platform for Android devices
 *
 * Copyright (C) 2013 - 2017 Victor Diaz Barrales @victordiaz (Protocoder)
 * Copyright (C) 2017 - Victor Diaz Barrales @victordiaz (Phonk)
 *
 * Phonk is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Phonk 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 Lesser General Public License
 * along with Phonk. If not, see <http://www.gnu.org/licenses/>.
 *
 */

package io.phonk.runner.apprunner.api;

import android.animation.ValueAnimator;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.net.Uri;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;

import androidx.browser.customtabs.CustomTabsIntent;
import androidx.core.view.MotionEventCompat;
import androidx.fragment.app.FragmentManager;

import java.util.HashMap;
import java.util.LinkedHashMap;

import io.phonk.runner.AppRunnerFragment;
import io.phonk.runner.R;
import io.phonk.runner.apidoc.annotation.PhonkMethod;
import io.phonk.runner.apidoc.annotation.PhonkObject;
import io.phonk.runner.apprunner.AppRunner;
import io.phonk.runner.apprunner.api.common.ReturnInterface;
import io.phonk.runner.apprunner.api.common.ReturnObject;
import io.phonk.runner.apprunner.api.widgets.PPopupDialogFragment;
import io.phonk.runner.apprunner.api.widgets.PToolbar;
import io.phonk.runner.apprunner.api.widgets.StyleProperties;
import io.phonk.runner.apprunner.api.widgets.WidgetHelper;
import io.phonk.runner.apprunner.interpreter.PhonkNativeArray;
import io.phonk.runner.base.utils.AndroidUtils;
import io.phonk.runner.base.utils.MLog;

@PhonkObject(mergeFrom = "ViewsArea")
public class PUI extends PViewsArea {
    LinkedHashMap<String, StyleProperties> styles = new LinkedHashMap<>();

    public StyleProperties style;
    public StyleProperties theme;
    private boolean isMainLayoutSetup = false;
    public int screenWidth;
    public int screenHeight;

    public PUI(AppRunner appRunner) {
        super(appRunner);
    }

    @Override
    public void initForParentFragment(AppRunnerFragment fragment) {
        super.initForParentFragment(fragment);

        if (fragment != null) {
            toolbar = new PToolbar(getAppRunner(), getActivity().getSupportActionBar());
        }
        initializeLayout();
    }


    /**
     * This method creates the basic layout where the user created views will lay out
     * It has to be programatically created since it might be used somewhere else without access to the R file
     *   scriptedLayout
     *     [rep]  uiHolderLayout -> common background
     *       scrollView
     *       absolutelayout
     */
    protected void initializeLayout() {
        if (isMainLayoutSetup) return;
        View mainLayout = initMainLayout();

        // here we add the layout
        if (getFragment() != null) {
            getFragment().addScriptedLayout(mainLayout);
        } else if (getService() != null) {
            getService().addScriptedLayout(mainLayout);
        }

        isMainLayoutSetup = true;

        setTheme();
        setStyle();
        background((String) theme.get("background"));
        // background("#cccccc");
    }

    @PhonkMethod
    public PViewsArea newArea(Object x, Object y, Object w, Object h) {
        PViewsArea pViewsArea = new PViewsArea(mAppRunner);
        View v = pViewsArea.initMainLayout();
        addViewAbsolute(v, x, y, w, h);
        return pViewsArea;
    }

    @PhonkMethod
    public PViewsArea newArea() {
        PViewsArea pViewsArea = new PViewsArea(mAppRunner);
        View v = pViewsArea.initMainLayout();
        return pViewsArea;
    }

    private void setTheme() {
        theme = new StyleProperties();

        theme.put("background", getContext().getResources().getString(R.color.phonk_backgroundColor));
        theme.put("primary", getContext().getResources().getString(R.color.phonk_colorPrimary));
        theme.put("primaryShade", "#11FFFFFF");
        theme.put("secondary", getContext().getResources().getString(R.color.phonk_colorSecondary));
        theme.put("secondaryShade", getContext().getResources().getString(R.color.phonk_colorSecondary));
        theme.put("secondaryShade", "#11FFFFFF");
        theme.put("textPrimary", getContext().getResources().getString(R.color.phonk_colorPrimary));
        theme.put("textSecondary", getContext().getResources().getString(R.color.phonk_colorSecondary));
    }

    private void setStyle() {
        String colorPrimary = (String) theme.get("primary");
        String colorPrimaryShade = (String) theme.get("primaryShade");
        String colorSecondary = (String) theme.get("secondary");
        String colorSecondaryShade = (String) theme.get("secondaryShade");
        String colorBackground = (String) theme.get("background");
        String colorTransparent = "#00FFFFFF";
        String textSecondary = getContext().getResources().getString(R.color.phonk_colorSecondary);
        int borderSize = 5;

        style = new StyleProperties();
        style.put("enabled", style, true);
        style.put("opacity", style, 1.0f);
        style.put("visibility", style, "visible");
        style.put("background", style, colorTransparent);
        style.put("backgroundHover", style, "#88000000");
        style.put("backgroundPressed", style, "#88FFFFFF");
        style.put("backgroundSelected", style, "#88000000");
        style.put("backgroundChecked", style, "#88000000");
        style.put("borderColor", style, colorTransparent);
        style.put("borderWidth", style, 0);
        style.put("borderRadius", style, 0);

        style.put("src", style, "");
        style.put("srcPressed", style, "");

        style.put("textColor", style, textSecondary);
        style.put("textSize", style, 16);
        style.put("textFont", style, "monospace");
        style.put("textStyle", style, "normal");
        style.put("textAlign", style, "center");
        style.put("textTransform", style, "none");
        style.put("padding", style, AndroidUtils.dpToPixels(getContext(), 2));
        style.put("paddingLeft", style, AndroidUtils.dpToPixels(getContext(), 2));
        style.put("paddingTop", style, AndroidUtils.dpToPixels(getContext(), 2));
        style.put("paddingRight", style, AndroidUtils.dpToPixels(getContext(), 2));
        style.put("paddingBottom", style, AndroidUtils.dpToPixels(getContext(), 2));

        style.put("hintColor", style, colorSecondaryShade);

        style.put("animInBefore", style, "this.x(0).y(100)");
        style.put("animIn", style, "this.animate().x(100)");
        style.put("animOut", style, "this.animate().x(0)");

        // slider
        style.put("slider", style, colorPrimary);
        style.put("sliderPressed", style, colorPrimary);
        style.put("sliderHeight", style, 20);
        style.put("sliderBorderSize", style, 0);
        style.put("sliderBorderColor", style, colorTransparent);

        // pad
        style.put("padSize", style, AndroidUtils.dpToPixels(mContext, 50));
        // style.put("background", style, colorSecondaryShade);
        style.put("padColor", style, colorPrimary);
        style.put("padBorderColor", style, colorPrimary);
        style.put("padBorderSize", style, AndroidUtils.dpToPixels(mContext, 2));

        // knobf
        style.put("knobBorderWidth", style, AndroidUtils.dpToPixels(mAppRunner.getAppContext(), 1));
        style.put("knobProgressWidth", style, AndroidUtils.dpToPixels(mAppRunner.getAppContext(), 2));
        style.put("knobProgressSeparation", style, AndroidUtils.dpToPixels(mAppRunner.getAppContext(), 15));
        style.put("knobBorderColor", style, colorSecondary);
        style.put("knobProgressColor", style, colorPrimary);

        // matrix
        style.put("matrixCellColor", style, colorTransparent);
        style.put("matrixCellSelectedColor", style, colorPrimary);
        style.put("matrixCellBorderSize", style, AndroidUtils.dpToPixels(mAppRunner.getAppContext(), 1));
        style.put("matrixCellBorderColor", style, colorSecondaryShade);
        style.put("matrixCellBorderRadius", style, 0);

        // plot
        style.put("plotColor", style, colorPrimary);
        style.put("background", style, colorSecondaryShade);
        style.put("plotWidth", style, AndroidUtils.dpToPixels(mAppRunner.getAppContext(), 2));
        styles.put("*", style);

        StyleProperties buttonStyle = new StyleProperties();
        buttonStyle.put("textStyle", buttonStyle, "bold");
        buttonStyle.put("textAlign", buttonStyle, "center");
        buttonStyle.put("background", buttonStyle, colorSecondaryShade);
        // buttonStyle.put("backgroundPressed", buttonStyle, colorPrimary);

        style.put("srcTintPressed", buttonStyle, colorSecondary);
        styles.put("button", buttonStyle);

        StyleProperties imageStyle = new StyleProperties();
        imageStyle.put("background", imageStyle, colorTransparent);
        imageStyle.put("srcMode", imageStyle, "fit");
        styles.put("image", imageStyle);

        StyleProperties imageButtonStyle = new StyleProperties();
        imageButtonStyle.put("srcMode", imageButtonStyle, "resize");
        style.put("srcTintPressed", imageButtonStyle, colorSecondary);
        styles.put("imagebutton", imageButtonStyle);

        StyleProperties knobStyle = new StyleProperties();
        knobStyle.put("background", knobStyle, colorTransparent);
        knobStyle.put("textColor", knobStyle, colorSecondary);
        knobStyle.put("textFont", knobStyle, "monospace");
        knobStyle.put("textSize", knobStyle, 10);
        styles.put("knob", knobStyle);

        StyleProperties textStyle = new StyleProperties();
        textStyle.put("background", textStyle, colorTransparent);
        textStyle.put("textColor", textStyle, textSecondary);
        // textStyle.put("textSize", textStyle, 12);
        textStyle.put("textAlign", textStyle, "left");
        styles.put("text", textStyle);

        StyleProperties toggleStyle = new StyleProperties();
        toggleStyle.put("textColor", toggleStyle, colorPrimary);
        toggleStyle.put("background", toggleStyle, colorTransparent);
        toggleStyle.put("backgroundChecked", toggleStyle, colorSecondaryShade);
        toggleStyle.put("backgroundPressed", toggleStyle, colorSecondaryShade);
        toggleStyle.put("borderColor", toggleStyle, colorSecondary);
        toggleStyle.put("borderWidth", toggleStyle, borderSize);
        styles.put("toggle", toggleStyle);

        StyleProperties inputStyle = new StyleProperties();
        inputStyle.put("textAlign", inputStyle, "left");
        inputStyle.put("background", inputStyle, colorSecondaryShade);
        inputStyle.put("backgroundPressed", inputStyle, colorSecondaryShade);
        inputStyle.put("borderColor", inputStyle, colorSecondary);
        inputStyle.put("borderWidth", inputStyle, borderSize);
        // inputStyle.put("textAlign", inputStyle, "center");
        inputStyle.put("textColor", inputStyle, textSecondary);
        styles.put("input", inputStyle);

        StyleProperties matrixStyle = new StyleProperties();
        textStyle.put("background", matrixStyle, colorTransparent);
        textStyle.put("backgroundPressed", matrixStyle, colorTransparent);
        styles.put("matrix", matrixStyle);

        StyleProperties plotStyle = new StyleProperties();
        plotStyle.put("textColor", plotStyle, "#ffffff");
        // plotStyle.put("borderColor", plotStyle, colorSecondaryShade);
        plotStyle.put("background", plotStyle, colorSecondaryShade);
        styles.put("plot", plotStyle);

        StyleProperties touchPadStyle = new StyleProperties();
        touchPadStyle.put("background", plotStyle, colorSecondaryShade);
        styles.put("touchpad", plotStyle);

    }

    public void registerStyle(String name, StyleProperties widgetProp) {
        if (styles.containsKey(name) == false) {
            styles.put(name, widgetProp);
        }
    }


    public LinkedHashMap<String, StyleProperties> getStyles() {
        return styles;
    }

    public void screenMode(String mode) {
        switch (mode) {
            case "fullscreen":
                getActivity().setFullScreen();
                break;

            case "lightsout":
                getActivity().lightsOutMode();
                break;

            case "immersive":
                getActivity().setImmersive();
                break;

            default:
                getActivity().setNormal();
        }

        updateScreenSizes();
    }

    public void updateScreenSizes() {
        screenWidth = uiAbsoluteLayout.width();
        screenHeight = uiAbsoluteLayout.height();
    }

    public void screenOrientation(String mode) {
        if (mode.equals("landscape")) {
            getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        } else if (mode.equals("portrait")) {
            getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        } else {
            getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
        }

        updateScreenSizes();
    }

    /**
     * Adds an overlay title to the script
     *
     * @param title
     */
    @PhonkMethod
    public void addTitle(String title) {
        getFragment().changeTitle(title);
    }

    /**
     * Adds an overlay subtitle to the script
     *
     * @param subtitle
     */
    @PhonkMethod
    public void addSubtitle(String subtitle) {
        getFragment().changeSubtitle(subtitle);
    }

    /**
     * Creates a popup
     *
     * @return
     * @status TOREVIEW
     */
    @PhonkMethod
    public PPopupDialogFragment popup() {
        FragmentManager fm = getActivity().getSupportFragmentManager();
        PPopupDialogFragment pPopupCustomFragment = PPopupDialogFragment.newInstance(fm);

        return pPopupCustomFragment;
    }

    /**
     * Shows a web in a different screen.
     * Once the web is opened, we loose the control of the script
     *
     * @param url
     * @status TOREVIEW
     * @advanced
     */
    @PhonkMethod
    public void showWeb(String url) {
        CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
        builder.setToolbarColor(Color.BLUE);
        builder.addDefaultShareMenuItem();
        builder.setInstantAppsEnabled(true);

        // builder.setStartAnimations(this, R.anim.slide_in_right, R.anim.slide_out_left);
        // builder.setExitAnimations(this, R.anim.slide_in_left, R.anim.slide_out_right);

        CustomTabsIntent customTabsIntent = builder.build();
        customTabsIntent.launchUrl(getActivity(), Uri.parse(url));
    }

    /**
     * Shows a little popup with a given text during t time
     *
     * @param text
     */
    @PhonkMethod
    public void toast(String text) {
        Toast.makeText(getContext(), text, Toast.LENGTH_SHORT).show();
    }

    @PhonkMethod
    public void toast(String text, boolean length) {
        int duration = Toast.LENGTH_SHORT;
        if (length) {
            duration = Toast.LENGTH_LONG;
        }
        Toast.makeText(getContext(), text, duration).show();
    }

    /**
     * Clip a view and add a shadow. This only works on Android > 5
     *
     * @param v
     * @param type 0 for rect, 1 for round
     * @param r    roundness
     * @status TOREVIEW
     * @advanced
     */
    @PhonkMethod
    public void clipAndShadow(View v, int type, int r) {
        AndroidUtils.setViewGenericShadow(v, type, 0, 0, v.getWidth(), v.getHeight(), r);
        // v.setElevation();
        // v.setZ();
        // v.animate().
    }

    @PhonkMethod
    public void clipAndShadow(View v, int type, int x, int y, int w, int h, int r) {
        AndroidUtils.setViewGenericShadow(v, type, x, y, w, h, r);
    }

    /*
     * Utilities
     */


    /**
     * Resize a view to a given width and height. If a parameter is -1 then that dimension is not changed
     *
     * @param v
     * @param w
     * @param h
     * @param animated
     * @advanced
     * @status TOREVIEW
     */
    @PhonkMethod
    public void resize(final View v, int w, int h, boolean animated) {
        if (!animated) {
            if (h != -1) {
                v.getLayoutParams().height = h;
            }
            if (w != -1) {
                v.getLayoutParams().width = w;
            }
            v.setLayoutParams(v.getLayoutParams());
        } else {
            int initHeight = v.getLayoutParams().height;
            int initWidth = v.getLayoutParams().width;
            // v.setLayoutParams(v.getLayoutParams());

            ValueAnimator animH = ValueAnimator.ofInt(initHeight, h);
            animH.addUpdateListener(valueAnimator -> {
                int val = (Integer) valueAnimator.getAnimatedValue();
                v.getLayoutParams().height = val;
                v.setLayoutParams(v.getLayoutParams());
            });
            animH.setDuration(200);
            animH.start();

            ValueAnimator animW = ValueAnimator.ofInt(initWidth, w);
            animW.addUpdateListener(valueAnimator -> {
                int val = (Integer) valueAnimator.getAnimatedValue();
                v.getLayoutParams().width = val;
                v.setLayoutParams(v.getLayoutParams());
            });
            animW.setDuration(200);
            animW.start();
        }
    }

    /**
     * Move view
     *
     * @param viewHandler
     * @param viewContainer
     * @param callback
     * @status TOREVIEW
     * @advanced
     */
    @PhonkMethod
    public void movable(View viewHandler, View viewContainer, ReturnInterface callback) {
        WidgetHelper.setMovable(viewHandler, viewContainer, callback);
    }

    /**
     * Remove movable
     *
     * @param viewHandler
     * @status TOREVIEW
     * @advanced
     */
    @PhonkMethod
    public void removeMovable(View viewHandler) {
        WidgetHelper.removeMovable(viewHandler);
    }

    class Touch {
        int id;
        Object x, y;
        String action;

        Touch() {

        }
    }

    /**
     * @param view
     * @param callback
     * @status TOREVIEW
     */
    @PhonkMethod
    public void onTouches(View view, final ReturnInterface callback) {
        final PhonkNativeArray ar = new PhonkNativeArray(20);
        final HashMap<Integer, Touch> touches = new HashMap<Integer, Touch>();

        view.setOnTouchListener((view1, motionEvent) -> {

            int pointerIndex = MotionEventCompat.getActionIndex(motionEvent);
            int pointerId = motionEvent.getPointerId(pointerIndex);

            boolean ret = false;

            switch (MotionEventCompat.getActionMasked(motionEvent)) {
                case MotionEvent.ACTION_POINTER_DOWN:
                case MotionEvent.ACTION_DOWN: {
                    MLog.d(TAG, "down: " + pointerId);

                    Touch touch = touches.get(pointerId);
                    if (touch == null) {
                        Touch t = new Touch();
                        t.id = pointerId;
                        t.x = motionEvent.getX();
                        t.y = motionEvent.getY();
                        t.action = "down";
                        touches.put(pointerId, t);
                    }
                    ret = true;
                    break;
                }
                case MotionEvent.ACTION_MOVE: {
                    for (int i = 0; i < motionEvent.getPointerCount(); i++) {
                        int id = motionEvent.getPointerId(i);
                        MLog.d(TAG, "move: " + id);

                        Touch t = touches.get(id);
                        t.x = motionEvent.getX(i);
                        t.y = motionEvent.getY(i);
                        t.action = "move";
                    }
                    break;
                }
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                case MotionEvent.ACTION_POINTER_UP:
                    MLog.d(TAG, "up: " + pointerId);
                    Touch t = touches.get(pointerId);
                    t.action = "up";
                    break;
            }


            PhonkNativeArray ar1 = new PhonkNativeArray(touches.size());
            int toRemove = -1;
            int i = 0;

            MLog.d(TAG, ">>>>>>>>>>>>>>>>>>>>> ");

            for (Integer key : touches.keySet()) {
                Touch touch = touches.get(key);

                if (touch.action.equals("up")) {
                    MLog.d(TAG, "to remove");
                    toRemove = key;
                }

                ReturnObject t = new ReturnObject();
                t.put("x", touch.x);
                t.put("y", touch.y);
                t.put("id", touch.id);
                t.put("action", touch.action);
                MLog.d(TAG, "" + i + " " + touch.id + " action " + touch.action);

                ar1.addPE(i, t);
                i++;
            }

            if (toRemove != -1) {
                MLog.d(TAG, "removing " + toRemove + " of " + touches.size());
                touches.remove(toRemove);
            }

            ReturnObject returnObject = new ReturnObject();
            returnObject.put("touches", ar1);
            returnObject.put("count", motionEvent.getPointerCount());

            callback.event(returnObject);

            return ret;
        });
    }

    /**
     * Takes a screenshot of a view in a Bitmap form
     *
     * @param v View
     * @return
     * @status TODO_EXAMPLE
     */
    @PhonkMethod
    public Bitmap takeViewScreenshot(View v) {
        return AndroidUtils.takeScreenshotView(v);
    }

    @Override
    public void __stop() {
    }

}