package com.reactnativebluestackmodule;

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 = "BluestackModuleViewManager 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 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);
    }

    @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);
            getBannerView(view).setPropsChanged(true);
        }

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

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

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

    @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");

        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()),
                BluestackBannerView.Events.EVENT_AD_LOAD_CALLED.toString(),
                MapBuilder.of("registrationName", BluestackBannerView.Events.EVENT_AD_LOAD_CALLED.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) {
        super.receiveCommand(root, commandId, args);
        // handle command
        switch (commandId) {
            case COMMAND_CREATE_NAME:
                if (args != null) {
                    final String pref = args.getString(0);
                    getBannerView(root).setPreference(pref);
                    // getBannerView(root).create(pref);
                    // loadBanner is now called from the onAfterUpdateTransaction based on
                    // shouldLoadNow prop, to wait till all the props are updated
                }
                break;
            case COMMAND_DESTROY_NAME:
                getBannerView(root).destroy();
                break;
            case COMMAND_REFRESH_NAME:
                if (args != null) {
                    final boolean state = args.getBoolean(0);
                    getBannerView(root).refresh(state);
                }
                break;
            default: {
            }
        }

    }

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

        // Check shouldLoadNow prop
        boolean shouldLoadNow = getBannerView(root).getShouldLoadNow();
        boolean shouldLoadWhenReady = getBannerView(root).getShouldLoadWhenReady();
        boolean propsChanged = getBannerView(root).getPropsChanged();

        if (shouldLoadNow) {
            final String preference = getBannerView(root).getPreference();
            getBannerView(root).loadBannerAd(preference);
        } else if (shouldLoadWhenReady && propsChanged) {
            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());
    }
}
