package com.mobify.astro;

import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Parcelable;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.util.Log;
import android.view.Window;
import android.view.inputmethod.InputMethodManager;

import com.mobify.astro.messaging.EventMessage;
import com.mobify.astro.messaging.EventRegistrar;
import com.mobify.astro.messaging.MessageSender;
import com.mobify.astro.messaging.RpcMessageListener;
import com.mobify.astro.messaging.annotations.RpcMethod;
import com.mobify.astro.utilities.AstroWebUtilities;

import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.List;

public class AstroApplication extends RpcMessageListener {
    private static final String TAG = AstroApplication.class.getName();
    private static final String ADDRESS = "AstroApplication:0";
    private AstroActivity activity;
    private MessageSender messageSender;

    public AstroApplication(@NonNull AstroActivity activity, @NonNull EventRegistrar eventRegistrar,
                            @NonNull MessageSender messageSender) {
        super(eventRegistrar, messageSender);
        this.activity = activity;
        this.messageSender = messageSender;
    }

    class AstroApplicationException extends AstroException {
        AstroApplicationException(String message) {
            super(TAG + ": " + message);
        }
    }

    @Override
    public String getInstanceAddress() {
        return ADDRESS;
    }

    @RpcMethod(methodName = "restartAstro")
    public void restartAstro() {
        activity.recreate();
    }

    /**
     * Returns true if the plugins activity got launched with a savedInstanceState bundle
     * @return true if the plugins activity got launched with a savedInstanceState bundle
     */
    @RpcMethod(methodName = "isResuming")
    public boolean isResuming() {
        return activity.isResuming();
    }

    /**
     * Sets the main activity's view to the plugin's view
     */
    @RpcMethod(methodName = "setMainViewPlugin", parameterNames = {"address"})
    public void setMainViewPlugin(String address) {
        activity.setMainViewPlugin(address);
    }

    /**
     * Returns the Uri that the application was told to start with
     * @return Uri the application was started with, or null
     */
    @RpcMethod(methodName = "getStartUri")
    public String getStartUri() {
        return activity.deepLinkHandler.getStartUri();
    }

    protected List<Intent> getValidIntents(String urlStr) {
        Uri url = Uri.parse(urlStr);
        Intent intent = new Intent(android.content.Intent.ACTION_VIEW, url);

        PackageManager packageManager = activity.getPackageManager();
        List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
        List<Intent> validIntents = new ArrayList<Intent>();

        // Build a list of intents to pass to our custom chooser.
        for (ResolveInfo currentInfo : activities) {
            String packageName = currentInfo.activityInfo.packageName;

            // Don't add intents to our own application
            if (packageName.equals(activity.getPackageName())) {
                continue;
            }
            Intent viewIntent = new Intent(android.content.Intent.ACTION_VIEW, url);
            viewIntent.setPackage(packageName);
            validIntents.add(viewIntent);
        }
        return validIntents;
    }

    @RpcMethod(methodName = "getOSInformation")
    public JSONObject getOSInformation() {
        return AstroWebUtilities.getOSInfo();
    }

    @RpcMethod(methodName = "getAppInformation")
    public JSONObject getAppInformation() throws AstroApplicationException {
        JSONObject appInfo = new JSONObject();

        // Return a unique identifier for the device
        // http://android-developers.blogspot.ca/2011/03/identifying-app-installations.html
        String installationID = Settings.Secure.getString(activity.getContentResolver(), Settings.Secure.ANDROID_ID);
        if (installationID == null || installationID == "" || installationID == "Unknown") {
            throw new AstroApplicationException("installationID is null or Unknown");
        }

        String bundleID = activity.getPackageName();
        if (bundleID == null) {
            bundleID = "";
        }

        try {
            appInfo.put("installationID", installationID);
            appInfo.put("bundleID", bundleID);
        } catch (JSONException e) {
            Log.e(TAG, e.getMessage(), e);
        }

        return appInfo;
    }

    @RpcMethod(methodName = "openStore")
    public void openStore() {
        final String appPackageName = activity.getPackageName();
        try {
            activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + appPackageName)));
        } catch (android.content.ActivityNotFoundException anfe) {
            activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=" + appPackageName)));
        }
    }

    @RpcMethod(methodName = "openInBrowser", parameterNames = {"url"})
    public void openInBrowser(String url) {
        List<Intent> validIntents = getValidIntents(url);
        // We create a custom chooser here rather then using an implicit intent. If we used an
        // implicit intent, then there is a chance that we'd re-open the URL in the same app if the user
        // selected "Always open in".
        Intent chooserIntent = Intent.createChooser(validIntents.remove(0), "Open link with");
        chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS,
                validIntents.toArray(new Parcelable[validIntents.size()]));
        activity.startActivity(chooserIntent);
    }

    @RpcMethod(methodName = "openLocationSettings")
    public void openLocationSettings() {
        final int LOCATION_SETTINGS_REQUEST_CODE = 1;
        Intent callGPSSettingIntent = new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
        // Although we don't really need the result, opening the activity this way allows the user
        // to return to the app by hitting the back button.
        activity.startActivityForResult(callGPSSettingIntent, LOCATION_SETTINGS_REQUEST_CODE);
    }

    @RpcMethod(methodName = "setStatusBarLightText")
    public void setStatusBarLightText() {
        // This method is not relevant on android
    }

    @RpcMethod(methodName = "setStatusBarDarkText")
    public void setStatusBarDarkText() {
        // This method is not relevant on android
    }

    @RpcMethod(methodName = "setStatusBarColor", parameterNames = {"color"})
    public void setStatusBarColor(String color) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Window window = activity.getWindow();
            window.setStatusBarColor(Color.parseColor(color));
        }
    }

    @RpcMethod(methodName = "setBackgroundColor", parameterNames = {"color"})
    public void setBackgroundColor(String color) {
        activity.setBackgroundColor(Color.parseColor(color));
    }

    @RpcMethod(methodName = "closeApp")
    public void closeApp() {
        activity.moveTaskToBack(false);
    }

    @RpcMethod(methodName = "dismissLaunchImage")
    public void dismissLaunchImage() {
        activity.dismissLaunchImage();
    }

    public void deepLink(JSONObject params) {
        EventMessage message = new EventMessage("private:receivedDeepLink", params, this);
        messageSender.sendMessageToAddress(message, getEventsAddress());
    }

    public void backButtonPressed(JSONObject params) {
        EventMessage message = new EventMessage("backButtonPressed", params, this);
        messageSender.sendMessageToAddress(message, getEventsAddress());
    }

    public void togglePreview(JSONObject params) {
        EventMessage message = new EventMessage("previewToggled", params, this);
        messageSender.sendMessageToAddress(message, getEventsAddress());
    }

    public void appActivated() {
        EventMessage message = new EventMessage("appActivated", null, this);
        messageSender.sendMessageToAddress(message, getEventsAddress());
    }

    public void appDeactivated() {
        EventMessage message = new EventMessage("appDeactivated", null, this);
        messageSender.sendMessageToAddress(message, getEventsAddress());
    }

    public void hideKeyboard() {
        InputMethodManager inputManager = (InputMethodManager) activity.getSystemService(this.activity.INPUT_METHOD_SERVICE);
        inputManager.hideSoftInputFromWindow(this.activity.getWindow().getDecorView().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
    }

}
