package com.jens.automation2;

import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.os.BatteryManager;
import android.os.Build;
import android.service.notification.StatusBarNotification;
import android.telephony.TelephonyManager;
import android.util.Log;

import androidx.annotation.RequiresApi;

import com.jens.automation2.location.LocationProvider;
import com.jens.automation2.location.WifiBroadcastReceiver;
import com.jens.automation2.receivers.BatteryReceiver;
import com.jens.automation2.receivers.BluetoothReceiver;
import com.jens.automation2.receivers.BroadcastListener;
import com.jens.automation2.receivers.CalendarReceiver;
import com.jens.automation2.receivers.ConnectivityReceiver;
import com.jens.automation2.receivers.DateTimeListener;
import com.jens.automation2.receivers.DeviceOrientationListener;
import com.jens.automation2.receivers.HeadphoneJackListener;
import com.jens.automation2.receivers.MediaPlayerListener;
import com.jens.automation2.receivers.NfcReceiver;
import com.jens.automation2.receivers.NoiseListener;
import com.jens.automation2.receivers.NotificationListener;
import com.jens.automation2.receivers.PhoneStatusListener;
import com.jens.automation2.receivers.ProcessListener;
import com.jens.automation2.receivers.ProximityReceiver;
import com.jens.automation2.receivers.ScreenStateReceiver;
import com.jens.automation2.receivers.TetheringReceiver;

import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;

public class Trigger
{
	public enum Trigger_Enum
	{
		pointOfInterest,
		timeFrame,
		charging,
		batteryLevel,
		usb_host_connection,
		speed,
		noiseLevel,
		wifiConnection,
		process_started_stopped,
		airplaneMode,
		roaming,
		nfcTag,
		activityDetection,
		bluetoothConnection,
		headsetPlugged,
		notification,
		deviceOrientation,
		profileActive,
		screenState,
		musicPlaying,
		deviceStarts,
		serviceStarts,
		broadcastReceived,
		tethering,
		subSystemState,
		checkVariable,
        proximity,
		calendarEvent,
		phoneCall; //phoneCall always needs to be at the very end because of Google's shitty so called privacy

		public String getFullName(Context context)
		{
			switch(this)
			{
				case pointOfInterest:
					return context.getResources().getString(R.string.triggerPointOfInterest);
				case timeFrame:
					return context.getResources().getString(R.string.triggerTimeFrame);
				case charging:
					return context.getResources().getString(R.string.triggerCharging);
				case batteryLevel:
					return context.getResources().getString(R.string.batteryLevel);
				case usb_host_connection:
					return context.getResources().getString(R.string.triggerUsb_host_connection);
				case speed:
					return context.getResources().getString(R.string.triggerSpeed);
				case noiseLevel:
					return context.getResources().getString(R.string.triggerNoiseLevel);
				case wifiConnection:
					return context.getResources().getString(R.string.wifiConnection);
				case process_started_stopped:
					return context.getResources().getString(R.string.anotherAppIsRunning);
				case airplaneMode:
					return context.getResources().getString(R.string.airplaneMode);
				case roaming:
					return context.getResources().getString(R.string.roaming);
				case phoneCall:
					return context.getResources().getString(R.string.phoneCall);
				case nfcTag:
					return context.getResources().getString(R.string.nfcTag);
				case activityDetection:
					return context.getResources().getString(R.string.activityDetection);
				case bluetoothConnection:
					return context.getResources().getString(R.string.bluetoothConnection);
				case headsetPlugged:
					return context.getResources().getString(R.string.triggerHeadsetPlugged);
				case notification:
					return context.getResources().getString(R.string.notification);
				case deviceOrientation:
					return context.getResources().getString(R.string.deviceOrientation);
				case profileActive:
					return context.getResources().getString(R.string.profile);
				case musicPlaying:
					return context.getResources().getString(R.string.musicPlaying);
				case screenState:
					return context.getResources().getString(R.string.screenState);
				case deviceStarts:
					return context.getResources().getString(R.string.deviceStarts);
				case serviceStarts:
					return context.getResources().getString(R.string.serviceStarts);
				case broadcastReceived:
					return context.getResources().getString(R.string.broadcastReceivedTitle);
				case tethering:
					return context.getResources().getString(R.string.tetheringState);
				case subSystemState:
					return context.getResources().getString(R.string.subSystemState);
				case checkVariable:
					return context.getResources().getString(R.string.checkVariable);
                case proximity:
                    return context.getResources().getString(R.string.proximity);
				case calendarEvent:
					return context.getResources().getString(R.string.calendarEventCapital);
				default:
					return "Unknown";
			}
		}
	};

	boolean triggerParameter; //if true->started event, if false->stopped
	String triggerParameter2;

	public static final String triggerParameter2Split = "tp2split";

	Trigger_Enum triggerType = null;
	PointOfInterest pointOfInterest = null;
	TimeFrame timeFrame;

	public static String triggerPhoneCallStateRinging = "ringing";
	public static String triggerPhoneCallStateStarted = "started";
	public static String triggerPhoneCallStateStopped = "stopped";
	public static String triggerPhoneCallDirectionIncoming = "incoming";
	public static String triggerPhoneCallDirectionOutgoing = "outgoing";
	public static String triggerPhoneCallDirectionAny = "any";
	public static String triggerPhoneCallNumberAny = "any";

	double speed; //km/h
	long noiseLevelDb;
	String processName = null;
	int batteryLevel;
	int phoneDirection = 0; // 0=any, 1=incoming, 2=outgoing
	String phoneNumber = null;
	String nfcTagId = null;
	String bluetoothEvent = null;
	String bluetoothDeviceAddress = null;
	int activityDetectionType = -1;
	int headphoneType = -1;

	public static enum subSystemStates { wifi, bluetooth };

	Rule parentRule = null;
	Calendar lastTimeNotApplied = null;

	final static String anyAppString = "-1";

	public boolean applies(Object triggeringObject, Context context)
    {
		boolean result = true;

		try
		{
			switch(this.getTriggerType())
			{
				case timeFrame:
					if(!checkDateTime(triggeringObject, false))
						result = false;
					break;
				case pointOfInterest:
					if(!checkLocation())
						result = false;
					break;
				case charging:
					if(!checkCharging())
						result = false;
					break;
				case usb_host_connection:
					if(!checkUsbHostConnection())
						result = false;
					break;
				case batteryLevel:
					if(!checkBatteryLevel())
						result = false;
					break;
				case speed:
					if(!checkSpeed())
						result = false;
					break;
				case noiseLevel:
					if(!checkNoiseLevel())
						result = false;
					break;
				case wifiConnection:
					if(!checkWifiConnection())
						result = false;
					break;
				case process_started_stopped:
					if(!checkProcess())
						result = false;
					break;
				case airplaneMode:
					if(!checkAirplaneMode())
						result = false;
					break;
				case roaming:
					if(!checkRoaming())
						result = false;
					break;
				case phoneCall:
					if(!checkPhoneCall())
						result = false;
					break;
				case nfcTag:
					if(!checkNfc())
						result = false;
					break;
				case bluetoothConnection:
					if(!checkBluetooth())
						result = false;
					break;
				case headsetPlugged:
					if(!checkHeadsetPlugged())
						result = false;
					break;
				case notification:
					if(!checkNotification())
						result = false;
					break;
				case deviceOrientation:
					if(!checkDeviceOrientation())
						result = false;
					break;
				case activityDetection:
					if(!getParentRule().checkActivityDetection(this))
						result = false;
					break;
				case profileActive:
					if(!checkProfileActive())
						result = false;
					break;
				case musicPlaying:
					if(!checkMusicPlaying())
						result = false;
					break;
				case screenState:
					if(!checkScreenState())
						result = false;
					break;
				case deviceStarts:
					if(!checkDeviceStarts())
						result = false;
					break;
				case serviceStarts:
					if(!checkServiceStarts())
						result = false;
					break;
				case broadcastReceived:
					if(!checkBroadcastReceived())
						result = false;
					break;
				case tethering:
					if(!checkTetheringActive())
						result = false;
					break;
				case subSystemState:
					if(!checkSubSystemState())
						result = false;
				case checkVariable:
					if(!checkVariable())
						result = false;
					break;
				case calendarEvent:
					if(!checkCalendarEvent(false))
						result = false;
					break;
                case proximity:
                    if(!checkProximity())
                        result = false;
                    break;
				default:
					break;
			}
		}
		catch(Exception e)
		{
			Miscellaneous.logEvent("e", "Trigger", "Error while checking if rule " + getParentRule().getName() + " applies." + Miscellaneous.lineSeparator + Log.getStackTraceString(e), 1);
			result = false;
		}

		if(!result)
			lastTimeNotApplied = Calendar.getInstance();

		return result;
    }

	boolean checkBroadcastReceived()
	{
		/*
			We cannot reasonably check the current state for every broadcast event.
			We can only hope that when starting the receiver we get an initial broadcast
			for every current state. That collection of states will be saved and checked if
			it contains the specific event of this trigger.
		 */

		return triggerParameter == BroadcastListener.getInstance().hasBroadcastOccurred(triggerParameter2);
	}

	boolean checkNotification()
	{
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
		{
			String[] params = this.getTriggerParameter2().split(triggerParameter2Split, -1);

			String myApp = params[0];
			String myTitleDir = params[1];
			String requiredTitle = params[2];
			String myTextDir = params[3];
//			String requiredText;
//			if (params.length >= 5)
			String requiredText = params[4];
//			else
//				requiredText = "";

			if(this.getTriggerParameter())
			{
				// Check an active notification that is still there

				boolean foundMatch = false;

				for (StatusBarNotification sbn : NotificationListener.getInstance().getActiveNotifications())
				{
					if(getParentRule().getLastExecution() == null || sbn.getPostTime() > this.getParentRule().getLastExecution().getTimeInMillis())
					{
						NotificationListener.SimpleNotification sn = NotificationListener.convertNotificationToSimpleNotification(true, sbn);

						Miscellaneous.logEvent("i", "NotificationCheck", "Checking if this notification matches our rule " + this.getParentRule().getName() + ": " + sn.toString(), 5);

						if (!myApp.equals(anyAppString))
						{
							if (!myApp.equalsIgnoreCase(sn.getApp()))
							{
								Miscellaneous.logEvent("i", "NotificationCheck", "Notification app name does not match rule.", 5);
								continue;
							}
						}
						else
						{
						/*
						 	Notifications from Automation are disregarded to avoid infinite loops.
						 */
							if(myApp.equals(BuildConfig.APPLICATION_ID))
							{
								return false;
							}
						}

					/*
						If there are multiple notifications ("stacked") title or text might be null:
						https://stackoverflow.com/questions/28047767/notificationlistenerservice-not-reading-text-of-stacked-notifications
					 */

						// T I T L E
						if (!StringUtils.isEmpty(requiredTitle))
						{
							if (!Miscellaneous.compare(myTitleDir, requiredTitle, sn.getTitle()))
							{
								Miscellaneous.logEvent("i", "NotificationCheck", "Notification title does not match rule.", 5);
								continue;
							}
						}
						else
							Miscellaneous.logEvent("i", "NotificationCheck", "A required title for a notification trigger was not specified.", 5);

						// T E X T
						if (!StringUtils.isEmpty(requiredText))
						{
							if (!Miscellaneous.compare(myTextDir, requiredText, sn.getText()))
							{
								Miscellaneous.logEvent("i", "NotificationCheck", "Notification text does not match rule.", 5);
								continue;
							}
						}
						else
							Miscellaneous.logEvent("i", "NotificationCheck", "A required text for a notification trigger was not specified.", 5);

						/*
							We can only get here through the startup routine of the main service.
							Because the notification did not come in at runtime, but was there
							before we started, w need to take a record of it.
						 */
						if(NotificationListener.getLastNotification() == null)
							NotificationListener.setLastNotification(sn);

						foundMatch = true;
						break;
					}
				}

				if(!foundMatch)
					return false;
			}
			else
			{
				// check a notification that is gone

				if(NotificationListener.getLastNotification() != null)
				{
					if(!NotificationListener.getLastNotification().isCreated())
					{
						String app = NotificationListener.getLastNotification().getApp();
						String title = NotificationListener.getLastNotification().getTitle();
						String text = NotificationListener.getLastNotification().getText();

						if (!myApp.equals(anyAppString))
						{
							if (!app.equalsIgnoreCase(myApp))
								return false;
						}
						else
						{
							if(myApp.equals(BuildConfig.APPLICATION_ID))
							{
								return false;
							}
						}

						if (requiredTitle.length() > 0)
						{
							if (!Miscellaneous.compare(myTitleDir, title, requiredTitle))
								return false;
						}

						if (requiredText.length() > 0)
						{
							if (!Miscellaneous.compare(myTextDir, text, requiredText))
								return false;
						}
					}
					else
						return false;
				}
			}
		}

		return true;
	}

	boolean checkMusicPlaying()
	{
		return triggerParameter == MediaPlayerListener.isAudioPlaying(Miscellaneous.getAnyContext());
	}

	boolean checkDeviceStarts()
	{
		if(triggerParameter && checkServiceStarts() && !Settings.deviceStartDone)
			return true;
		else if(!triggerParameter && !checkServiceStarts() && Settings.deviceStartDone)
			return true;
		else
			return false;
	}

	boolean checkServiceStarts()
	{
		return !Settings.serviceStartDone == getTriggerParameter();
	}

	boolean checkProfileActive()
	{
		String demandedProfileName = getTriggerParameter2().split(Trigger.triggerParameter2Split)[0];
		boolean checkSettings = Boolean.parseBoolean(getTriggerParameter2().split(Trigger.triggerParameter2Split)[1]);

		if(checkSettings)
		{
			Profile profile = Profile.getByName(demandedProfileName);
			return profile.areMySettingsCurrentlyActive(Miscellaneous.getAnyContext());
		}
		else
		{
			try
			{
				Profile lastProfile = null;

				if (Profile.profileActivationHistory.size() > 0)
				{
					lastProfile = Profile.profileActivationHistory.get(Profile.profileActivationHistory.size() - 1);

					if (getTriggerParameter())
						return demandedProfileName.equals(lastProfile.getName());
					else
						return !demandedProfileName.equals(lastProfile.getName());
				}
				else
					return !getTriggerParameter();
			}
			catch (Exception e)
			{
				Miscellaneous.logEvent("w", "Trigger", "Error checking profile trigger.", 4);
			}
		}

		return false;
	}

	boolean checkScreenState()
	{
		try
		{
			int desiredState = Integer.parseInt(getTriggerParameter2());

			if(desiredState == ScreenStateReceiver.SCREEN_STATE_OFF || desiredState == ScreenStateReceiver.SCREEN_STATE_ON)
				return desiredState == ScreenStateReceiver.getScreenPowerState();
			else
				return desiredState == ScreenStateReceiver.getScreenLockState();
		}
		catch (Exception e)
		{
			Miscellaneous.logEvent("w", "Trigger", "Error checking screen state trigger.", 4);
		}

		return false;
	}

	boolean checkDeviceOrientation()
	{
		String deviceOrientationPieces[] = getTriggerParameter2().split(Trigger.triggerParameter2Split);
		float desiredAzimuth = Float.parseFloat(deviceOrientationPieces[0]);
		float desiredAzimuthTolerance = Float.parseFloat(deviceOrientationPieces[1]);
		float desiredPitch = Float.parseFloat(deviceOrientationPieces[2]);
		float desiredPitchTolerance = Float.parseFloat(deviceOrientationPieces[3]);
		float desiredRoll = Float.parseFloat(deviceOrientationPieces[4]);
		float desiredRollTolerance = Float.parseFloat(deviceOrientationPieces[5]);
		float currentAzimuth = DeviceOrientationListener.getInstance().getAzimuth();
		float currentPitch = DeviceOrientationListener.getInstance().getPitch();
		float currentRoll = DeviceOrientationListener.getInstance().getRoll();

		if(desiredAzimuthTolerance < 180)
		{
			if (!(desiredAzimuth - desiredAzimuthTolerance <= currentAzimuth && currentAzimuth <= desiredAzimuth + desiredAzimuthTolerance))
			{
				Miscellaneous.logEvent("i", "DeviceOrientation", "Azimuth outside of tolerance area.", 5);
				if (getTriggerParameter())
					return false;
				else
					return true;
			}
		}

		if(desiredPitchTolerance < 180)
		{
			if (!(desiredPitch - desiredPitchTolerance <= currentPitch && currentPitch <= desiredPitch + desiredPitchTolerance))
			{
				Miscellaneous.logEvent("i", "DeviceOrientation", "Pitch outside of tolerance area.", 5);
				if (getTriggerParameter())
					return false;
				else
					return true;
			}
		}

		if(desiredRollTolerance < 180)
		{
			if (!(desiredRoll - desiredRollTolerance <= currentRoll && currentRoll <= desiredRoll + desiredRollTolerance))
			{
				Miscellaneous.logEvent("i", "DeviceOrientation", "Roll outside of tolerance area.", 5);
				if (getTriggerParameter())
					return false;
				else
					return true;
			}
		}

		if(getTriggerParameter())
			return true;
		else
			return false;
	}

    boolean checkHeadsetPlugged()
	{
		/*
			Wenn verbunden:
			- either -> true
			- headphone -> true
			- mikro -> wenn tatsächlich mikro

			Wenn nicht verbunden:
			- either -> true
			- headphone -> true
			- mikro -> true
		 */

		if(HeadphoneJackListener.isHeadsetConnected())	// connection detected
		{
			if(this.getTriggerParameter())				// connection demanded
			{
				if (this.getHeadphoneType() == 1)
				{
					if (this.getHeadphoneType() == HeadphoneJackListener.getHeadphoneType())    // Microphone demanded
						return true;
					else
						Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleDoesntApplyWrongHeadphoneType), this.getParentRule().getName()), 3);
				}
				else
					return true;
			}
		}
		else	// disconnection detected
		{
			// Type doesn't matter here

			if(!this.getTriggerParameter())		// disconnection demanded
				return true;
		}

		return false;
	}

	boolean checkSubSystemState()
	{
		try
		{
			subSystemStates state = subSystemStates.valueOf(triggerParameter2);

			switch (state)
			{
				case wifi:
					return WifiBroadcastReceiver.isWifiEnabled(Miscellaneous.getAnyContext()) == triggerParameter;
				case bluetooth:
					return BluetoothReceiver.isBluetoothEnabled() == triggerParameter;
				default:
					Miscellaneous.logEvent("w", "checkSubSystemState()", "Invalid subSystemState: " + state.name(), 3);
					return false;
			}
		}
		catch(Exception e)
		{
			Miscellaneous.logEvent("e", "checkSubSystemState()", Log.getStackTraceString(e), 1);
		}

		return false;
	}

	boolean checkVariable()
	{
		try
		{
			Map<String,String> map = AutomationService.getInstance().getVariableMap();

			String[] conditions = this.getTriggerParameter2().split(Trigger.triggerParameter2Split);

			if(conditions.length == 1) // no real condition
				return true;
			else
			{
				if (map.containsKey(conditions[0]))
				{
					if(Miscellaneous.isRegularExpression(conditions[1]))
					{
						return (map.get(conditions[0]).matches(conditions[1]) == this.getTriggerParameter());
					}
					else
					{
						return (map.get(conditions[0]).equals(conditions[1]) == this.getTriggerParameter());
					}
				}
			}
		}
		catch(Exception e)
		{
			Miscellaneous.logEvent("e", "checkVariable()", Log.getStackTraceString(e), 1);
		}

		return false;
	}

	public boolean checkCalendarEvent(boolean ignoreActive)
	{
		try
		{
			List<CalendarReceiver.CalendarEvent> calendarEvents = CalendarReceiver.readCalendarEvents(AutomationService.getInstance(), true,false);

			for(CalendarReceiver.CalendarEvent event : calendarEvents)
			{
				if(!checkCalendarEvent(event, ignoreActive))
					continue;

				return true;
			}

			// At this point none of the calendar items match this trigger

			/*
				If trigger demands no calendar event and there is absolutely no future event,
				further criteria don't matter if there are no events to check.
			*/
			if(calendarEvents.size() == 0 && getTriggerParameter() == false)
				return true;
		}
		catch(Exception e)
		{
			Miscellaneous.logEvent("e", "checkCalendarEvent()", Log.getStackTraceString(e), 1);
		}

		return false;
	}

	public boolean checkCalendarEvent(CalendarReceiver.CalendarEvent event, boolean ignoreActive)
	{
		String[] conditions = this.getTriggerParameter2().split(Trigger.triggerParameter2Split);
//		List<CalendarReceiver.CalendarEvent> calendarEvents = CalendarReceiver.readCalendarEvents(AutomationService.getInstance(), true,false);

		/*
			0 = titleDir
			1 = title
			2 = descriptionDir
			3 = description
			4 = locationDir
			5 = location
			6 = evaluate all day event
			7 = all day event
			8 = evaluate reoccurring
			9 = reoccurring
			10 = availability list
			11 = calendars list
		 */

		boolean shouldBeActive = getTriggerParameter();
		if (!ignoreActive && shouldBeActive != event.isCurrentlyActive())
		{
			Miscellaneous.logEvent("i", "CalendarCheck", "Event " + event.title + " has to be currently active: " + String.valueOf(triggerParameter) + ", but is required otherwise.", 5);
			return false;
		}

		if (!StringUtils.isEmpty(conditions[1]))
		{
			if (!Miscellaneous.compare(conditions[0], conditions[1], event.title))
			{
				Miscellaneous.logEvent("i", "CalendarCheck", "Title of event " + event.title + " does not match.", 5);
				return false;
			}
		}

		if (!StringUtils.isEmpty(conditions[3]))
		{
			if (!Miscellaneous.compare(conditions[2], conditions[3], event.description))
			{
				Miscellaneous.logEvent("i", "CalendarCheck", "Description " + event.title + " does not match.", 5);
				return false;
			}
		}

		if (!StringUtils.isEmpty(conditions[5]))
		{
			if (!Miscellaneous.compare(conditions[4], conditions[5], event.location))
			{
				Miscellaneous.logEvent("i", "CalendarCheck", "Location " + event.title + " does not match.", 5);
				return false;
			}
		}

		if (Boolean.parseBoolean(conditions[6]))
		{
			if (Boolean.parseBoolean(conditions[7]) != event.allDay)
			{
				Miscellaneous.logEvent("i", "CalendarCheck", "All day setting " + event.title + " does not match.", 5);
				return false;
			}
		}

		if (Boolean.parseBoolean(conditions[8]))
		{
			if (Boolean.parseBoolean(conditions[9]) != event.reoccurring)
			{
				Miscellaneous.logEvent("i", "CalendarCheck", "Reoccurring setting " + event.title + " does not match.", 5);
				return false;
			}
		}

		if (!StringUtils.isEmpty(conditions[10]))
		{
			String[] availabilities = conditions[10].split(ActivityManageTriggerCalendar.separator);
			if (availabilities.length > 0)
			{
				if (!Miscellaneous.arraySearch(availabilities, event.availability, false, true))
				{
					Miscellaneous.logEvent("i", "CalendarCheck", "Availability of event " + event.title + " does not match.", 5);
					return false;
				}
			}
		}

		if (!StringUtils.isEmpty(conditions[11]))
		{
			String[] calendars = conditions[11].split(ActivityManageTriggerCalendar.separator);
			if (calendars.length > 0)
			{
				if (!Miscellaneous.arraySearch(calendars, String.valueOf(event.calendarId), false, true))
				{
					Miscellaneous.logEvent("i", "CalendarCheck", "Calendar of event " + event.title + " does not match.", 5);
					return false;
				}
			}
		}

		// No contradictions found
		Miscellaneous.logEvent("i", "CalendarCheck", "Event " + event + " matches.", 4);

		return true;
	}

    public boolean checkProximity()
    {
        try
        {
            float currentLevel = ProximityReceiver.getProximityLevel();
            String[] values = this.getTriggerParameter2().split(Trigger.triggerParameter2Split);
            float part1 = Float.parseFloat(values[0]);
            float part2 = Float.parseFloat(values[1]);

            // Are we within the defined scope?
            if(part1 <= currentLevel && currentLevel <= part2)
                return true;
        }
        catch(Exception e)
        {
            Miscellaneous.logEvent("e", "checkProximity()", Log.getStackTraceString(e), 1);
        }

        return false;
    }

    boolean checkBluetooth()
	{
		Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format("Checking for bluetooth...", this.getParentRule().getName()), 4);

		if(this.getBluetoothDeviceAddress().equals("<any>"))
		{
			if(this.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
			{
				if(BluetoothReceiver.isAnyDeviceConnected() != this.getTriggerParameter())
					return false;
			}
			else if((this.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
			{
				if(BluetoothReceiver.isAnyDeviceConnected() != this.getTriggerParameter())
					return false;
			}
			else
			{
				// range
				if(BluetoothReceiver.isAnyDeviceInRange() != this.getTriggerParameter())
					return false;
			}
		}
		else if(this.getBluetoothDeviceAddress().equals("<none>"))
		{
			if(this.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
			{
				if(BluetoothReceiver.isAnyDeviceConnected() == this.getTriggerParameter())
					return false;
			}
			else if((this.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
			{
				if(BluetoothReceiver.isAnyDeviceConnected() == this.getTriggerParameter())
					return false;
			}
			else
			{
				// range
				if(BluetoothReceiver.isAnyDeviceInRange() == this.getTriggerParameter())
					return false;
			}
		}
		else if(this.getBluetoothDeviceAddress().length() > 0)
		{
			if(this.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
			{
				if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(this.getBluetoothDeviceAddress())) != this.getTriggerParameter())
					return false;
			}
			else if((this.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
			{
				if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(this.getBluetoothDeviceAddress())) != this.getTriggerParameter())
					return false;
			}
			else
			{
				// range
				if(BluetoothReceiver.isDeviceInRange(BluetoothReceiver.getDeviceByAddress(this.getBluetoothDeviceAddress())) != this.getTriggerParameter())
					return false;
			}
		}
		else
		{
			Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleDoesntApplyStateNotCorrect), this.getParentRule().getName()), 3);
			return false;
		}

		return true;
	}

    boolean checkNfc()
	{
		if(NfcReceiver.lastReadLabel == null)
		{
			Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleDoesntApplyNoTagLabel), this.getParentRule().getName()),3);
			return false;
		}
		else if(!NfcReceiver.lastReadLabel.equals(this.getNfcTagId()))
		{
			Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleDoesntApplyWrongTagLabel) + " " + NfcReceiver.lastReadLabel + " / " + this.getNfcTagId(), this.getParentRule().getName()), 3);
			return false;
		}

		return true;
	}

    boolean checkPhoneCall()
	{
		String[] elements = this.getTriggerParameter2().split(triggerParameter2Split);
		// state dir number

		if(elements[2].equals(Trigger.triggerPhoneCallNumberAny) || Miscellaneous.comparePhoneNumbers(PhoneStatusListener.getLastPhoneNumber(), elements[2]) || (Miscellaneous.isRegularExpression(elements[2]) && PhoneStatusListener.getLastPhoneNumber().matches(elements[2])))
		{
			//if(PhoneStatusListener.isInACall() == oneTrigger.getTriggerParameter())
			if(
					(elements[0].equals(Trigger.triggerPhoneCallStateRinging) && PhoneStatusListener.getCurrentState() == TelephonyManager.CALL_STATE_RINGING)
							||
					(elements[0].equals(Trigger.triggerPhoneCallStateStarted) && PhoneStatusListener.getCurrentState() == TelephonyManager.CALL_STATE_OFFHOOK)
							||
					(elements[0].equals(Trigger.triggerPhoneCallStateStopped) && PhoneStatusListener.getCurrentState() == TelephonyManager.CALL_STATE_IDLE)
			)
			{
				if(
						elements[1].equals(Trigger.triggerPhoneCallDirectionAny)
								||
						(elements[1].equals(Trigger.triggerPhoneCallDirectionIncoming) && PhoneStatusListener.getLastPhoneDirection() == 1)
								||
						(elements[1].equals(Trigger.triggerPhoneCallDirectionOutgoing) && PhoneStatusListener.getLastPhoneDirection() == 2)
				)
				{
					// Trigger conditions are met
				}
				else
				{
					Miscellaneous.logEvent("i", "Rule", "A trigger of rule " + getParentRule().getName() + " doesn't apply. Wrong direction. Demanded: " + String.valueOf(this.getPhoneDirection()) + ", got: " + String.valueOf(PhoneStatusListener.getLastPhoneDirection()), 4);
					return false;
				}
			}
			else
			{
				Miscellaneous.logEvent("i", "Rule", "A trigger of rule " + getParentRule().getName() + " doesn't apply. Wrong call status. Demanded: " + elements[0] + ", got: " + String.valueOf(PhoneStatusListener.getCurrentState()) + " (0=idle, 1=ringing, 2=offhook)", 4);
				return false;
			}
		}
		else
		{
			Miscellaneous.logEvent("i", "Rule", "A trigger of rule " + getParentRule().getName() + " doesn't apply. Wrong phone number. Demanded: " + this.getPhoneNumber() + ", got: " + PhoneStatusListener.getLastPhoneNumber(), 4);
			return false;
		}

		return true;
	}

    boolean checkRoaming()
	{
		if(ConnectivityReceiver.isRoaming(Miscellaneous.getAnyContext()) != this.getTriggerParameter())
		{
			return false;
		}

		return true;
	}

    boolean checkAirplaneMode()
	{
		if(ConnectivityReceiver.isAirplaneMode(Miscellaneous.getAnyContext()) != this.getTriggerParameter())
		{
			return false;
		}

		return true;
	}

    boolean checkProcess()
	{
		boolean running = false;

		if(getTriggerParameter2().contains(triggerParameter2Split))
		{
			String parts[] = triggerParameter2.split(triggerParameter2Split);
			for(String appName : ProcessListener.getRunningApps())
			{
				if(appName.startsWith(parts[0]))
					running = true;
			}
		}
		else
			running = ProcessListener.getRunningApps().contains(this.getProcessName());

		if(running)
			Miscellaneous.logEvent("i", "ProcessMonitoring", "App " + this.getProcessName() + " is currently running.", 4);
		else
			Miscellaneous.logEvent("i", "ProcessMonitoring", "App " + this.getProcessName() + " is not running.", 4);

		if(running != this.getTriggerParameter())
		{
			Miscellaneous.logEvent("i", "ProcessMonitoring", "Trigger doesn't apply.", 4);
			return false;
		}

		Miscellaneous.logEvent("i", "ProcessMonitoring", "Trigger applies.", 4);

		return true;
	}

    boolean checkWifiConnection()
	{
		Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format( "Checking for wifi state", this.getParentRule().getName()),4);
		if(this.getTriggerParameter() == WifiBroadcastReceiver.lastConnectedState)	// connected / disconnected
		{
			if(this.getTriggerParameter2().length() > 0)	// only check if any wifi name specified, otherwise any wifi will do
			{
				Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format("Wifi name specified, checking that.", this.getParentRule().getName()), 4);

				if(WifiBroadcastReceiver.lastConnectedState) //when connected
				{
					if (!WifiBroadcastReceiver.getLastWifiSsid().equals(this.getTriggerParameter2()) && !(Miscellaneous.isRegularExpression(this.getTriggerParameter2()) && WifiBroadcastReceiver.getLastWifiSsid().matches(this.getTriggerParameter2())))
					{
						Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleDoesntApplyNotTheCorrectSsid), getParentRule().getName(), this.getTriggerParameter2(), WifiBroadcastReceiver.getLastWifiSsid()), this.getParentRule().getName()), 3);
						return false;
					}
					else
						Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format("Wifi name matches. Rule will apply.", this.getParentRule().getName()), 4);
				}
				else
				{
					if (
							!Settings.serviceStartDone
								||
							(!WifiBroadcastReceiver.getLastWifiSsidReal().equals(this.getTriggerParameter2()) && !(Miscellaneous.isRegularExpression(this.getTriggerParameter2()) && WifiBroadcastReceiver.getLastWifiSsidReal().matches(this.getTriggerParameter2()))))
					{
						Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleDoesntApplyNotTheCorrectSsid), getParentRule().getName(), this.getTriggerParameter2(), WifiBroadcastReceiver.getLastWifiSsidReal()), this.getParentRule().getName()), 3);
						return false;
					}
					else
						Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format("Wifi name matches. Rule will apply.", this.getParentRule().getName()), 4);
				}
			}
			else
				Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format("No wifi name specified, any will do.", this.getParentRule().getName()), 4);
		}
		else
		{
			Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format("Wifi state not correct, demanded " + String.valueOf(this.getTriggerParameter() + ", got " + String.valueOf(WifiBroadcastReceiver.lastConnectedState)), this.getParentRule().getName()), 4);
			return false;
		}

		return true;
	}

    boolean checkNoiseLevel()
	{
		if(this.getTriggerParameter())
		{
			if(NoiseListener.getNoiseLevelDb() < this.getNoiseLevelDb())
			{
				Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleDoesntApplyItsQuieterThan) + " " + String.valueOf(this.getNoiseLevelDb()), this.getParentRule().getName()), 3);
				return false;
			}
		}
		else
		{
			if(NoiseListener.getNoiseLevelDb() > this.getNoiseLevelDb())
			{
				Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleDoesntApplyItsLouderThan) + " " + String.valueOf(this.getNoiseLevelDb()), this.getParentRule().getName()), 3);
				return false;
			}
		}

		return true;
	}

    boolean checkSpeed()
	{
		if(this.getTriggerParameter())
		{
			if(LocationProvider.getSpeed() < this.getSpeed())
			{
				Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleDoesntApplyWeAreSlowerThan) + " " + String.valueOf(this.getSpeed()), this.getParentRule().getName()), 3);
				return false;
			}
		}
		else
		{
			if(LocationProvider.getSpeed() > this.getSpeed())
			{
				Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleDoesntApplyWeAreFasterThan) + " " + String.valueOf(this.getSpeed()), this.getParentRule().getName()), 3);
				return false;
			}
		}

		return true;
	}

    boolean checkBatteryLevel()
	{
		/*
			-1 means value is not known, yet.
		 */
		if(BatteryReceiver.getBatteryLevel() == -1)
			return false;

		if(this.getTriggerParameter())
		{
			if(this.getBatteryLevel() == 100)
			{
				Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), "Rule " + this.getParentRule().getName() + " doesn't apply. Can never exceed 100%.", 3);
				return false;
			}

			if(BatteryReceiver.getBatteryLevel() < this.getBatteryLevel())
			{
				Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleDoesntApplyBatteryLowerThan) + " " + String.valueOf(this.getBatteryLevel()), this.getParentRule().getName()), 3);
				return false;
			}
		}
		else
		{
			if(this.getBatteryLevel() == 0)
			{
				Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), "Rule " + this.getParentRule().getName() + " doesn't apply. Can never drop below 0%.", 3);
				return false;
			}

			if(BatteryReceiver.getBatteryLevel() < 100 && BatteryReceiver.getBatteryLevel() >= this.getBatteryLevel()
						||
				BatteryReceiver.getBatteryLevel() > this.getBatteryLevel()
			)
			{
				Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleDoesntApplyBatteryHigherThan) + " " + String.valueOf(this.getBatteryLevel()), this.getParentRule().getName()), 3);
				return false;
			}
		}

		return true;
	}

    boolean checkUsbHostConnection()
	{
		if(BatteryReceiver.isUsbHostConnected() != this.getTriggerParameter())
		{
			return false;
		}

		return true;
	}

    boolean checkLocation()
	{
		// Am I here?
		PointOfInterest activePoi = PointOfInterest.getActivePoi();
		if(activePoi != null)	//entering one
		{
			if(this.getPointOfInterest() != null)
			{
				if(activePoi.equals(this.getPointOfInterest()))
				{
					if(!this.getTriggerParameter())
					{
						Miscellaneous.logEvent("i", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), this.getParentRule().getName()), String.format("Rule \"%1$s\" doesn't apply. We're entering POI: " + this.getPointOfInterest().getName() + ", not leaving it.", getParentRule().getName()), 4);
						return false;
					}
				}
				else
				{
					Miscellaneous.logEvent("i", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), this.getParentRule().getName()), String.format("Rule \"%1$s\" doesn't apply. This is " + activePoi.getName() + ", not " + this.getPointOfInterest().getName() + ".", getParentRule().getName()), 4);
					return false;
				}
			}
			else if(this.getPointOfInterest() == null)
			{
				if(this.getTriggerParameter())
				{
					Miscellaneous.logEvent("i", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), this.getParentRule().getName()), String.format("Rule %s$s doesn't apply. We're at a POI. Rule specifies not at none, so leaving any.",getParentRule().getName()),  4);
					return false;
				}
			}
		}
		else					//leaving one
		{
			// We are not at any POI. But if this trigger requires us NOT to be there, that may be fine.
			if(this.getPointOfInterest() != null)
			{
				if(!this.getTriggerParameter())
				{
					Miscellaneous.logEvent("i", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), this.getParentRule().getName()), "We are not at POI \"" + this.getPointOfInterest().getName() + "\". But since that's required by this rule that's fine.", 4);
				}
				else
				{
					Miscellaneous.logEvent("i", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), this.getParentRule().getName()), String.format("Rule \"%1$s\" doesn't apply. We're not at POI \"" + this.getPointOfInterest().getName() + "\".", getParentRule().getName()), 3);
					return false;
				}
			}
			else if(this.getPointOfInterest() == null)
			{
				if(!this.getTriggerParameter())
				{
					Miscellaneous.logEvent("i", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), this.getParentRule().getName()), String.format("Rule \"%1$s\" doesn't apply. We're at no POI. Rule specifies to be at anyone.", getParentRule().getName()), 5);
					return false;
				}
			}
		}

		return true;
	}

	public boolean hasStateNotAppliedSinceLastRuleExecution()
	{
        /*
            There is no easy event to tell when a tag has been disconnected again.
         */
		if(getTriggerType().equals(Trigger_Enum.nfcTag))
            return true;

		if(getParentRule().getLastExecution() == null)
		{
			Miscellaneous.logEvent("i", "Trigger", "Trigger " + this.toString() + " of rule " + getParentRule().getName() + " has NOT applied since the parent rule\'s last activation.", 4);
			return true;
		}
		else if(lastTimeNotApplied != null)
		{
			if(lastTimeNotApplied.getTimeInMillis() > getParentRule().getLastExecution().getTimeInMillis())
			{
				Miscellaneous.logEvent("i", "Trigger", "Trigger " + this.toString() + " of rule " + getParentRule().getName() + " has NOT applied since the parent rule\'s last activation.", 4);
				return true;
			}
		}

		Miscellaneous.logEvent("i", "Trigger", "Trigger " + this.toString() + " of rule " + getParentRule().getName() + " may apply currently, but has not NOT applied since the rule\'s last execution.", 4);
		return false;
	}

	boolean checkCharging()
	{
		if(BatteryReceiver.isDeviceCharging(Miscellaneous.getAnyContext()) == 0)		// unknown state
		{
			return false; // unknown charging state, can't activate rule under these conditions
		}
		else if(BatteryReceiver.isDeviceCharging(Miscellaneous.getAnyContext()) == 1)	// we are discharging
		{
			if(!this.getTriggerParameter()) // rule says when charging, but we're currently discharging
				return true;
		}
		else if(BatteryReceiver.isDeviceCharging(Miscellaneous.getAnyContext()) == 2)	// we are charging
		{
			if(this.getTriggerParameter()) // rule says when discharging, but we're currently charging
			{
				// check charging type
				if(StringUtils.isEmpty(getTriggerParameter2()))
					return true;
				else
				{
					int desiredType;
					String[] typeParams = getTriggerParameter2().split(triggerParameter2Split, -1);
					desiredType = Integer.parseInt(typeParams[0]);
					if(desiredType == BatteryReceiver.batteryChargingTypeAny)
						return true;
					else if(desiredType == BatteryReceiver.getCurrentChargingType())
						return true;
				}
			}
		}

		return false;
	}

	boolean checkTetheringActive()
	{
		if(TetheringReceiver.isTetheringActive() == triggerParameter)
		{
			if(getTriggerParameter2().equals(ActivityManageTriggerTethering.tetheringTypeAny))
				return true;

			for(String oneOfLastTypes : TetheringReceiver.getLastTetheringTypes())
			{
				if(oneOfLastTypes.equals(getTriggerParameter2()))
				{
					if(triggerParameter)
						return true;
					else
						return false;
				}
			}
		}

		return false;
	}

	public boolean checkDateTime(Object triggeringObject, boolean checkIfStateChangedSinceLastRuleExecution)
	{
		/*
		 * Use format known from Automation
		 * 07:30:00/17:30:00/23456/300	<-- last parameter is optional: repetition in seconds
		 * Also required: inside or outside that interval
		 */

		Date triggeringTime;
		if(triggeringObject instanceof Date)
			triggeringTime = (Date)triggeringObject;
		else
			triggeringTime = new Date();

		String timeString = String.valueOf(triggeringTime.getHours()) + ":" + String.valueOf(triggeringTime.getMinutes()) + ":" + String.valueOf(triggeringTime.getSeconds());
		TimeObject nowTime = TimeObject.valueOf(timeString);
		Calendar calNow = Calendar.getInstance();

		try
		{
			TimeFrame tf = new TimeFrame(getTriggerParameter2());

			if (DateTimeListener.areWeInTimeFrame(this, triggeringObject) != this.getTriggerParameter())
			{
				if (triggerParameter)
					Miscellaneous.logEvent("i", "Trigger", "TimeFrame: We're currently (" + calNow.getTime().toString() + ", Day: " + String.valueOf(calNow.get(Calendar.DAY_OF_WEEK)) + ") outside of the specified TimeFrame (" + tf.toString() + "), but demanded is inside.", 5);
				else
					Miscellaneous.logEvent("i", "Trigger", "TimeFrame: We're currently (" + calNow.getTime().toString() + ") in the specified TimeFrame (" + tf.toString() + "), but demanded is outside.", 4);

				return false;
			}

			// We are inside or outside of the timeframe as demanded by the trigger

			if (tf.getRepetition() > 0)
			{
				if (!isSupposedToRepeatSinceLastExecution(Calendar.getInstance()))
				{
					Miscellaneous.logEvent("i", "TimeFrame", "TimeFrame: Trigger of rule " + this.getParentRule().getName() + " applies, but repetition is not due, yet.", 4);
					return false;
				}
				else
					Miscellaneous.logEvent("i", "TimeFrame", "TimeFrame: Trigger of rule " + this.getParentRule().getName() + " would apply because repetition is due.", 4);
			}
			else
			{
				if (checkIfStateChangedSinceLastRuleExecution)
				{
					/*
					 * This is not a repeating rule. Have we left
					 * the relevant timeframe since the last run?
					 * Determine if it has ran today already. If yes
					 * return false because every rule that is not
					 * repeating can only be executed once per day.
					 */

					if (
							getParentRule().getLastExecution().get(Calendar.YEAR) == calNow.get(Calendar.YEAR)
									&&
							getParentRule().getLastExecution().get(Calendar.MONTH) == calNow.get(Calendar.MONTH)
									&&
							getParentRule().getLastExecution().get(Calendar.DAY_OF_MONTH) == calNow.get(Calendar.DAY_OF_MONTH)
					)
					{
						Miscellaneous.logEvent("i", "TimeFrame", "TimeFrame: Trigger of rule " + this.getParentRule().getName() + " applies, but it was already executed today.", 4);
						return false;
					}
				}
			}
		}
		catch(Exception e)
		{
			Miscellaneous.logEvent("e", "Trigger", "There was an error while checking if the time based trigger applies: " + Log.getStackTraceString(e), 1);
			return false;
		}

		return true;
	}

	boolean isSupposedToRepeatSinceLastExecution(Calendar now)
	{
		TimeFrame tf = new TimeFrame(getTriggerParameter2());
		Calendar lastExec = getParentRule().getLastExecution();

		// the simple stuff:

		if(lastExec == null)				// rule never ran, go in any case
			return true;
		else if(tf.getRepetition() <= 0)	// is not set to repeat at all
			return false;

		/*
		 * We don't need to check if the trigger currently applies, that has
		 * been done externally via the applies() function. We can safely assume
		 * we're inside the specified timeframe.
		 */

		Calendar lastRepetitionNotExecutedYet = DateTimeListener.getNextRepeatedExecution(this);
		lastRepetitionNotExecutedYet.add(Calendar.SECOND, (int) -(tf.getRepetition()));

		Miscellaneous.logEvent("i", "isSupposedToRepeatSinceLastExecution()", "Last execution: " + Miscellaneous.formatDate(lastExec.getTime()), 5);
		Miscellaneous.logEvent("i", "isSupposedToRepeatSinceLastExecution()", "lastRepetitionNotExecutedYet: " + Miscellaneous.formatDate(lastRepetitionNotExecutedYet.getTime()), 5);

		if(now.getTimeInMillis() > lastRepetitionNotExecutedYet.getTimeInMillis())
			return true;

		return false;
	}
    
	public int getHeadphoneType()
	{
		return headphoneType;
	}
	public void setHeadphoneType(int headphoneType)
	{
		this.headphoneType = headphoneType;
	}
	public String getNfcTagId()
	{
		return nfcTagId;
	}
	public void setNfcTagId(String nfcTagId)
	{
		this.nfcTagId = nfcTagId;
	}
	
	public int getActivityDetectionType()
	{
		return activityDetectionType;
	}
	public void setActivityDetectionType(int activityDetectionType)
	{
		this.activityDetectionType = activityDetectionType;
	}
	public String getBluetoothDeviceAddress()
	{
		return bluetoothDeviceAddress;
	}
	public void setBluetoothDeviceAddress(String bluetoothDeviceAddress)
	{
		this.bluetoothDeviceAddress = bluetoothDeviceAddress;
	}
	public void setPhoneNumber(String phoneNumber)
	{
		this.phoneNumber = phoneNumber;
	}
	public String getPhoneNumber()
	{
		return phoneNumber;
	}

	public void setPhoneDirection(int phoneDirection)
	{
		this.phoneDirection = phoneDirection;
	}
	public int getPhoneDirection()
	{
		return phoneDirection;
	}

	public int getBatteryLevel()
	{
		return batteryLevel;
	}

	public void setBatteryLevel(int batteryLevel)
	{
		this.batteryLevel = batteryLevel;
	}

	public String getProcessName()
	{
		return processName;
	}

	public void setProcessName(String processName)
	{
		this.processName = processName;
	}

	public PointOfInterest getPointOfInterest()
	{
		return pointOfInterest;
	}

	public void setPointOfInterest(PointOfInterest setPointOfInterest)
	{
		this.pointOfInterest = setPointOfInterest;
	}
	
	public double getSpeed()
	{
		return speed;
	}

	public void setSpeed(double speed)
	{
		this.speed = speed;
	}
	
	public long getNoiseLevelDb()
	{
		return noiseLevelDb;
	}

	public void setNoiseLevelDb(long noiseLevelDb)
	{
		this.noiseLevelDb = noiseLevelDb;
	}

	public Trigger_Enum getTriggerType()
	{
		return triggerType;
	}

	public void setTriggerType(Trigger_Enum setTriggerType)
	{
		this.triggerType = setTriggerType;
	}

	public boolean getTriggerParameter()
	{
		return triggerParameter;
	}

	public void setTriggerParameter(boolean triggerParameter)
	{
		this.triggerParameter = triggerParameter;
	}

	public String getTriggerParameter2()
	{
		return triggerParameter2;
	}

	public void setTriggerParameter2(String triggerParameter2)
	{
		this.triggerParameter2 = triggerParameter2.trim();
	}

	public TimeFrame getTimeFrame()
	{
		return timeFrame;
	}

	public void setTimeFrame(TimeFrame timeFrame)
	{
		this.timeFrame = timeFrame;
	}

	@RequiresApi(api = Build.VERSION_CODES.KITKAT)
	@SuppressWarnings("unused")
	@Override
	public String toString()
	{
		StringBuilder returnString = new StringBuilder();
		
		switch(this.getTriggerType())
		{
			case charging:
				if (getTriggerParameter())
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.starting) + " ");
				else
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.stopping) + " ");
				returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.triggerCharging));
				returnString.append(" (");
				if(!StringUtils.isEmpty(getTriggerParameter2()))
				{
					String[] pieces = getTriggerParameter2().split(triggerParameter2Split, -1);
					if(pieces[0].equals("0"))
						returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.any));
					else if(pieces[0].equals(String.valueOf(BatteryManager.BATTERY_PLUGGED_AC)))
						returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.charging_AC));
					else if(pieces[0].equals(String.valueOf(BatteryManager.BATTERY_PLUGGED_USB)))
						returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.usb));
					else if(pieces[0].equals(String.valueOf(BatteryManager.BATTERY_PLUGGED_WIRELESS)))
						returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.charging_wireless));
				}
				returnString.append(")");
				break;
			case batteryLevel:
				returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.batteryLevel));
				if (getTriggerParameter())
					returnString.append(" " + Miscellaneous.getAnyContext().getResources().getString(R.string.exceeds) + " ");
				else
					returnString.append(" " + Miscellaneous.getAnyContext().getResources().getString(R.string.dropsBelow) + " ");
				returnString.append(String.valueOf(this.getBatteryLevel()) + " %");
				break;
			case usb_host_connection:
				if (getTriggerParameter())
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.connecting) + " ");
				else
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.disconnecting) + " ");

				returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.triggerUsb_host_connection));
				break;
			case pointOfInterest:
				if (this.getPointOfInterest() != null)
				{
					if (getTriggerParameter())
						returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.entering) + " ");
					else
						returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.leaving) + " ");

					returnString.append(this.getPointOfInterest().getName().toString());
				}
				else
				{
					if (getTriggerParameter())
						returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.leaving) + " " + Miscellaneous.getAnyContext().getResources().getString(R.string.anyLocation));
					else
						returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.entering) + " " + Miscellaneous.getAnyContext().getResources().getString(R.string.anyLocation));
				}
				break;
			case timeFrame:
				if (getTriggerParameter())
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.entering) + " ");
				else
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.leaving) + " ");

				String repeat = ", " + Miscellaneous.getAnyContext().getResources().getString(R.string.noRepetition);
				if (this.getTimeFrame().getRepetition() > 0)
					repeat = ", " + String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.repeatEveryXsecondsWithVariable), String.valueOf(this.getTimeFrame().getRepetition()));

				returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.triggerTimeFrame) + ": " + this.getTimeFrame().getTriggerTimeStart().toString() + " " + Miscellaneous.getAnyContext().getResources().getString(R.string.until) + " " + this.getTimeFrame().getTriggerTimeStop().toString() + " " + Miscellaneous.getAnyContext().getResources().getString(R.string.onDays) + " " + this.getTimeFrame().getDayList().toString() + repeat);
				break;
			case speed:
				if (getTriggerParameter())
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.exceeding) + " ");
				else
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.droppingBelow) + " ");
				returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.triggerSpeed) + ": " + String.valueOf(this.getSpeed()) + " km/h");
				break;
			case noiseLevel:
				if (getTriggerParameter())
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.exceeding) + " ");
				else
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.droppingBelow) + " ");

				returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.triggerNoiseLevel) + ": " + String.valueOf(this.getNoiseLevelDb()) + " dB");
				break;
			case wifiConnection:
				String wifiDisplayName = "";
				if (this.getTriggerParameter2().length() == 0)
					wifiDisplayName += Miscellaneous.getAnyContext().getResources().getString(R.string.anyWifi);
				else
					wifiDisplayName += this.getTriggerParameter2();

				if (getTriggerParameter())
					returnString.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.connectedToWifi), wifiDisplayName));
				else
					returnString.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.disconnectedFromWifi), wifiDisplayName));

				break;
			case process_started_stopped:
				returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.application) + " " + this.getProcessName() + " " + Miscellaneous.getAnyContext().getResources().getString(R.string.is) + " ");
				if (this.triggerParameter)
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.started));
				else
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.stopped));
				break;
			case airplaneMode:
				if (getTriggerParameter())
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.activated) + " ");
				else
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.deactivated) + " ");
				returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.airplaneMode));
				break;
			case roaming:
				returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.roaming));
				if (getTriggerParameter())
					returnString.append(" " + Miscellaneous.getAnyContext().getResources().getString(R.string.activated));
				else
					returnString.append(" " + Miscellaneous.getAnyContext().getResources().getString(R.string.deactivated));
				break;
			case phoneCall:
				String[] elements = triggerParameter2.split(triggerParameter2Split);

				returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.phoneCall));

				returnString.append(" ");

				if (elements[1].equals(triggerPhoneCallDirectionAny))
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.with));
				else if (elements[1].equals(triggerPhoneCallDirectionIncoming))
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.from));
				else if (elements[1].equals(triggerPhoneCallDirectionOutgoing))
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.to));

				returnString.append(" ");

				if (elements[2].equals(Trigger.triggerPhoneCallNumberAny))
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.any) + " " + Miscellaneous.getAnyContext().getResources().getString(R.string.number));
				else
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.number) + " " + Miscellaneous.getAnyContext().getResources().getString(R.string.matching) + " " + elements[2]);

				returnString.append(" ");

				if (elements[0].equals(Trigger.triggerPhoneCallStateRinging))
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.ringing));
				else if (elements[0].equals(Trigger.triggerPhoneCallStateStarted))
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.started));
				else if (elements[0].equals(Trigger.triggerPhoneCallStateStopped))
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.stopped));

				break;
			case nfcTag:
				// This type doesn't have an activate/deactivate equivalent
				returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.closeTo) + " " + Miscellaneous.getAnyContext().getResources().getString(R.string.nfcTag) + " " + Miscellaneous.getAnyContext().getResources().getString(R.string.withLabel) + " " + this.getNfcTagId());
				break;
			case activityDetection:
				try
				{
					Class testClass = Class.forName(ActivityManageRule.activityDetectionClassPath);
					if (ActivityPermissions.isPermissionDeclaredInManifest(Miscellaneous.getAnyContext(), "com.google.android.gms.permission.ACTIVITY_RECOGNITION"))
					{
						// This type doesn't have an activate/deactivate equivalent, at least not yet.
						returnString.append(Miscellaneous.runMethodReflective(ActivityManageRule.activityDetectionClassPath, "getDescription", new Object[]{getActivityDetectionType()}));
					}
					else
						returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.featureNotInFdroidVersion));
				}
				catch (ClassNotFoundException e)
				{
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.featureNotInFdroidVersion));
				}
				break;
			case bluetoothConnection:
				String device = Miscellaneous.getAnyContext().getResources().getString(R.string.anyDevice);
				if (bluetoothDeviceAddress.equals("<any>"))
				{
					device = Miscellaneous.getAnyContext().getResources().getString(R.string.any);
				}
				else if (bluetoothDeviceAddress.equals("<none>"))
				{
					device = Miscellaneous.getAnyContext().getResources().getString(R.string.noDevice);
				}
				else
				{
					try
					{
						device = BluetoothReceiver.getDeviceByAddress(bluetoothDeviceAddress).getName() + " (" + this.bluetoothDeviceAddress + ")";
					}
					catch (NullPointerException e)
					{
						device = Miscellaneous.getAnyContext().getResources().getString(R.string.invalidDevice) + ": " + this.bluetoothDeviceAddress;
						Miscellaneous.logEvent("w", "Trigger", device, 3);
					}
				}

				if (bluetoothEvent.equals(BluetoothDevice.ACTION_ACL_CONNECTED) || bluetoothEvent.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED))
				{
					if (this.triggerParameter)
						returnString.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.bluetoothConnectionTo), device));
					else
						returnString.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.bluetoothDisconnectFrom), device));
				}
				else if (bluetoothEvent.equals(BluetoothDevice.ACTION_FOUND))
				{
					if (this.triggerParameter)
						returnString.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.bluetoothDeviceInRange), device));
					else
						returnString.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.bluetoothDeviceOutOfRange), device));
				}
				break;
			case headsetPlugged:
				String type;
				switch (headphoneType)
				{
					case 0:
						type = Miscellaneous.getAnyContext().getResources().getString(R.string.headphoneSimple);
						break;
					case 1:
						type = Miscellaneous.getAnyContext().getResources().getString(R.string.headphoneMicrophone);
						break;
					case 2:
						type = Miscellaneous.getAnyContext().getResources().getString(R.string.headphoneAny);
						break;
					default:
						type = Miscellaneous.getAnyContext().getResources().getString(R.string.notSet);
						break;
				}
				if (getTriggerParameter())
					returnString.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.headsetConnected), type));
				else
					returnString.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.headsetDisconnected), type));
				break;
			case notification:
				if (this.getTriggerParameter2().contains(triggerParameter2Split))
				{
					String[] params = getTriggerParameter2().split(triggerParameter2Split);

					String app = params[0];
					String titleDir = params[1];
					String title = params[2];
					String textDir = params[3];
					String text;
					if (params.length >= 5)
						text = params[4];
					else
						text = "";
					StringBuilder triggerBuilder = new StringBuilder();

					String appString;
					if (app.equalsIgnoreCase(anyAppString))
						appString = Miscellaneous.getAnyContext().getResources().getString(R.string.anyApp);
					else
						appString = "app " + app;

					if (triggerParameter)
						triggerBuilder.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.postsNotification), appString));
					else
						triggerBuilder.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.removedNotification), appString));

					if (title.length() > 0)
						triggerBuilder.append(", " + Miscellaneous.getAnyContext().getString(R.string.title) + " " + Trigger.getMatchString(titleDir) + " " + title);

					if (text.length() > 0)
						triggerBuilder.append(", " + Miscellaneous.getAnyContext().getString(R.string.text) + " " + Trigger.getMatchString(textDir) + " " + text);

					returnString.append(triggerBuilder.toString());
				}
				else
				{
					setTriggerParameter2(anyAppString + triggerParameter2Split + directionEquals + triggerParameter2Split + triggerParameter2Split + directionEquals + triggerParameter2Split + triggerParameter2Split);
				}
				break;
			case deviceOrientation:
				returnString.append(Miscellaneous.getAnyContext().getString(R.string.deviceIsInCertainOrientation));
				break;
			case profileActive:
				if (triggerParameter)
					returnString.append(String.format(Miscellaneous.getAnyContext().getString(R.string.profileActive), getTriggerParameter2().split(Trigger.triggerParameter2Split)[0]));
				else
					returnString.append(String.format(Miscellaneous.getAnyContext().getString(R.string.profileNotActive), getTriggerParameter2().split(Trigger.triggerParameter2Split)[0]));
				break;
			case musicPlaying:
				if (triggerParameter)
					returnString.append(Miscellaneous.getAnyContext().getString(R.string.musicIsPlaying));
				else
					returnString.append(Miscellaneous.getAnyContext().getString(R.string.musicIsNotPlaying));
				break;
			case screenState:
				String state;
				switch (triggerParameter2)
				{
					case "0":
						state = Miscellaneous.getAnyContext().getString(R.string.off);
						break;
					case "1":
						state = Miscellaneous.getAnyContext().getString(R.string.on);
						break;
					case "2":
						state = Miscellaneous.getAnyContext().getString(R.string.unlocked);
						break;
					case "3":
						state = Miscellaneous.getAnyContext().getString(R.string.lockedWithoutSecurity);
						break;
					case "4":
						state = Miscellaneous.getAnyContext().getString(R.string.lockedWithSecurity);
						break;
					default:
						state = Miscellaneous.getAnyContext().getString(R.string.unknown);
				}
				returnString.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.screenIs), state));
				break;
			case deviceStarts:
				// This type doesn't have an activate/deactivate equivalent
				returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.deviceIsStarting) + ": " + String.valueOf(triggerParameter));
				break;
			case serviceStarts:
				// This type doesn't have an activate/deactivate equivalent
				returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.serviceIsStarting) + ": " + String.valueOf(triggerParameter));
				break;
			case broadcastReceived:
				if (triggerParameter)
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.broadcastReceived));
				else
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.broadcastNotReceived));

				returnString.append(": " + triggerParameter2);
				break;
			case tethering:
				if (triggerParameter)
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.tetheringActive));
				else
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.tetheringNotActive));

				returnString.append(" (" + Miscellaneous.getAnyContext().getResources().getString(R.string.type) + ": " + triggerParameter2 + ")");
				break;
			case subSystemState:
				Trigger.subSystemStates desiredState = subSystemStates.valueOf(triggerParameter2);
				switch (desiredState)
				{
					case wifi:
						returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.wifi));
						break;
					case bluetooth:
						returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.bluetooth));
						break;
				}

				returnString.append(" " + Miscellaneous.getAnyContext().getResources().getString(R.string.is) + " ");

				if (triggerParameter)
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.activated));
				else
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.deactivated));
				break;
			case checkVariable:
				if(triggerParameter)
				{
					if (triggerParameter2.contains(triggerParameter2Split))
					{
						String[] parts = triggerParameter2.split(triggerParameter2Split);
						returnString.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.variableCheckString), parts[0], parts[1]));
					}
					else
						returnString.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.variableCheckStringDeleted), triggerParameter2));
				}
				else
				{
					String[] parts = triggerParameter2.split(triggerParameter2Split);
					returnString.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.variableCheckStringNot), parts[0], parts[1]));
				}
				break;
			case calendarEvent:
				returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.calendarEvent));

				returnString.append(" (");

				if(triggerParameter)
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.eventIsCurrentlyHappening));
				else
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.eventIsCurrentlyNotHappening));

				returnString.append( ", ");

				String[] conditions = triggerParameter2.split(triggerParameter2Split, -1);

				if (!StringUtils.isEmpty(conditions[1]))
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.title) + " " + conditions[0] + " " + conditions[1] + ", ");
				if (!StringUtils.isEmpty(conditions[3]))
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.location) + " " + conditions[2] + " " + conditions[3] + ", ");
				if (!StringUtils.isEmpty(conditions[5]))
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.calendarDescription) + " " + conditions[4] + " " + conditions[5] + ", ");

				if(Boolean.parseBoolean(conditions[6]))
				{
					if (Boolean.parseBoolean(conditions[7]))
						returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.allDayEventTrue) + ", ");
					else
						returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.allDayEventFalse) + ", ");
				}

				if(Boolean.parseBoolean(conditions[8]))
				{
					if (Boolean.parseBoolean(conditions[9]))
						returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.reoccurringTrue) + ", ");
					else
						returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.reoccurringFalse) + ", ");
				}

				if (!StringUtils.isEmpty(conditions[10]))
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.availabilities) + " " + conditions[10] + ", ");

				if (!StringUtils.isEmpty(conditions[11]))
					returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.calendars) + " " + conditions[11]);

				if (returnString.toString().endsWith(", "))
					returnString.delete(returnString.length() - 2, returnString.length());

				returnString.append(")");

				break;
            case proximity:
                String[] parts = triggerParameter2.split(triggerParameter2Split);
                returnString.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.proximityText), parts[0], parts[1]));
                break;
			default:
				returnString.append("error");
				break;
		}

		return returnString.toString();
	}

	public static final String directionEquals = "eq";
	public static final String directionContains = "ct";
	public static final String directionNotContains = "nc";
	public static final String directionStartsWith = "sw";
	public static final String directionEndsWith = "ew";
	public static final String directionNotEquals = "ne";

	public static String getMatchString(String direction)
	{
		switch(direction)
		{
			case directionEquals:
				return Miscellaneous.getAnyContext().getString(R.string.directionStringEquals);
			case directionContains:
				return Miscellaneous.getAnyContext().getString(R.string.directionStringContains);
			case directionNotContains:
				return Miscellaneous.getAnyContext().getString(R.string.directionStringDoesNotContain);
			case directionStartsWith:
				return Miscellaneous.getAnyContext().getString(R.string.directionStringStartsWith);
			case directionEndsWith:
				return Miscellaneous.getAnyContext().getString(R.string.directionStringEndsWith);
			case directionNotEquals:
				return Miscellaneous.getAnyContext().getString(R.string.directionStringNotEquals);
			default:
				return Miscellaneous.getAnyContext().getString(R.string.error);
		}
	}

	public static String getMatchCode(String direction)
	{
		if(direction.equalsIgnoreCase(Miscellaneous.getAnyContext().getString(R.string.directionStringEquals)))
			return directionEquals;
		else if(direction.equalsIgnoreCase(Miscellaneous.getAnyContext().getString(R.string.directionStringContains)))
			return directionContains;
		else if(direction.equalsIgnoreCase(Miscellaneous.getAnyContext().getString(R.string.directionStringDoesNotContain)))
			return directionNotContains;
		else if(direction.equalsIgnoreCase(Miscellaneous.getAnyContext().getString(R.string.directionStringStartsWith)))
			return directionStartsWith;
		else if(direction.equalsIgnoreCase(Miscellaneous.getAnyContext().getString(R.string.directionStringEndsWith)))
			return directionEndsWith;
		else if(direction.equalsIgnoreCase(Miscellaneous.getAnyContext().getString(R.string.directionStringNotEquals)))
			return directionNotEquals;
		else
			return Miscellaneous.getAnyContext().getString(R.string.error);
	}

	public static String[] getTriggerTypesAsArray()
	{
		ArrayList<String> triggerTypesList = new ArrayList<String>();

		for(Trigger_Enum triggerType : Trigger_Enum.values())
			triggerTypesList.add(triggerType.name());
		
		return (String[])triggerTypesList.toArray(new String[triggerTypesList.size()]);
	}
	

	public static String[] getTriggerTypesStringAsArray(Context context)
	{
		ArrayList<String> triggerTypesList = new ArrayList<String>();

		for(Trigger_Enum triggerType : Trigger_Enum.values())
			triggerTypesList.add(triggerType.getFullName(context));
		
		return (String[])triggerTypesList.toArray(new String[triggerTypesList.size()]);
	}

	public void setBluetoothEvent(String string)
	{
		this.bluetoothEvent = string;
	}
	public Object getBluetoothEvent()
	{
		return this.bluetoothEvent;
	}

	public Rule getParentRule()
	{
		return parentRule;
	}

	public void setParentRule(Rule parentRule)
	{
		this.parentRule = parentRule;
	}
}