package org.apache.cordova.sharingreceptor;

import java.lang.RuntimeException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.HashSet;
import java.util.Arrays;

import org.apache.cordova.CordovaActivity;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.Intent;
import android.os.Bundle;
import android.net.Uri;
import android.text.Html;

import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CordovaResourceApi;
import org.apache.cordova.PluginResult;

import android.util.Log;

/**
 *
 * @author lorber.sebastien@gmail.com
 */
public class SharingReceptor extends CordovaPlugin {

    // Constant that holds all the intent actions that we will handle.
    private static final Set<String> SEND_INTENTS = new HashSet<String>(Arrays.asList(
            Intent.ACTION_SEND,
            Intent.ACTION_SEND_MULTIPLE
    ));

    private static boolean isSendIntent(Intent intent) {
        return SEND_INTENTS.contains(intent.getAction());
    }

    private static JSONObject serializeIntent(Intent intent) throws JSONException {
        JSONObject extras = SharingReceptor.serializeBundle(intent.getExtras());
        JSONObject intentJson = new JSONObject();
        intentJson.put("action", intent.getAction());
        intentJson.put("extras", extras);
        JSONObject result = new JSONObject();
        result.put("platform", "android");
        result.put("intent", intentJson);
        return result;
    }

    // See http://stackoverflow.com/a/21859000/82609
    private static JSONObject serializeBundle(Bundle bundle) throws JSONException {
        JSONObject json = new JSONObject();
        if ( bundle != null ) {
            Set<String> keys = bundle.keySet();
            for (String key : keys) {
                try {
                    json.put(key, JSONObject.wrap(bundle.get(key)));
                } catch(JSONException e) {
                    throw new RuntimeException("Can't serialize bundle for key = " + key,e);
                }
            }
        }
        return json;
    }

    /////////////////////////////////////////////////////////////////////////////////////////:
    /////////////////////////////////////////////////////////////////////////////////////////:
    /////////////////////////////////////////////////////////////////////////////////////////:



    private CallbackContext listenerCallback = null;
    private String TAG = this.getClass().getSimpleName();


    @Override
    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
        try {
            if ( action.equals("listen") ) {
                installListener(callbackContext);
                return true;
            }
            else {
                callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.INVALID_ACTION,"action ["+action+"] does not exist"));
                return false;
            }
        }
        catch (Exception e) {
            Log.e(TAG,"Error while executing action ["+action+"]",e);
            callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR,e.getMessage()));
            return false;
        }
    }



    private void installListener(CallbackContext callbackContext) {
        if ( this.listenerCallback != null ) {
            throw new RuntimeException("You already set a listener: it does not make sense to reset it.");
        }
        this.listenerCallback = callbackContext;

        // handle the case where the app is started by a new intent:
        // we publish the current intent (if needed) directly after the callback registration
        this.maybePublishIntent(this.cordova.getActivity().getIntent());
    }

    // handle the case when the app is already in background, and is "awakened" by a new intent sent from another app
    @Override
    public void onNewIntent(Intent intent) {
        Log.i(TAG, "onNewIntent -> " + intent);
        this.maybePublishIntent(intent);

        // TODO would it be useful to replace current activity intent by new intent? Can it messes things up?
    }




    // We try to publish in the JS callback the intent data, if the intent is a send intent, and if the callback was correctly setup
    private void maybePublishIntent(Intent intent) {
        if ( !SharingReceptor.isSendIntent(intent) ) {
            Log.i(TAG, "maybePublishIntent -> not publishing intent because the action name is not part of SEND_INTENTS=" + SEND_INTENTS);
        }
        else if ( this.listenerCallback == null ) {
            Log.w(TAG, "maybePublishIntent -> not publishing intent because listener callback not set");
        }
        else {

            JSONObject intentJson;
            try {
                intentJson = SharingReceptor.serializeIntent(intent);
            } catch (Exception e) {
                throw new RuntimeException("Can't serialize intent " + intent,e);
            }
            Log.i(TAG, "maybePublishIntent -> will publish intent -> " + intentJson.toString());
            PluginResult result = new PluginResult(PluginResult.Status.OK, intentJson);
            result.setKeepCallback(true);
            this.listenerCallback.sendPluginResult(result);
        }
    }


}

@@ -729,24 +729,102 @@
 vaPlugin %7B%0A%0A
+    private static final String TAG = SharingReceptor.class.getSimpleName();%0A%0A
     // Const
@@ -1437,24 +1437,217 @@
 tAction());%0A
+        intentJson.put(%22data%22, SharingReceptor.safeJSONWrap(intent.getData()) );%0A        intentJson.put(%22dataString%22, intent.getDataString());%0A        intentJson.put(%22type%22, intent.getType());%0A
         inte
@@ -2132,24 +2132,72 @@
 y : keys) %7B%0A
+                Object value = bundle.get(key);%0A
             
@@ -2244,39 +2244,42 @@
 ey, 
-JSONObject.wrap(bundle.get(key)
+SharingReceptor.safeJSONWrap(value
 ));%0A
@@ -2302,20 +2302,16 @@
 %7D catch(
-JSON
 Exceptio
@@ -2309,32 +2309,95 @@
 (Exception e) %7B%0A
+                    Log.e(TAG,%22Can't serialize key %22 + key,e);%0A
                 
@@ -2527,19 +2527,544 @@
     
-return json
+Log.i(TAG, %22json -%3E %22 + json.toString());%0A        return json;%0A    %7D%0A%0A    // Because JSONObject.wrap(value) returns null on failure (this is the case for HierarchicalUri instances for example)%0A    private static Object safeJSONWrap(Object value) throws JSONException %7B%0A        Object wrappedValue = JSONObject.wrap(value);%0A        boolean wrappingHasFailed = (value != null && wrappedValue == null);%0A        if ( wrappingHasFailed ) %7B%0A            wrappedValue = value.toString(); // Fallback method%0A        %7D%0A        return wrappedValue
 ;%0A  
@@ -3355,17 +3355,16 @@
 ////:%0A%0A%0A
-%0A
     priv
@@ -3412,66 +3412,8 @@
 ll;%0A
-    private String TAG = this.getClass().getSimpleName();%0A
 %0A%0A  

Fix strange serialization problem for uris in extra stream