package com.google.android.iwlan.epdg;

import android.support.annotation.NonNull;
import android.telephony.data.NetworkSliceInfo;
import android.util.Log;

import java.util.Arrays;
import java.util.List;

public class NetworkSliceSelectionAssistanceInformation {
    private static final String TAG =
            NetworkSliceSelectionAssistanceInformation.class.getSimpleName();
    static final List<Integer> VALID_LENGTHS = Arrays.asList(1, 2, 4, 5, 8);

    public static NetworkSliceInfo getSliceInfo(byte[] snssai) {
        if (snssai == null) {
            return null;
        }
        /*
         * From 3GPP TS 24.501 Section 9.11.2.8, Content structure of the Value of S-NSSAI
         *
         * <p>Slice Service Type - 1 byte
         * Slice Differentiator - 3 bytes
         * Mapped HPLMN Slice Service Type - 1 byte
         * Mapped HPLMN Slice Differentiator - 3 bytes
         *
         * <p>Depending on the value of the length field the following S-NSSAI contents are
         * included:
         * Bits 8 7 6 5 4 3 2 1
         * 0 0 0 0 0 0 0 1 SST
         * 0 0 0 0 0 0 1 0 SST and mapped HPLMN SST
         * 0 0 0 0 0 1 0 0 SST and SD
         * 0 0 0 0 0 1 0 1 SST, SD and mapped HPLMN SST
         * 0 0 0 0 1 0 0 0 SST, SD, mapped HPLMN SST and mapped HPLMN SD
         * All other values are reserved.
         */
        NetworkSliceInfo.Builder siBuilder = new NetworkSliceInfo.Builder();
        int len = snssai.length;
        // validity checks. See TS 24.501 Section 9.11.2.8
        // length should be valid
        if (!VALID_LENGTHS.contains(len)) {
            Log.d(TAG, "Invalid S-NSSAI length : " + snssai.length);
            return null;
        }

        switch (len) {
            case 1 -> // get SST
                    siBuilder.setSliceServiceType(getSST(snssai, 0));
            case 2 -> { // get SST and mapped SST
                siBuilder.setSliceServiceType(getSST(snssai, 0));
                siBuilder.setMappedHplmnSliceServiceType(getSST(snssai, 1));
            }
            case 4 -> { // get SST and SD
                siBuilder.setSliceServiceType(getSST(snssai, 0));
                siBuilder.setSliceDifferentiator(getSD(snssai, 1));
            }
            case 5 -> { // get SST, SD and mapped SST
                siBuilder.setSliceServiceType(getSST(snssai, 0));
                siBuilder.setSliceDifferentiator(getSD(snssai, 1));
                siBuilder.setMappedHplmnSliceServiceType(getSST(snssai, 4));
            }
            case 8 -> { // get SST, SD, mapped SST, mapped SD
                siBuilder.setSliceServiceType(getSST(snssai, 0));
                siBuilder.setSliceDifferentiator(getSD(snssai, 1));
                siBuilder.setMappedHplmnSliceServiceType(getSST(snssai, 4));
                siBuilder.setMappedHplmnSliceDifferentiator(getSD(snssai, 5));
            }
        }

        return siBuilder.build();
    }

    private static int getSST(@NonNull byte[] snssai, int offset) {
        if (offset < 0 || snssai.length < offset + 1) {
            return NetworkSliceInfo.SLICE_SERVICE_TYPE_NONE;
        }
        /*
         * From 3GPP TS 23.003: Values 0 to 127 belong to the standardized SST range and they are
         * defined in 3GPP TS 23.501. Values 128 to 255 belong to the Operator-specific range
         */
        return Byte.toUnsignedInt(snssai[offset]);
    }

    private static int getSD(byte[] snssai, int offset) {
        /*
         * Slice Descriptor is 3 bytes long
         * The SD field has a reserved value "no SD value associated with the SST"
         * defined as hexadecimal FFFFFF
         */
        if (offset >= 0 && snssai.length >= offset + 3) {
            // Constructs the 24-bit integer from 3 bytes (Big-Endian/Network Order)
            return ((snssai[offset] & 0xFF) << 16)
                    | ((snssai[offset + 1] & 0xFF) << 8)
                    | ((snssai[offset + 2] & 0xFF));
        }
        return NetworkSliceInfo.SLICE_SERVICE_TYPE_NONE;
    }
}
