/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.server.uwb;

import android.os.PersistableBundle;
import android.uwb.RangingChangeReason;

import com.android.server.uwb.data.UwbUciConstants;

import com.google.uwb.support.aliro.AliroParams;
import com.google.uwb.support.aliro.AliroRangingError;
import com.google.uwb.support.base.Params;
import com.google.uwb.support.ccc.CccParams;
import com.google.uwb.support.ccc.CccRangingError;
import com.google.uwb.support.fira.FiraStatusCode;

public class UwbSessionNotificationHelper {
    public static int convertUciReasonCodeToApiReasonCode(int reasonCode) {
        /* set default */
        int rangingChangeReason = RangingChangeReason.UNKNOWN;
        switch (reasonCode) {
            case UwbUciConstants.REASON_STATE_CHANGE_WITH_SESSION_MANAGEMENT_COMMANDS:
                rangingChangeReason = RangingChangeReason.LOCAL_API;
                break;
            case UwbUciConstants.REASON_MAX_RANGING_ROUND_RETRY_COUNT_REACHED:
                rangingChangeReason = RangingChangeReason.MAX_RR_RETRY_REACHED;
                break;
            case UwbUciConstants.REASON_MAX_NUMBER_OF_MEASUREMENTS_REACHED:
            case UwbUciConstants.REASON_SESSION_STOPPED_DUE_TO_MAX_STS_INDEX_VALUE:
                rangingChangeReason = RangingChangeReason.REMOTE_REQUEST;
                break;
            case UwbUciConstants.REASON_ERROR_INSUFFICIENT_SLOTS_PER_RR:
            case UwbUciConstants.REASON_ERROR_SLOT_LENGTH_NOT_SUPPORTED:
            case UwbUciConstants.REASON_ERROR_INVALID_UL_TDOA_RANDOM_WINDOW:
            case UwbUciConstants.REASON_ERROR_MAC_ADDRESS_MODE_NOT_SUPPORTED:
            case UwbUciConstants.REASON_ERROR_INVALID_RANGING_INTERVAL:
            case UwbUciConstants.REASON_ERROR_INVALID_STS_CONFIG:
            case UwbUciConstants.REASON_ERROR_INVALID_RFRAME_CONFIG:
            case UwbUciConstants.REASON_ERROR_HUS_NOT_ENOUGH_SLOTS:
            case UwbUciConstants.REASON_ERROR_HUS_CFP_PHASE_TOO_SHORT:
            case UwbUciConstants.REASON_ERROR_HUS_CAP_PHASE_TOO_SHORT:
            case UwbUciConstants.REASON_ERROR_HUS_OTHERS:
                rangingChangeReason = RangingChangeReason.BAD_PARAMETERS;
                break;
            case UwbUciConstants.REASON_ERROR_SESSION_KEY_NOT_FOUND:
            case UwbUciConstants.REASON_ERROR_SUB_SESSION_KEY_NOT_FOUND:
                rangingChangeReason = RangingChangeReason.PROTOCOL_SPECIFIC;
                break;
            case UwbUciConstants.REASON_REGULATION_UWB_OFF:
                rangingChangeReason = RangingChangeReason.SYSTEM_REGULATION;
                break;
            case UwbUciConstants.REASON_SESSION_RESUMED_DUE_TO_INBAND_SIGNAL:
                rangingChangeReason = RangingChangeReason.SESSION_RESUMED;
                break;
            case UwbUciConstants.REASON_SESSION_SUSPENDED_DUE_TO_INBAND_SIGNAL:
                rangingChangeReason = RangingChangeReason.SESSION_SUSPENDED;
                break;
            case UwbUciConstants.REASON_SESSION_STOPPED_DUE_TO_INBAND_SIGNAL:
                rangingChangeReason = RangingChangeReason.INBAND_SESSION_STOP;
                break;
        }
        return rangingChangeReason;
    }

    public static int convertUciStatusToApiReasonCode(int status) {
        /* set default */
        int rangingChangeReason = RangingChangeReason.UNKNOWN;
        switch (status) {
            case UwbUciConstants.STATUS_CODE_OK:
                rangingChangeReason = RangingChangeReason.LOCAL_API;
                break;
            case UwbUciConstants.STATUS_CODE_ERROR_MAX_SESSIONS_EXCEEDED:
                rangingChangeReason = RangingChangeReason.MAX_SESSIONS_REACHED;
                break;
            case UwbUciConstants.STATUS_CODE_INVALID_PARAM:
            case UwbUciConstants.STATUS_CODE_INVALID_RANGE:
            case UwbUciConstants.STATUS_CODE_INVALID_MESSAGE_SIZE:
                rangingChangeReason = RangingChangeReason.BAD_PARAMETERS;
                break;
            case UwbUciConstants.STATUS_CODE_ERROR_SESSION_NOT_EXIST:
            case UwbUciConstants.STATUS_CODE_CCC_LIFECYCLE:
            case UwbUciConstants.STATUS_CODE_CCC_SE_BUSY:
                rangingChangeReason = RangingChangeReason.PROTOCOL_SPECIFIC;
                break;
            case UwbUciConstants.REASON_ERROR_INSUFFICIENT_SLOTS_PER_RR:
                rangingChangeReason = RangingChangeReason.INSUFFICIENT_SLOTS_PER_RR;
                break;
        }
        return rangingChangeReason;
    }

    /**
     * Convert UCI status code received in SESSION_DATA_TRANSFER_PHASE_CONFIGURATION_NTF to
     * an API reason code.
     */
    public static int convertDataTransferPhaseConfigStatusToApiReasonCode(
            int dataTransferPhaseConfigStatus) {
        int rangingChangeReason = RangingChangeReason.UNKNOWN;
        switch (dataTransferPhaseConfigStatus) {
            case UwbUciConstants.STATUS_CODE_DATA_TRANSFER_PHASE_CONFIG_DTPCM_CONFIG_SUCCESS:
                rangingChangeReason = RangingChangeReason.LOCAL_API;
                break;
            case UwbUciConstants.STATUS_CODE_DATA_TRANSFER_PHASE_CONFIG_ERROR_DUPLICATE_SLOT_ASSIGNMENT:
                rangingChangeReason = RangingChangeReason.PROTOCOL_SPECIFIC;
                break;
        }
        return rangingChangeReason;
    }

    /**
     * Convert Multicast list update status codes to an API reason code.
     */
    public static int convertMulticastListUpdateStatusToApiReasonCode(
            int multicastListUpdateStatus) {
        int rangingChangeReason = RangingChangeReason.UNKNOWN;
        switch (multicastListUpdateStatus) {
            case UwbUciConstants.MULTICAST_LIST_UPDATE_STATUS_OK:
                rangingChangeReason = RangingChangeReason.LOCAL_API;
                break;
            case UwbUciConstants.MULTICAST_LIST_UPDATE_STATUS_ERROR_FULL:
            case UwbUciConstants.MULTICAST_LIST_UPDATE_STATUS_ERROR_KEY_FETCH_FAIL:
                rangingChangeReason = RangingChangeReason.PROTOCOL_SPECIFIC;
                break;
            case UwbUciConstants.MULTICAST_LIST_UPDATE_STATUS_ERROR_SUB_SESSION_ID_NOT_FOUND:
            case UwbUciConstants.MULTICAST_LIST_UPDATE_STATUS_ERROR_SUB_SESSION_KEY_NOT_FOUND:
            case UwbUciConstants.MULTICAST_LIST_UPDATE_STATUS_ERROR_SUB_SESSION_KEY_NOT_APPLICABLE:
            case UwbUciConstants.MULTICAST_LIST_UPDATE_STATUS_ERROR_SESSION_KEY_NOT_FOUND:
            case UwbUciConstants.MULTICAST_LIST_UPDATE_STATUS_ERROR_ADDRESS_NOT_FOUND:
            case UwbUciConstants.MULTICAST_LIST_UPDATE_STATUS_ERROR_ADDRESS_ALREADY_PRESENT:
                rangingChangeReason = RangingChangeReason.BAD_PARAMETERS;
                break;
        }
        return rangingChangeReason;
    }

    /**
     * Convert UCI reason code values to UCI status code, as some of the callbacks expect to get
     * the latter.
     */
    public static int convertUciReasonCodeToUciStatusCode(int reasonCode) {
        int statusCode = UwbUciConstants.STATUS_CODE_FAILED;
        switch (reasonCode) {
            case UwbUciConstants.REASON_STATE_CHANGE_WITH_SESSION_MANAGEMENT_COMMANDS:
                statusCode = UwbUciConstants.STATUS_CODE_OK;
                break;
            case UwbUciConstants.REASON_ERROR_SESSION_KEY_NOT_FOUND:
            case UwbUciConstants.REASON_ERROR_SUB_SESSION_KEY_NOT_FOUND:
                statusCode = UwbUciConstants.STATUS_CODE_ERROR_SESSION_NOT_EXIST;
                break;
        }
        return statusCode;
    }

    private static @CccParams.ProtocolError int convertUciStatusToApiCccProtocolError(int status) {
        switch (status) {
            case UwbUciConstants.STATUS_CODE_ERROR_SESSION_NOT_EXIST:
                return CccParams.PROTOCOL_ERROR_NOT_FOUND;
            case UwbUciConstants.STATUS_CODE_CCC_LIFECYCLE:
                return CccParams.PROTOCOL_ERROR_LIFECYCLE;
            case UwbUciConstants.STATUS_CODE_CCC_SE_BUSY:
                return CccParams.PROTOCOL_ERROR_SE_BUSY;
            default:
                return CccParams.PROTOCOL_ERROR_UNKNOWN;
        }
    }

    private static @AliroParams.ProtocolError int convertUciStatusToApiAliroProtocolError(
            int status) {
        switch (status) {
            case UwbUciConstants.STATUS_CODE_ERROR_SESSION_NOT_EXIST:
                return AliroParams.PROTOCOL_ERROR_NOT_FOUND;
            default:
                return AliroParams.PROTOCOL_ERROR_UNKNOWN;
        }
    }

    public static PersistableBundle convertUciStatusToParam(String protocolName, int status) {
        Params c;
        if (protocolName.equals(CccParams.PROTOCOL_NAME)) {
            c = new CccRangingError.Builder()
                    .setError(convertUciStatusToApiCccProtocolError(status))
                    .build();
        } else if (protocolName.equals(AliroParams.PROTOCOL_NAME)) {
            c = new AliroRangingError.Builder()
                    .setError(convertUciStatusToApiAliroProtocolError(status))
                    .build();
        } else {
            c = new FiraStatusCode.Builder().setStatusCode(status).build();
        }
        return c.toBundle();
    }

    static String getSessionStateString(int state) {
        String ret = "";
        switch (state) {
            case UwbUciConstants.UWB_SESSION_STATE_INIT:
                ret = "INIT";
                break;
            case UwbUciConstants.UWB_SESSION_STATE_DEINIT:
                ret = "DEINIT";
                break;
            case UwbUciConstants.UWB_SESSION_STATE_ACTIVE:
                ret = "ACTIVE";
                break;
            case UwbUciConstants.UWB_SESSION_STATE_IDLE:
                ret = "IDLE";
                break;
            case UwbUciConstants.UWB_SESSION_STATE_ERROR:
                ret = "ERROR";
                break;
        }
        return ret;
    }
}
