package com.sbugert.rnadmob;

import android.content.Context;
import android.view.View;

import androidx.annotation.Nullable;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableNativeArray;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.uimanager.PixelUtil;
import com.facebook.react.uimanager.ViewGroupManager;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.events.RCTEventEmitter;
import com.facebook.react.views.view.ReactViewGroup;
import com.google.android.gms.ads.AdListener;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.AdSize;
import com.google.android.gms.ads.AdView;
import com.google.android.gms.ads.LoadAdError;

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

class ReactAdView extends ReactViewGroup {

  protected AdView adView;

  String adUnitID;
  String[] testDevices;
  AdSize adSize;

  public ReactAdView(final Context context) {
    super(context);
    this.createAdView();
  }

  private void createAdView() {
    if (this.adView != null) this.adView.destroy();

    final Context context = getContext();
    this.adView = new AdView(context);
    this.adView.setAdListener(new AdListener() {
      @Override
      public void onAdLoaded() {
        int width = adView.getAdSize().getWidthInPixels(context);
        int height = adView.getAdSize().getHeightInPixels(context);
        int left = adView.getLeft();
        int top = adView.getTop();
        adView.measure(width, height);
        adView.layout(left, top, left + width, top + height);
        sendOnSizeChangeEvent();
        sendEvent(RNAdMobBannerViewManager.EVENT_AD_LOADED, null);
      }

      @Override
      public void onAdFailedToLoad(LoadAdError adError) {
        String errorMessage = "Unknown error";
        switch (adError.getCode()) {
          case AdRequest.ERROR_CODE_INTERNAL_ERROR:
            errorMessage = "Internal error, an invalid response was received from the ad server.";
            break;
          case AdRequest.ERROR_CODE_INVALID_REQUEST:
            errorMessage = "Invalid ad request, possibly an incorrect ad unit ID was given.";
            break;
          case AdRequest.ERROR_CODE_NETWORK_ERROR:
            errorMessage = "The ad request was unsuccessful due to network connectivity.";
            break;
          case AdRequest.ERROR_CODE_NO_FILL:
            errorMessage = "The ad request was successful, but no ad was returned due to lack of ad inventory.";
            break;
        }
        WritableMap event = Arguments.createMap();
        WritableMap error = Arguments.createMap();
        error.putString("message", errorMessage);
        event.putMap("error", error);
        sendEvent(RNAdMobBannerViewManager.EVENT_AD_FAILED_TO_LOAD, event);
      }

      @Override
      public void onAdOpened() {
        sendEvent(RNAdMobBannerViewManager.EVENT_AD_OPENED, null);
      }

      @Override
      public void onAdClosed() {
        sendEvent(RNAdMobBannerViewManager.EVENT_AD_CLOSED, null);
      }

      @Override
      public void onAdClicked() {
//            sendEvent(RNAdMobBannerViewManager.EVENT_AD_CLOSED, null);
      }

//            @Override
//            public void onAdLeftApplication() {
//                sendEvent(RNAdMobBannerViewManager.EVENT_AD_LEFT_APPLICATION, null);
//            }
    });
    this.addView(this.adView);
  }

  private void sendOnSizeChangeEvent() {
    int width;
    int height;
    ReactContext reactContext = (ReactContext) getContext();
    WritableMap event = Arguments.createMap();
    AdSize adSize = this.adView.getAdSize();
    if (this.adSize == AdSize.SMART_BANNER) {
      width = (int) PixelUtil.toDIPFromPixel(adSize.getWidthInPixels(reactContext));
      height = (int) PixelUtil.toDIPFromPixel(adSize.getHeightInPixels(reactContext));
    } else {
      width = adSize.getWidth();
      height = adSize.getHeight();
    }
    event.putDouble("width", width);
    event.putDouble("height", height);
    sendEvent(RNAdMobBannerViewManager.EVENT_SIZE_CHANGE, event);
  }

  private void sendEvent(String name, @Nullable WritableMap event) {
    ReactContext reactContext = (ReactContext) getContext();
    reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(
      getId(),
      name,
      event);
  }

  public void loadBanner() {
    AdRequest.Builder adRequestBuilder = new AdRequest.Builder();
    if (testDevices != null) {
      for (int i = 0; i < testDevices.length; i++) {
        String testDevice = testDevices[i];
        if (testDevice == "SIMULATOR") {
          testDevice = AdRequest.DEVICE_ID_EMULATOR;
        }
//                adRequestBuilder.addTestDevice(testDevice);
      }
    }
    AdRequest adRequest = adRequestBuilder.build();
    this.adView.loadAd(adRequest);
  }

  public void setAdUnitID(String adUnitID) {
    if (this.adUnitID != null) {
      // We can only set adUnitID once, so when it was previously set we have
      // to recreate the view
      this.createAdView();
    }
    this.adUnitID = adUnitID;
    this.adView.setAdUnitId(adUnitID);
  }

  public void setTestDevices(String[] testDevices) {
    this.testDevices = testDevices;
  }

  public void setAdSize(AdSize adSize) {
    this.adSize = adSize;
    this.adView.setAdSize(adSize);
  }
}

public class RNAdMobBannerViewManager extends ViewGroupManager<ReactAdView> {

  public static final String REACT_CLASS = "RNGADBannerView";

  public static final String PROP_AD_SIZE = "adSize";
  public static final String PROP_AD_UNIT_ID = "adUnitID";
  public static final String PROP_TEST_DEVICES = "testDevices";

  public static final String EVENT_SIZE_CHANGE = "onSizeChange";
  public static final String EVENT_AD_LOADED = "onAdLoaded";
  public static final String EVENT_AD_FAILED_TO_LOAD = "onAdFailedToLoad";
  public static final String EVENT_AD_OPENED = "onAdOpened";
  public static final String EVENT_AD_CLOSED = "onAdClosed";
  public static final String EVENT_AD_LEFT_APPLICATION = "onAdLeftApplication";

  public static final int COMMAND_LOAD_BANNER = 1;

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

  @Override
  protected ReactAdView createViewInstance(ThemedReactContext themedReactContext) {
    ReactAdView adView = new ReactAdView(themedReactContext);
    return adView;
  }

  @Override
  public void addView(ReactAdView parent, View child, int index) {
    throw new RuntimeException("RNAdMobBannerView cannot have subviews");
  }

  @Override
  @Nullable
  public Map<String, Object> getExportedCustomDirectEventTypeConstants() {
    MapBuilder.Builder<String, Object> builder = MapBuilder.builder();
    String[] events = {
      EVENT_SIZE_CHANGE,
      EVENT_AD_LOADED,
      EVENT_AD_FAILED_TO_LOAD,
      EVENT_AD_OPENED,
      EVENT_AD_CLOSED,
      EVENT_AD_LEFT_APPLICATION
    };
    for (int i = 0; i < events.length; i++) {
      builder.put(events[i], MapBuilder.of("registrationName", events[i]));
    }
    return builder.build();
  }

  @ReactProp(name = PROP_AD_SIZE)
  public void setPropAdSize(final ReactAdView view, final String sizeString) {
    AdSize adSize = getAdSizeFromString(sizeString);
    view.setAdSize(adSize);
  }

  @ReactProp(name = PROP_AD_UNIT_ID)
  public void setPropAdUnitID(final ReactAdView view, final String adUnitID) {
    view.setAdUnitID(adUnitID);
  }

  @ReactProp(name = PROP_TEST_DEVICES)
  public void setPropTestDevices(final ReactAdView view, final ReadableArray testDevices) {
    ReadableNativeArray nativeArray = (ReadableNativeArray) testDevices;
    ArrayList<Object> list = nativeArray.toArrayList();
    view.setTestDevices(list.toArray(new String[list.size()]));
  }

  private AdSize getAdSizeFromString(String adSize) {
    switch (adSize) {
      case "banner":
        return AdSize.BANNER;
      case "largeBanner":
        return AdSize.LARGE_BANNER;
      case "mediumRectangle":
        return AdSize.MEDIUM_RECTANGLE;
      case "fullBanner":
        return AdSize.FULL_BANNER;
      case "leaderBoard":
        return AdSize.LEADERBOARD;
      case "smartBannerPortrait":
        return AdSize.SMART_BANNER;
      case "smartBannerLandscape":
        return AdSize.SMART_BANNER;
      case "smartBanner":
        return AdSize.SMART_BANNER;
      default:
        return AdSize.BANNER;
    }
  }

  @Nullable
  @Override
  public Map<String, Integer> getCommandsMap() {
    return MapBuilder.of("loadBanner", COMMAND_LOAD_BANNER);
  }

  @Override
  public void receiveCommand(ReactAdView root, int commandId, @javax.annotation.Nullable ReadableArray args) {
    switch (commandId) {
      case COMMAND_LOAD_BANNER:
        root.loadBanner();
        break;
    }
  }
}
