package com.azerion.bluestack.react;

import com.azerion.bluestack.react.utils.BlueStackLogger;

import android.content.Context;
import android.util.TypedValue;
import android.util.Log;
import android.view.Choreographer;
import android.view.Gravity;
import android.view.View;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.annotations.ReactPropGroup;
import java.util.Map;


public class BluestackModuleViewManager extends SimpleViewManager<FrameLayout> {

    private static final String TAG = "ViewManager Bridge";

    private static final String REACT_CLASS = "BluestackModuleView";
    public final int COMMAND_CREATE_ID = 1;
    public final String COMMAND_CREATE_NAME = "create";
    public final int COMMAND_DESTROY_ID = 2;
    public final String COMMAND_DESTROY_NAME = "destroy";
    public final int COMMAND_REFRESH_ID = 3;
    public final String COMMAND_REFRESH_NAME = "refresh";

    private boolean shouldLoadNow = false;

    private BluestackBannerView getBannerView(FrameLayout view) {
        return (BluestackBannerView) view.getChildAt(0);
    }

    @ReactProp(name = "placementId")
    public void setPlacementId(FrameLayout view, String placementId) {
        getBannerView(view).setPlacementId(placementId);
        getBannerView(view).setPropsChanged(true);
    }

    @ReactProp(name = "preference")
    public void setPreference(FrameLayout view, String preference) {
        getBannerView(view).setPreference(preference);
        getBannerView(view).setPropsChanged(true);
    }

    @ReactProp(name = "adSize")
    public void setAdSize(FrameLayout view, String adSize) {
        getBannerView(view).setAdSize(adSize);
        getBannerView(view).setPropsChanged(true);
    }

    @ReactPropGroup(names = { "width", "height" }, customType = "Style")
    public void setStyle(FrameLayout view, int index, Integer value) {
        if (index == 0) {
            // Log.d(TAG, "setStyle Width DP: " + value);
            getBannerView(view).setWidth(value);
        }

        if (index == 1) {
            // Log.d(TAG, "setStyle Height DP: " + value);
            getBannerView(view).setHeight(value);
        }
    }

    @ReactProp(name = "shouldLoadWhenReady")
    public void setShouldLoadWhenReady(FrameLayout view, boolean shouldLoadWhenReady) {
        getBannerView(view).setShouldLoadWhenReady(shouldLoadWhenReady);
        getBannerView(view).setPropsChanged(true);
    }

    @ReactProp(name = "visible")
    public void setVisible(FrameLayout view, boolean visible) {
        getBannerView(view).setVisible(visible);
    }

    @Override
    @NonNull
    public String getName() {
        return REACT_CLASS;
    }

    @Override
    @NonNull
    public FrameLayout createViewInstance(@NonNull ThemedReactContext reactContext) {
        FrameLayout bannerContainer = new FrameLayout(reactContext);
        BluestackBannerView bannerView = new BluestackBannerView(reactContext);
        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.MATCH_PARENT);
        layoutParams.gravity = Gravity.CENTER;
        bannerContainer.addView(bannerView, layoutParams);
        setupLayout(bannerView);
        return bannerContainer;
    }

    private void setupLayout(BluestackBannerView view) {
        Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
            @Override
            public void doFrame(long frameTimeNanos) {
                manuallyLayoutChildren(view);
                view.getViewTreeObserver().dispatchOnGlobalLayout();
                Choreographer.getInstance().postFrameCallback(this);
            }
        });
    }

    /**
     * Layout all children properly
     */
    public void manuallyLayoutChildren(BluestackBannerView view) {
        // propWidth and propHeight coming from react-native props
        int width = dpToPx(view.getAdWidth(), view.getContext());
        int height = dpToPx(view.getAdHeight(), view.getContext());
        // Log.d(TAG, "Banner manuallyLayoutChildren Height: "
        // + view.getAdHeight() + "dp, " + height + "px");
        // Log.d(TAG, "Banner manuallyLayoutChildren Width: "
        // + view.getAdWidth() + "dp, " + width + "px");

        view.measure(
                View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
                View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY));

        view.layout(0, 0, width, height);
    }

    @Override
    public @Nullable Map getExportedCustomDirectEventTypeConstants() {
        return MapBuilder.of(
                BluestackBannerView.Events.EVENT_LOAD_BANNER.toString(),
                MapBuilder.of("registrationName", BluestackBannerView.Events.EVENT_LOAD_BANNER.toString()),
                BluestackBannerView.Events.EVENT_FAIL_TO_LOAD.toString(),
                MapBuilder.of("registrationName", BluestackBannerView.Events.EVENT_FAIL_TO_LOAD.toString()),
                BluestackBannerView.Events.EVENT_REFRESHED_BANNER.toString(),
                MapBuilder.of("registrationName", BluestackBannerView.Events.EVENT_REFRESHED_BANNER.toString()),
                BluestackBannerView.Events.EVENT_FAIL_TO_REFRESH.toString(),
                MapBuilder.of("registrationName", BluestackBannerView.Events.EVENT_FAIL_TO_REFRESH.toString()),
                BluestackBannerView.Events.EVENT_AD_CLICKED.toString(),
                MapBuilder.of("registrationName", BluestackBannerView.Events.EVENT_AD_CLICKED.toString()));
    }

    @Nullable
    @Override
    public Map<String, Integer> getCommandsMap() {
        return MapBuilder.of(
                COMMAND_CREATE_NAME, COMMAND_CREATE_ID,
                COMMAND_DESTROY_NAME, COMMAND_DESTROY_ID,
                COMMAND_REFRESH_NAME, COMMAND_REFRESH_ID);
    }

    @Override
    public void receiveCommand(@NonNull FrameLayout root, String commandId, @Nullable ReadableArray args) {
        Log.d(TAG, "receiveCommand: " + commandId);
        super.receiveCommand(root, commandId, args);
        // handle command
        switch (commandId) {
            case COMMAND_CREATE_NAME:
                if (args != null) {
                    BlueStackLogger.debug(TAG, "receiveCommand CREATE");
                    final String pref = args.getString(0);
                    getBannerView(root).create(pref);

                    // shouldLoadNow = true;
                    getBannerView(root).setPreference(pref);
                    // Post to next frame to ensure execution after all props are updated
                    root.post(() -> {
                        BlueStackLogger.debug(TAG, "Executing create() after props update");
                        getBannerView(root).create(pref);
                    });

                    // loadBanner was called from the onAfterUpdateTransaction based on
                    // shouldLoadNow, to wait till all the props are updated
                    // else the banner load gets executed before the props are updated
                }
                break;
            case COMMAND_DESTROY_NAME:
                BlueStackLogger.debug(TAG, "receiveCommand DESTROY");
                getBannerView(root).destroy();
                break;
            case COMMAND_REFRESH_NAME:
                if (args != null) {
                    BlueStackLogger.debug(TAG, "receiveCommand REFRESH");
                    final boolean state = args.getBoolean(0);
                    getBannerView(root).refresh(state);
                }
                break;
            default: {
            }
        }

    }

    @Override
    public void onAfterUpdateTransaction(@NonNull FrameLayout root) {
        super.onAfterUpdateTransaction(root);

        boolean shouldLoadWhenReady = getBannerView(root).getShouldLoadWhenReady();
        boolean propsChanged = getBannerView(root).getPropsChanged();

        BlueStackLogger.debug(TAG, "onAfterUpdateTransaction shouldLoadWhenReady: " + shouldLoadWhenReady);
        BlueStackLogger.debug(TAG, "onAfterUpdateTransaction propsChanged: " + propsChanged);

        if (shouldLoadWhenReady && propsChanged) {
            BlueStackLogger.debug(TAG, "onAfterUpdateTransaction shouldLoadWhenReady");
            getBannerView(root).createAdViewIfCan();
        }

        getBannerView(root).setPropsChanged(false);
    }

    public static int dpToPx(float dp, Context context) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
                context.getResources().getDisplayMetrics());
    }
}
