package name.ratson.cordova.admob;

import android.os.Bundle;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.util.Log;

import com.google.ads.mediation.admob.AdMobAdapter;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;

import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CordovaWebView;
import org.apache.cordova.PluginResult;
import org.apache.cordova.PluginResult.Status;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Iterator;

import name.ratson.cordova.admob.banner.BannerExecutor;
import name.ratson.cordova.admob.interstitial.InterstitialExecutor;
import name.ratson.cordova.admob.rewardvideo.RewardVideoExecutor;

/**
 * This class represents the native implementation for the AdMob Cordova plugin.
 * This plugin can be used to request AdMob ads natively via the Google AdMob SDK.
 * The Google AdMob SDK is a dependency for this plugin.
 */
public class AdMob extends CordovaPlugin {
    /**
     * Common tag used for logging statements.
     */
    private static final String TAG = "AdMob";

    public final AdMobConfig config = new AdMobConfig();

    private BannerExecutor bannerExecutor = null;
    private InterstitialExecutor interstitialExecutor = null;
    private RewardVideoExecutor rewardVideoExecutor = null;

    private boolean isGpsAvailable = false;

    @Override
    public void initialize(CordovaInterface cordova, CordovaWebView webView) {
        super.initialize(cordova, webView);
        isGpsAvailable = (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(cordova.getActivity()) == ConnectionResult.SUCCESS);
        Log.w(TAG, String.format("isGooglePlayServicesAvailable: %s", isGpsAvailable ? "true" : "false"));
    }

    /**
     * This is the main method for the AdMob plugin.  All API calls go through here.
     * This method determines the action, and executes the appropriate call.
     *
     * @param action          The action that the plugin should execute.
     * @param inputs          The input parameters for the action.
     * @param callbackContext The callback context.
     * @return A PluginResult representing the result of the provided action.  A
     * status of INVALID_ACTION is returned if the action is not recognized.
     */
    @Override
    public boolean execute(String action, JSONArray inputs, CallbackContext callbackContext) throws JSONException {
        if (bannerExecutor == null) {
            bannerExecutor = new BannerExecutor(this);
        }
        if (interstitialExecutor == null) {
            interstitialExecutor = new InterstitialExecutor(this);
        }
        if (rewardVideoExecutor == null) {
            rewardVideoExecutor = new RewardVideoExecutor(this);
        }

        PluginResult result = null;

        if (Actions.SET_OPTIONS.equals(action)) {
            JSONObject options = inputs.optJSONObject(0);
            result = executeSetOptions(options, callbackContext);

        } else if (Actions.CREATE_BANNER.equals(action)) {
            JSONObject options = inputs.optJSONObject(0);
            result = bannerExecutor.prepareAd(options, callbackContext);

        } else if (Actions.DESTROY_BANNER.equals(action)) {
            result = bannerExecutor.removeAd(callbackContext);

        } else if (Actions.REQUEST_AD.equals(action)) {
            JSONObject options = inputs.optJSONObject(0);
            result = bannerExecutor.requestAd(options, callbackContext);

        } else if (Actions.SHOW_AD.equals(action)) {
            boolean show = inputs.optBoolean(0);
            result = bannerExecutor.showAd(show, callbackContext);

        } else if (Actions.PREPARE_INTERSTITIAL.equals(action)) {
            JSONObject options = inputs.optJSONObject(0);
            result = interstitialExecutor.prepareAd(options, callbackContext);

        } else if (Actions.CREATE_INTERSTITIAL.equals(action)) {
            JSONObject options = inputs.optJSONObject(0);
            result = interstitialExecutor.createAd(options, callbackContext);

        } else if (Actions.REQUEST_INTERSTITIAL.equals(action)) {
            JSONObject options = inputs.optJSONObject(0);
            result = interstitialExecutor.requestAd(options, callbackContext);

        } else if (Actions.SHOW_INTERSTITIAL.equals(action)) {
            boolean show = inputs.optBoolean(0);
            result = interstitialExecutor.showAd(show, callbackContext);

        } else if(Actions.IS_INTERSTITIAL_READY.equals(action)) {
            result = interstitialExecutor.isReady(callbackContext);

        } else if (Actions.CREATE_REWARD_VIDEO.equals(action)) {
            JSONObject options = inputs.optJSONObject(0);
            result = rewardVideoExecutor.prepareAd(options, callbackContext);

        } else if (Actions.SHOW_REWARD_VIDEO.equals(action)) {
            boolean show = inputs.optBoolean(0);
            result = rewardVideoExecutor.showAd(show, callbackContext);

        } else if(Actions.IS_REWARD_VIDEO_READY.equals(action)) {
            result = rewardVideoExecutor.isReady(callbackContext);

        } else {
            Log.d(TAG, String.format("Invalid action passed: %s", action));
            result = new PluginResult(Status.INVALID_ACTION);
        }

        if (result != null) {
            callbackContext.sendPluginResult(result);
        }

        return true;
    }

    private PluginResult executeSetOptions(JSONObject options, CallbackContext callbackContext) {
        Log.w(TAG, "executeSetOptions");

        config.setOptions(options);

        callbackContext.success();
        return null;
    }

    public AdRequest buildAdRequest() {
        AdRequest.Builder builder = new AdRequest.Builder();
        if (config.isTesting || isRunningInTestLab()) {
            builder = builder.addTestDevice(AdRequest.DEVICE_ID_EMULATOR).addTestDevice(getDeviceId());
        }

        if (config.testDeviceList != null) {
            Iterator<String> iterator = config.testDeviceList.iterator();
            while (iterator.hasNext()) {
                builder = builder.addTestDevice(iterator.next());
            }
        }

        Bundle bundle = new Bundle();
        bundle.putInt("cordova", 1);
        if (config.adExtras != null) {
            Iterator<String> it = config.adExtras.keys();
            while (it.hasNext()) {
                String key = it.next();
                try {
                    bundle.putString(key, config.adExtras.get(key).toString());
                } catch (JSONException exception) {
                    Log.w(TAG, String.format("Caught JSON Exception: %s", exception.getMessage()));
                }
            }
        }
        builder = builder.addNetworkExtrasBundle(AdMobAdapter.class, bundle);

        if (config.gender != null) {
            if ("male".compareToIgnoreCase(config.gender) != 0) {
                builder.setGender(AdRequest.GENDER_MALE);
            } else if ("female".compareToIgnoreCase(config.gender) != 0) {
                builder.setGender(AdRequest.GENDER_FEMALE);
            } else {
                builder.setGender(AdRequest.GENDER_UNKNOWN);
            }
        }
        if (config.location != null) {
            builder.setLocation(config.location);
        }
        if ("yes".equals(config.forFamily)) {
            builder.setIsDesignedForFamilies(true);
        } else if ("no".equals(config.forFamily)) {
            builder.setIsDesignedForFamilies(false);
        }
        if ("yes".equals(config.forChild)) {
            builder.tagForChildDirectedTreatment(true);
        } else if ("no".equals(config.forChild)) {
            builder.tagForChildDirectedTreatment(false);
        }
        if (config.contentURL != null) {
            builder.setContentUrl(config.contentURL);
        }

        return builder.build();
    }


    @Override
    public void onPause(boolean multitasking) {
        if (bannerExecutor != null) {
            bannerExecutor.pauseAd();
        }
        super.onPause(multitasking);
    }

    @Override
    public void onResume(boolean multitasking) {
        super.onResume(multitasking);
        isGpsAvailable = (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(cordova.getActivity()) == ConnectionResult.SUCCESS);
        if (bannerExecutor != null) {
            bannerExecutor.resumeAd();
        }
    }

    @Override
    public void onDestroy() {
        if (bannerExecutor != null) {
            bannerExecutor.destroy();
            bannerExecutor = null;
        }
        if (interstitialExecutor != null) {
            interstitialExecutor.destroy();
            interstitialExecutor = null;
        }
        if (rewardVideoExecutor != null) {
            rewardVideoExecutor.destroy();
            rewardVideoExecutor = null;
        }
        super.onDestroy();
    }

    @NonNull
    private String getDeviceId() {
        // This will request test ads on the emulator and deviceby passing this hashed device ID.
        String ANDROID_ID = Settings.Secure.getString(cordova.getActivity().getContentResolver(), Settings.Secure.ANDROID_ID);
        return md5(ANDROID_ID).toUpperCase();
    }

    private boolean isRunningInTestLab() {
        String testLabSetting = Settings.System.getString(cordova.getActivity().getContentResolver(), "firebase.test.lab");
        return "true".equals(testLabSetting);
    }

    private static String md5(final String s) {
        try {
            MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
            digest.update(s.getBytes());
            byte messageDigest[] = digest.digest();
            StringBuffer hexString = new StringBuffer();
            for (int i = 0; i < messageDigest.length; i++) {
                String h = Integer.toHexString(0xFF & messageDigest[i]);
                while (h.length() < 2)
                    h = "0" + h;
                hexString.append(h);
            }
            return hexString.toString();

        } catch (NoSuchAlgorithmException e) {
        }
        return "";
    }
}