package com.ease.gsms.android.plugins;

import android.annotation.TargetApi;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.telephony.SmsManager;
import org.ajwcc.pduUtils.gsm3040.Pdu;
import org.ajwcc.pduUtils.gsm3040.PduParser;
import org.ajwcc.pduUtils.gsm3040.PduUtils;
import org.ajwcc.pduUtils.gsm3040.SmsStatusReportPdu;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.UUID;

@TargetApi(22)
public class Sms extends CordovaPlugin {

	public final String ACTION_SEND_SMS = "send";

	public final String ACTION_HAS_PERMISSION = "has_permission";
	
	public final String ACTION_REQUEST_PERMISSION = "request_permission";

	private static final String INTENT_FILTER_SMS_SENT = "SMS_SENT";

	private static final String INTENT_FILTER_SMS_DELIVERED = "SMS_DELIVERED";

	private static final int SEND_SMS_REQ_CODE = 0;

	private static final int REQUEST_PERMISSION_REQ_CODE = 1;

	private CallbackContext callbackContext;

	private JSONArray args;

	@Override
	public boolean execute(
			String action,
			final JSONArray args,
			final CallbackContext callbackContext
	) throws JSONException {
		this.callbackContext = callbackContext;
		this.args = args;
		if (action.equals(ACTION_SEND_SMS)) {
			if (hasPermission()) {
				sendSMS();
			} else {
				requestPermission(SEND_SMS_REQ_CODE);
			}
			return true;
		}
		else if (action.equals(ACTION_HAS_PERMISSION)) {
			callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, hasPermission()));
			return true;
		}
		else if (action.equals(ACTION_REQUEST_PERMISSION)) {
			requestPermission(REQUEST_PERMISSION_REQ_CODE);
			return true;
		}
		return false;
	}

	private boolean hasPermission() {
		return cordova.hasPermission(android.Manifest.permission.SEND_SMS);
	}

	private void requestPermission(int requestCode) {
		cordova.requestPermission(this, requestCode, android.Manifest.permission.SEND_SMS);
	}

	public void onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) throws JSONException {
		for (int r : grantResults) {
			if (r == PackageManager.PERMISSION_DENIED) {
				callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, "User has denied permission"));
				return;
			}
		}
		if (requestCode == SEND_SMS_REQ_CODE) {
			sendSMS();
			return;
		}
		callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, true));
	}

	private boolean sendSMS() {
		cordova.getThreadPool().execute(new Runnable() {
			@Override
			public void run() {
				try {
					String phoneNumber = args.getString(0);
					int subscriptionId = args.getInt(1);
					String message = args.getString(2);
					Boolean requestDeliveryReport = args.getBoolean(3);
					if (!checkSupport()) {
						callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, "SMS not supported on this platform"));
						return;
					}
					send(callbackContext, subscriptionId, phoneNumber, message, requestDeliveryReport);
				} catch (JSONException ex) {
					callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
				}
			}
		});
		return true;
	}

	private boolean checkSupport() {
		Activity ctx = this.cordova.getActivity();
		return ctx.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
	}

	private void send(final CallbackContext callbackContext, int subscriptionId, String phoneNumber, String message, boolean requestDeliveryReport) {

		SmsManager manager = SmsManager.getSmsManagerForSubscriptionId(subscriptionId);

		final ArrayList<String> parts = manager.divideMessage(message);

		final UUID messageUUID = UUID.randomUUID();

		final int partCount = parts.size();

		final BroadcastReceiver sendingBroadcastReceiver = new PartCountingBroadcastReceiver(cordova, partCount, messageUUID) {
			@Override
			protected int actOnResultCode(Intent intent) {
				return getResultCode();
			}

			@Override
			void onComplete() {
				callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, getResultCodeJson()));
			}
		};

		final BroadcastReceiver deliveryBroadcastReceiver =
			requestDeliveryReport
			?
			new PartCountingBroadcastReceiver(cordova, partCount, messageUUID) {
				@Override
				protected int actOnResultCode(Intent intent) {
					int resultCode = getResultCode();
					if (resultCode == -1) { // vrosca: we might actually not get the proper status from the PDU, try to go deeper
						try {
							Pdu pdu = new PduParser().parsePdu(PduUtils.bytesToPdu(intent.getExtras().getByteArray("pdu")));
							if (pdu instanceof SmsStatusReportPdu) {
								int status = ((SmsStatusReportPdu) pdu).getStatus();
								if (status != 0) {
									resultCode = status - 1; // offset by 1 to match convention that -1 is OK
									unregisterAndComplete();
								}
							}
						} catch (Exception ignored) {
						}

					}
					return resultCode;
				}

				@Override
				void onComplete() {
					cordova.getActivity().runOnUiThread(new Runnable() {
						@Override
						public void run() {
							webView.loadUrl("javascript:sms.handlers[\"" + messageUUID + "\"].delivered(JSON.parse(" + JSONObject.quote(getResultCodeJson().toString()) + "));");
						}
					});
				}
			}
			: null;

		String intentFilterActionSent = INTENT_FILTER_SMS_SENT + messageUUID;

		String intentFilterActionDelivered = INTENT_FILTER_SMS_DELIVERED + messageUUID;

		this.cordova.getActivity().registerReceiver(sendingBroadcastReceiver, new IntentFilter(intentFilterActionSent));

		if (requestDeliveryReport) {
			this.cordova.getActivity().registerReceiver(deliveryBroadcastReceiver, new IntentFilter(intentFilterActionDelivered));
		}

		PendingIntent sentIntent = PendingIntent.getBroadcast(this.cordova.getActivity(), 0, new Intent(intentFilterActionSent), 0);

		PendingIntent deliveredIntent = requestDeliveryReport
			? PendingIntent.getBroadcast(this.cordova.getActivity(), 0, new Intent(intentFilterActionDelivered), 0)
			: null;

		// depending on the number of parts we send a text message or multi parts
		if (partCount > 1) {
			ArrayList<PendingIntent> sentIntents = new ArrayList<>();
			ArrayList<PendingIntent> deliveredIntents = requestDeliveryReport ? new ArrayList<PendingIntent>() : null;
			for (int i = 0; i < partCount; i++) {
				sentIntents.add(sentIntent);
				if (requestDeliveryReport) {
					deliveredIntents.add(deliveredIntent);
				}
			}
			manager.sendMultipartTextMessage(phoneNumber, null, parts, sentIntents, deliveredIntents);
		}
		else {
			manager.sendTextMessage(phoneNumber, null, message, sentIntent, deliveredIntent);
		}
	}

}
