package uk.co.alt236.bluetoothlelib.device.adrecord;
import java.util.Arrays;

import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;

/**
 * Created by Dave Smith
 * Double Encore, Inc.
 *
 * Expanded by Alexandros Schillings
 */
public final class AdRecord implements Parcelable{

	//	02 # Number of bytes that follow in first AD structure
	//	01 # Flags AD type
	//	1A # Flags value 0x1A = 000011010
	//	   bit 0 (OFF) LE Limited Discoverable Mode
	//	   bit 1 (ON) LE General Discoverable Mode
	//	   bit 2 (OFF) BR/EDR Not Supported
	//	   bit 3 (ON) Simultaneous LE and BR/EDR to Same Device Capable (controller)
	//	   bit 4 (ON) Simultaneous LE and BR/EDR to Same Device Capable (Host)
	//	1A # Number of bytes that follow in second (and last) AD structure
	//	FF # Manufacturer specific data AD type
	//	4C 00 # Company identifier code (0x004C == Apple)
	//	02 # Byte 0 of iBeacon advertisement indicator
	//	15 # Byte 1 of iBeacon advertisement indicator
	//	e2 c5 6d b5 df fb 48 d2 b0 60 d0 f5 a7 10 96 e0 # iBeacon proximity uuid
	//	00 00 # major
	//	00 00 # minor
	//	c5 # The 2's complement of the calibrated Tx Power


	private static final String PARCEL_RECORD_DATA = "record_data";
	private static final String PARCEL_RECORD_TYPE = "record_type";
	private static final String PARCEL_RECORD_LENGTH = "record_length";

	/**
	 * General FLAGS
	 *
	 * Description: Flags
	 *
	 * Information:
	 * Bit 0: LE Limited Discoverable Mode
	 * Bit 1: LE General Discoverable Mode
	 * Bit 2: BR/EDR Not Supported (i.e. bit 37 of LMP Extended Feature bits Page 0)
	 * Bit 3: Simultaneous LE and BR/EDR to Same Device Capable (Controller) (i.e. bit 49 of LMP Extended Feature bits Page 0)
	 * Bit 4: Simultaneous LE and BR/EDR to Same Device Capable (Host) (i.e. bit 66 of LMP Extended Feature bits Page 1)
	 * Bits 5-7 Reserved
	 */
	public static final int TYPE_FLAGS                 = 0x01;

	// SERVICE
	public static final int TYPE_UUID16_INC            = 0x02;
	public static final int TYPE_UUID16                = 0x03;
	public static final int TYPE_UUID32_INC            = 0x04;
	public static final int TYPE_UUID32                = 0x05;
	public static final int TYPE_UUID128_INC           = 0x06;
	public static final int TYPE_UUID128               = 0x07;

	// Local name
	public static final int TYPE_LOCAL_NAME_SHORT            = 0x08;
	public static final int TYPE_LOCAL_NAME_COMPLETE         = 0x09;

	// TX Power Level
	public static final int TYPE_TX_POWER_LEVEL              = 0x0A;

	// SIMPLE PAIRING OPTIONAL OOB TAGS
	public static final int TYPE_DEVICE_CLASS                = 0x0D;
	public static final int TYPE_SIMPLE_PAIRING_HASH_C       = 0x0E;
	public static final int TYPE_SIMPLE_PAIRING_RANDOMIZER_R = 0x0F;

	// SECURITY MANAGER TK VALUE
	public static final int TYPE_TK_VALUE				     = 0x10;


	/* SECURITY MANAGER OOB FLAGS
	 *
	 * Description: Flag (1 octet)
	 *
	 * Information:
	 * Bit 0: OOB Flags Field: (0 = OOB data not present, 1 = OOB data present)
	 * Bit 1: LE supported (Host) (i.e. bit 65 of LMP Extended Feature bits Page 1
	 * Bit 2: Simultaneous LE and BR/EDR to Same Device Capable (Host) (i.e. bit 66 of LMP Extended Feature bits Page 1)
	 * Bit 3: Address type (0 = Public Address, 1 = Random Address)
	 * Bits 4-7 Reserved
	 */
	public static final int TYPE_SECURITY_MANAGER_OOB_FLAGS  = 0x11;


	/* SLAVE CONNECTION INTERVAL RANGE
	 *
	 * Description: Slave Connection Interval Range
	 *
	 * Information:
	 * The first 2 octets defines the minimum value for the connection interval in the following manner:
	 *	connInterval min = Conn_Interval_Min * 1.25 ms
	 *	Conn_Interval_Min range: 0x0006 to 0x0C80
	 *	Value of 0xFFFF indicates no specific minimum.
	 *	Values outside the range are reserved. (excluding 0xFFFF)
	 *
	 * The second 2 octets defines the maximum value for the connection interval in the following manner:
	 *  connInterval max = Conn_Interval_Max * 1.25 ms
	 *  Conn_Interval_Max range: 0x0006 to 0x0C80
	 *  Conn_Interval_Max shall be equal to or greater
	 *  than the Conn_Interval_Min.
	 *  Value of 0xFFFF indicates no specific maximum.
	 *  Values outside the range are reserved (excluding 0xFFFF)
	 */
	public static final int TYPE_CONNECTION_INTERVAL_RANGE  = 0x12;

	// SERVICE SOLICITATION
	public static final int TYPE_SERVICE_UUIDS_LIST_16BIT   = 0x14;
	public static final int TYPE_SERVICE_UUIDS_LIST_128BIT  = 0x15;

	/* SERVICE DATA
	 *
	 * Description: Service Data (2 or more octets)
	 * Information: The first 2 octets contain the 16 bit Service UUID followed by additional service data
	 */
	public static final int TYPE_SERVICE_DATA                = 0x16;


	/* MANUFACTURER SPECIFIC DATA
	 *
	 * Description: Manufacturer Specific Data (2 or more octets)
	 * Information: The first 2 octets contain the Company Identifier Code followed by additional manufacturer specific data
	 */
	public static final int TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF;

	/* Model Object Definition */
	private final int mLength;
	private final int mType;
	private final byte[] mData;


	public static final Parcelable.Creator<AdRecord> CREATOR = new Parcelable.Creator<AdRecord>() {
		public AdRecord createFromParcel(Parcel in) {
			return new AdRecord(in);
		}

		public AdRecord[] newArray(int size) {
			return new AdRecord[size];
		}
	};

	public AdRecord(int length, int type, byte[] data) {
		mLength = length;
		mType = type;
		mData = data;
	}

	public AdRecord(Parcel in) {
		final Bundle b = in.readBundle(getClass().getClassLoader());
		mLength = b.getInt(PARCEL_RECORD_LENGTH);
		mType = b.getInt(PARCEL_RECORD_TYPE);
		mData = b.getByteArray(PARCEL_RECORD_DATA);
	}

	@Override
	public int describeContents() {
		return 0;
	}

	public byte[] getData(){
		return mData;
	}

	public String getHumanReadableType(){
		return getHumanReadableAdType(mType);
	}

	public int getLength() {
		return mLength;
	}

	public int getType() {
		return mType;
	}

	@Override
	public String toString() {
		return "AdRecord [mLength=" + mLength + ", mType=" + mType + ", mData=" + Arrays.toString(mData) + ", getHumanReadableType()=" + getHumanReadableType() + "]";
	}

	@Override
	public void writeToParcel(Parcel parcel, int arg1) {
		final Bundle b = new Bundle(getClass().getClassLoader());

		b.putInt(PARCEL_RECORD_LENGTH, mLength);
		b.putInt(PARCEL_RECORD_TYPE, mType);
		b.putByteArray(PARCEL_RECORD_DATA, mData);

		parcel.writeBundle(b);
	}

	private static String getHumanReadableAdType(int type){
		switch(type){
		case TYPE_CONNECTION_INTERVAL_RANGE:
			return "Slave Connection Interval Range";
		case TYPE_DEVICE_CLASS:
			return "Class of device";
		case TYPE_FLAGS:
			return "Flags";
		case TYPE_MANUFACTURER_SPECIFIC_DATA:
			return "Manufacturer Specific Data";
		case TYPE_LOCAL_NAME_COMPLETE:
			return "Name (Complete)";
		case TYPE_LOCAL_NAME_SHORT:
			return "Name (Short)";
		case TYPE_SECURITY_MANAGER_OOB_FLAGS:
			return "Security Manager OOB Flags";
		case TYPE_SERVICE_UUIDS_LIST_128BIT:
			return "Service UUIDs (128bit)";
		case TYPE_SERVICE_UUIDS_LIST_16BIT:
			return "Service UUIDs (16bit)";
		case TYPE_SERVICE_DATA:
			return "Service Data";
		case TYPE_SIMPLE_PAIRING_HASH_C:
			return "Simple Pairing Hash C";
		case TYPE_SIMPLE_PAIRING_RANDOMIZER_R:
			return "Simple Pairing Randomizer R";
		case TYPE_TK_VALUE:
			return "TK Value";
		case TYPE_TX_POWER_LEVEL:
			return "Transmission Power Level";
		case TYPE_UUID128:
			return "Complete list of 128-bit UUIDs available";
		case TYPE_UUID128_INC:
			return "More 128-bit UUIDs available";
		case TYPE_UUID16:
			return "Complete list of 16-bit UUIDs available";
		case TYPE_UUID16_INC:
			return "More 16-bit UUIDs available";
		case TYPE_UUID32:
			return "Complete list of 32-bit UUIDs available";
		case TYPE_UUID32_INC:
			return "More 32-bit UUIDs available";
		default:
			return "Unknown AdRecord Structure: " + type;
		}
	}
}