package com.android.server.wifi.anqp;

import java.net.ProtocolException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;

/**
 * ANQP related constants (802.11-2012)
 */
public class Constants {

    public static final int NIBBLE_MASK = 0x0f;
    public static final int BYTE_MASK = 0xff;
    public static final int SHORT_MASK = 0xffff;
    public static final long INT_MASK = 0xffffffffL;
    public static final int BYTES_IN_SHORT = 2;
    public static final int BYTES_IN_INT = 4;
    public static final int BYTES_IN_EUI48 = 6;

    public static final int HS20_PREFIX = 0x119a6f50;   // Note that this is represented as a LE int
    public static final int HS20_FRAME_PREFIX = 0x109a6f50;
    public static final int UTF8_INDICATOR = 1;

    public static final int ANQP_QUERY_LIST = 256;
    public static final int ANQP_CAPABILITY_LIST = 257;
    public static final int ANQP_VENUE_NAME = 258;
    public static final int ANQP_EMERGENCY_NUMBER = 259;
    public static final int ANQP_NWK_AUTH_TYPE = 260;
    public static final int ANQP_ROAMING_CONSORTIUM = 261;
    public static final int ANQP_IP_ADDR_AVAILABILITY = 262;
    public static final int ANQP_NAI_REALM = 263;
    public static final int ANQP_3GPP_NETWORK = 264;
    public static final int ANQP_GEO_LOC = 265;
    public static final int ANQP_CIVIC_LOC = 266;
    public static final int ANQP_LOC_URI = 267;
    public static final int ANQP_DOM_NAME = 268;
    public static final int ANQP_EMERGENCY_ALERT = 269;
    public static final int ANQP_TDLS_CAP = 270;
    public static final int ANQP_EMERGENCY_NAI = 271;
    public static final int ANQP_NEIGHBOR_REPORT = 272;
    public static final int ANQP_VENDOR_SPEC = 56797;

    public static final int HS_QUERY_LIST = 1;
    public static final int HS_CAPABILITY_LIST = 2;
    public static final int HS_FRIENDLY_NAME = 3;
    public static final int HS_WAN_METRICS = 4;
    public static final int HS_CONN_CAPABILITY = 5;
    public static final int HS_NAI_HOME_REALM_QUERY = 6;
    public static final int HS_OPERATING_CLASS = 7;
    public static final int HS_OSU_PROVIDERS = 8;
    public static final int HS_ICON_REQUEST = 10;
    public static final int HS_ICON_FILE = 11;

    public enum ANQPElementType {
        ANQPQueryList,
        ANQPCapabilityList,
        ANQPVenueName,
        ANQPEmergencyNumber,
        ANQPNwkAuthType,
        ANQPRoamingConsortium,
        ANQPIPAddrAvailability,
        ANQPNAIRealm,
        ANQP3GPPNetwork,
        ANQPGeoLoc,
        ANQPCivicLoc,
        ANQPLocURI,
        ANQPDomName,
        ANQPEmergencyAlert,
        ANQPTDLSCap,
        ANQPEmergencyNAI,
        ANQPNeighborReport,
        ANQPVendorSpec,
        HSQueryList,
        HSCapabilityList,
        HSFriendlyName,
        HSWANMetrics,
        HSConnCapability,
        HSNAIHomeRealmQuery,
        HSOperatingclass,
        HSOSUProviders,
        HSIconRequest,
        HSIconFile
    }

    private static final Map<Integer, ANQPElementType> sAnqpMap = new HashMap<Integer, ANQPElementType>();
    private static final Map<Integer, ANQPElementType> sHs20Map = new HashMap<Integer, ANQPElementType>();
    private static final Map<ANQPElementType, Integer> sRevAnqpmap = new HashMap<ANQPElementType, Integer>();
    private static final Map<ANQPElementType, Integer> sRevHs20map = new HashMap<ANQPElementType, Integer>();

    static {
        sAnqpMap.put(ANQP_QUERY_LIST, ANQPElementType.ANQPQueryList);
        sAnqpMap.put(ANQP_CAPABILITY_LIST, ANQPElementType.ANQPCapabilityList);
        sAnqpMap.put(ANQP_VENUE_NAME, ANQPElementType.ANQPVenueName);
        sAnqpMap.put(ANQP_EMERGENCY_NUMBER, ANQPElementType.ANQPEmergencyNumber);
        sAnqpMap.put(ANQP_NWK_AUTH_TYPE, ANQPElementType.ANQPNwkAuthType);
        sAnqpMap.put(ANQP_ROAMING_CONSORTIUM, ANQPElementType.ANQPRoamingConsortium);
        sAnqpMap.put(ANQP_IP_ADDR_AVAILABILITY, ANQPElementType.ANQPIPAddrAvailability);
        sAnqpMap.put(ANQP_NAI_REALM, ANQPElementType.ANQPNAIRealm);
        sAnqpMap.put(ANQP_3GPP_NETWORK, ANQPElementType.ANQP3GPPNetwork);
        sAnqpMap.put(ANQP_GEO_LOC, ANQPElementType.ANQPGeoLoc);
        sAnqpMap.put(ANQP_CIVIC_LOC, ANQPElementType.ANQPCivicLoc);
        sAnqpMap.put(ANQP_LOC_URI, ANQPElementType.ANQPLocURI);
        sAnqpMap.put(ANQP_DOM_NAME, ANQPElementType.ANQPDomName);
        sAnqpMap.put(ANQP_EMERGENCY_ALERT, ANQPElementType.ANQPEmergencyAlert);
        sAnqpMap.put(ANQP_TDLS_CAP, ANQPElementType.ANQPTDLSCap);
        sAnqpMap.put(ANQP_EMERGENCY_NAI, ANQPElementType.ANQPEmergencyNAI);
        sAnqpMap.put(ANQP_NEIGHBOR_REPORT, ANQPElementType.ANQPNeighborReport);
        sAnqpMap.put(ANQP_VENDOR_SPEC, ANQPElementType.ANQPVendorSpec);

        sHs20Map.put(HS_QUERY_LIST, ANQPElementType.HSQueryList);
        sHs20Map.put(HS_CAPABILITY_LIST, ANQPElementType.HSCapabilityList);
        sHs20Map.put(HS_FRIENDLY_NAME, ANQPElementType.HSFriendlyName);
        sHs20Map.put(HS_WAN_METRICS, ANQPElementType.HSWANMetrics);
        sHs20Map.put(HS_CONN_CAPABILITY, ANQPElementType.HSConnCapability);
        sHs20Map.put(HS_NAI_HOME_REALM_QUERY, ANQPElementType.HSNAIHomeRealmQuery);
        sHs20Map.put(HS_OPERATING_CLASS, ANQPElementType.HSOperatingclass);
        sHs20Map.put(HS_OSU_PROVIDERS, ANQPElementType.HSOSUProviders);
        sHs20Map.put(HS_ICON_REQUEST, ANQPElementType.HSIconRequest);
        sHs20Map.put(HS_ICON_FILE, ANQPElementType.HSIconFile);

        for (Map.Entry<Integer, ANQPElementType> entry : sAnqpMap.entrySet()) {
            sRevAnqpmap.put(entry.getValue(), entry.getKey());
        }
        for (Map.Entry<Integer, ANQPElementType> entry : sHs20Map.entrySet()) {
            sRevHs20map.put(entry.getValue(), entry.getKey());
        }
    }

    public static ANQPElementType mapANQPElement(int id) {
        return sAnqpMap.get(id);
    }

    public static ANQPElementType mapHS20Element(int id) {
        return sHs20Map.get(id);
    }

    public static Integer getANQPElementID(ANQPElementType elementType) {
        return sRevAnqpmap.get(elementType);
    }

    public static Integer getHS20ElementID(ANQPElementType elementType) {
        return sRevHs20map.get(elementType);
    }

    public static long getInteger(ByteBuffer payload, ByteOrder bo, int size) {
        byte[] octets = new byte[size];
        payload.get(octets);
        long value = 0;
        if (bo == ByteOrder.LITTLE_ENDIAN) {
            for (int n = octets.length - 1; n >= 0; n--) {
                value = (value << Byte.SIZE) | (octets[n] & BYTE_MASK);
            }
        }
        else {
            for (byte octet : octets) {
                value = (value << Byte.SIZE) | (octet & BYTE_MASK);
            }
        }
        return value;
    }

    public static String getPrefixedString(ByteBuffer payload, int lengthLength, Charset charset)
            throws ProtocolException {
        return getPrefixedString(payload, lengthLength, charset, false);
    }

    public static String getPrefixedString(ByteBuffer payload, int lengthLength, Charset charset,
                                           boolean useNull) throws ProtocolException {
        if (payload.remaining() < lengthLength) {
            throw new ProtocolException("Runt string: " + payload.remaining());
        }
        return getString(payload, (int) getInteger(payload, ByteOrder.LITTLE_ENDIAN,
                lengthLength), charset, useNull);
    }

    public static String getString(ByteBuffer payload, int length, Charset charset)
            throws ProtocolException {
        return getString(payload, length, charset, false);
    }

    public static String getString(ByteBuffer payload, int length, Charset charset, boolean useNull)
            throws ProtocolException {
        if (length > payload.remaining()) {
            throw new ProtocolException("Bad string length: " + length);
        }
        if (useNull && length == 0) {
            return null;
        }
        byte[] octets = new byte[length];
        payload.get(octets);
        return new String(octets, charset);
    }
}
