package com.android.server.wifi.anqp;

import java.net.ProtocolException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

import static com.android.server.wifi.anqp.Constants.BYTE_MASK;
import static com.android.server.wifi.anqp.Constants.SHORT_MASK;

/**
 * An OSU Provider, as specified in
 * Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00,
 * section 4.8.1
 */
public class OSUProvider {

    public enum OSUMethod {OmaDm, SoapXml}

    private final List<I18Name> mNames;
    private final String mOSUServer;
    private final List<OSUMethod> mOSUMethods;
    private final List<IconInfo> mIcons;
    private final String mOsuNai;
    private final List<I18Name> mServiceDescriptions;
    private final int mHashCode;

    public OSUProvider(ByteBuffer payload) throws ProtocolException {
        if (payload.remaining() < 11) {
            throw new ProtocolException("Truncated OSU provider: " + payload.remaining());
        }

        int length = payload.getShort() & SHORT_MASK;
        int namesLength = payload.getShort() & SHORT_MASK;

        ByteBuffer namesBuffer = payload.duplicate().order(ByteOrder.LITTLE_ENDIAN);
        namesBuffer.limit(namesBuffer.position() + namesLength);
        payload.position(payload.position() + namesLength);

        mNames = new ArrayList<>();

        while (namesBuffer.hasRemaining()) {
            mNames.add(new I18Name(namesBuffer));
        }

        mOSUServer = Constants.getPrefixedString(payload, 1, StandardCharsets.UTF_8);
        int methodLength = payload.get() & BYTE_MASK;
        mOSUMethods = new ArrayList<>(methodLength);
        while (methodLength > 0) {
            int methodID = payload.get() & BYTE_MASK;
            mOSUMethods.add(methodID < OSUMethod.values().length ?
                    OSUMethod.values()[methodID] :
                    null);
            methodLength--;
        }

        int iconsLength = payload.getShort() & SHORT_MASK;
        ByteBuffer iconsBuffer = payload.duplicate().order(ByteOrder.LITTLE_ENDIAN);
        iconsBuffer.limit(iconsBuffer.position() + iconsLength);
        payload.position(payload.position() + iconsLength);

        mIcons = new ArrayList<>();

        while (iconsBuffer.hasRemaining()) {
            mIcons.add(new IconInfo(iconsBuffer));
        }

        mOsuNai = Constants.getPrefixedString(payload, 1, StandardCharsets.UTF_8, true);

        int descriptionsLength = payload.getShort() & SHORT_MASK;
        ByteBuffer descriptionsBuffer = payload.duplicate().order(ByteOrder.LITTLE_ENDIAN);
        descriptionsBuffer.limit(descriptionsBuffer.position() + descriptionsLength);
        payload.position(payload.position() + descriptionsLength);

        mServiceDescriptions = new ArrayList<>();

        while (descriptionsBuffer.hasRemaining()) {
            mServiceDescriptions.add(new I18Name(descriptionsBuffer));
        }

        int result = mNames.hashCode();
        result = 31 * result + mOSUServer.hashCode();
        result = 31 * result + mOSUMethods.hashCode();
        result = 31 * result + mIcons.hashCode();
        result = 31 * result + (mOsuNai != null ? mOsuNai.hashCode() : 0);
        result = 31 * result + mServiceDescriptions.hashCode();
        mHashCode = result;
    }

    public List<I18Name> getNames() {
        return mNames;
    }

    public String getOSUServer() {
        return mOSUServer;
    }

    public List<OSUMethod> getOSUMethods() {
        return mOSUMethods;
    }

    public List<IconInfo> getIcons() {
        return mIcons;
    }

    public String getOsuNai() {
        return mOsuNai;
    }

    public List<I18Name> getServiceDescriptions() {
        return mServiceDescriptions;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        OSUProvider that = (OSUProvider) o;

        if (!mOSUServer.equals(that.mOSUServer)) return false;
        if (!mNames.equals(that.mNames)) return false;
        if (!mServiceDescriptions.equals(that.mServiceDescriptions)) return false;
        if (!mIcons.equals(that.mIcons)) return false;
        if (!mOSUMethods.equals(that.mOSUMethods)) return false;
        if (mOsuNai != null ? !mOsuNai.equals(that.mOsuNai) : that.mOsuNai != null) return false;

        return true;
    }

    @Override
    public int hashCode() {
        return mHashCode;
    }

    @Override
    public String toString() {
        return "OSUProvider{" +
                "names=" + mNames +
                ", OSUServer='" + mOSUServer + '\'' +
                ", OSUMethods=" + mOSUMethods +
                ", icons=" + mIcons +
                ", NAI='" + mOsuNai + '\'' +
                ", serviceDescriptions=" + mServiceDescriptions +
                '}';
    }
}
