/**
 * Copyright (C) 2010 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.
 */

#include <map>

#include <v8.h>
#include "ril.h"

#include "hardware/ril/mock-ril/src/proto/ril.pb.h"

#include "logging.h"
#include "mock_ril.h"
#include "node_buffer.h"
#include "node_object_wrap.h"
#include "protobuf_v8.h"
#include "status.h"
#include "util.h"
#include "worker.h"

#include "responses.h"

//#define RESPONSES_DEBUG
#ifdef  RESPONSES_DEBUG

#define DBG(...) ALOGD(__VA_ARGS__)

#else

#define DBG(...)

#endif


/**
 * The Buffer is assumed to be empty so nothing to convert
 * @return STATUS_OK and *data = NULL *datalen = 0;
 */
RIL_Errno RspWithNoData(
        int cmd, RIL_Token token, RIL_Errno rilErrno, Buffer *buffer) {
    DBG("RspWithNoData E");

    // Complete the request
    s_rilenv->OnRequestComplete(token, rilErrno, NULL, 0);

    DBG("RspWithNoData X");
    return rilErrno;
}

/**
 * Handle response for an array of strings
 *
 * If a string value is "*magic-null*" then that value
 * will be returned as null.
 */
RIL_Errno RspStrings(
        int cmd, RIL_Token token, RIL_Errno rilErrno, Buffer *buffer) {
    DBG("RspStrings E");

    ril_proto::RspStrings *rsp = new ril_proto::RspStrings();
    rsp->ParseFromArray(buffer->data(), buffer->length());
    int result_len = rsp->strings_size() * sizeof(const char *);
    const char **result = (const char **)alloca(result_len);
    for (int i = 0; i < rsp->strings_size();  i++) {
        result[i] = rsp->strings(i).c_str();
        DBG("result[%d]='%s'", i, result[i]);
        if (strcmp("*magic-null*", result[i]) == 0) {
            result[i] = NULL;
        }
    }

    // Complete the request
    s_rilenv->OnRequestComplete(token, rilErrno, result, result_len);

    DBG("RspStrings X rilErrno=%d", rilErrno);
    return rilErrno;
}

/**
 * Handle response for a string
 */
RIL_Errno RspString(
        int cmd, RIL_Token token, RIL_Errno rilErrno, Buffer *buffer) {
    DBG("RspString E");

    ril_proto::RspStrings *rsp = new ril_proto::RspStrings();
    rsp->ParseFromArray(buffer->data(), buffer->length());
    const char *result = rsp->strings(0).c_str();

    // Complete the request
    s_rilenv->OnRequestComplete(token, rilErrno, (void *)result, strlen(result));

    DBG("RspString X rilErrno=%d", rilErrno);
    return rilErrno;
}

/**
 * Handle response for an array of integers
 */
RIL_Errno RspIntegers(
        int cmd, RIL_Token token, RIL_Errno rilErrno, Buffer *buffer) {
    DBG("RspIntegers E");

    ril_proto::RspIntegers *rsp = new ril_proto::RspIntegers();
    rsp->ParseFromArray(buffer->data(), buffer->length());
    int result_len = rsp->integers_size() * sizeof(const int32_t);
    int32_t *result = (int32_t *)alloca(result_len);
    for (int i = 0; i < rsp->integers_size();  i++) {
        result[i] = rsp->integers(i);
        DBG("result[%d]=%d", i, result[i]);
    }

    // Complete the request
    s_rilenv->OnRequestComplete(token, rilErrno, result, result_len);

    DBG("RspIntegers X rilErrno=%d", rilErrno);
    return rilErrno;
}

/**
 * Handle RIL_REQUEST_GET_SIM_STATUS response
 */
RIL_Errno RspGetSimStatus(
        int cmd, RIL_Token token, RIL_Errno rilErrno, Buffer *buffer) { // 1
    DBG("RspGetSimStatus E");

    ril_proto::RspGetSimStatus *rsp = new ril_proto::RspGetSimStatus();
    rsp->ParseFromArray(buffer->data(), buffer->length());
    const ril_proto::RilCardStatus& r = rsp->card_status();
    RIL_CardStatus_v6 cardStatus;
    cardStatus.card_state = RIL_CardState(r.card_state());
    cardStatus.universal_pin_state = RIL_PinState(r.universal_pin_state());
    cardStatus.gsm_umts_subscription_app_index = r.gsm_umts_subscription_app_index();
    cardStatus.ims_subscription_app_index = r.ims_subscription_app_index();
    cardStatus.num_applications = r.num_applications();
    for (int i = 0; i < cardStatus.num_applications; i++) {
       cardStatus.applications[i].app_type = RIL_AppType(r.applications(i).app_type());
       cardStatus.applications[i].app_state = RIL_AppState(r.applications(i).app_state());
       cardStatus.applications[i].perso_substate =
            RIL_PersoSubstate(r.applications(i).perso_substate());
       cardStatus.applications[i].aid_ptr = const_cast<char *>(r.applications(i).aid().c_str());
       cardStatus.applications[i].app_label_ptr =
            const_cast<char *>(r.applications(i).app_label().c_str());
       cardStatus.applications[i].pin1_replaced = r.applications(i).pin1_replaced();
       cardStatus.applications[i].pin1 = RIL_PinState(r.applications(i).pin1());
       cardStatus.applications[i].pin2 = RIL_PinState(r.applications(i).pin2());
    }

    // Complete the request
    s_rilenv->OnRequestComplete(token, rilErrno,
            &cardStatus, sizeof(cardStatus));

    DBG("RspGetSimStatus X rilErrno=%d", rilErrno);
    return rilErrno;
}

/**
 * Handle RIL_REQUEST_ENTER_SIM_PIN_DATA response
 */
RIL_Errno RspEnterSimPinData(
        int cmd, RIL_Token token, RIL_Errno rilErrno, Buffer *buffer) { // 2
    DBG("RspEnterSimPinData E");

    ril_proto::RspEnterSimPin *rsp = new ril_proto::RspEnterSimPin();
    rsp->ParseFromArray(buffer->data(), buffer->length());
    DBG("retries_remaining=%d", rsp->retries_remaining());
    int retries_remaining = rsp->retries_remaining();

    // Complete the request
    s_rilenv->OnRequestComplete(token, rilErrno,
            &retries_remaining, sizeof(retries_remaining));

    DBG("RspEnterSimPinData X rilErrno=%d", rilErrno);
    return rilErrno;
}

/**
 * Handle RIL_REQUEST_GET_CURRENT_CALLS response  // 9
 */
RIL_Errno RspGetCurrentCalls (
        int cmd, RIL_Token token, RIL_Errno rilErrno, Buffer *buffer) { // 9
    DBG("RspGetCurrentCalls E");

    ril_proto::RspGetCurrentCalls *rsp = new ril_proto::RspGetCurrentCalls();
    rsp->ParseFromArray(buffer->data(), buffer->length());
    int result_len = rsp->calls_size() * sizeof(const RIL_Call *);
    DBG("RspGetCurrentCalls rilErrno=%d result_len=%d", rilErrno, result_len);
    RIL_Call **result = (RIL_Call **)alloca(result_len);
    for (int i = 0; i < rsp->calls_size();  i++) {
        const ril_proto::RilCall& srcCall = rsp->calls(i);
        RIL_Call *dstCall = (RIL_Call *)alloca(sizeof(RIL_Call));

        result[i] = dstCall;
        dstCall->state = (RIL_CallState)srcCall.state();
        dstCall->index = srcCall.index();
        dstCall->toa = srcCall.toa();
        dstCall->isMpty = (char)srcCall.is_mpty();
        dstCall->isMT = (char)srcCall.is_mt();
        dstCall->als = srcCall.als();
        dstCall->isVoice = (char)srcCall.is_voice();
        dstCall->isVoicePrivacy = (char)srcCall.is_voice_privacy();
        dstCall->number = (char *)srcCall.number().c_str();
        dstCall->numberPresentation = srcCall.number_presentation();
        dstCall->name = (char *)srcCall.name().c_str();
        dstCall->namePresentation = srcCall.name_presentation();
        if (srcCall.has_uus_info()) {
            dstCall->uusInfo =
                (RIL_UUS_Info *)alloca(sizeof(RIL_UUS_Info));
            dstCall->uusInfo->uusType =
                (RIL_UUS_Type)srcCall.uus_info().uus_type();
            dstCall->uusInfo->uusDcs =
                (RIL_UUS_DCS)srcCall.uus_info().uus_dcs();
            dstCall->uusInfo->uusLength =
                srcCall.uus_info().uus_length();
            dstCall->uusInfo->uusData =
                (char *)srcCall.uus_info().uus_data().c_str();
        } else {
            dstCall->uusInfo = NULL;
        }
    }

    // Complete the request
    s_rilenv->OnRequestComplete(token, rilErrno, result, result_len);

    DBG("RspGetCurrentCalls X rilErrno=%d", rilErrno);
    return rilErrno;
}


void unmarshallRilSignalStrength(Buffer *buffer, RIL_SignalStrength_v6 *pSignalStrength) {
    // Retrieve response from response message
    ril_proto::RspSignalStrength *rsp = new ril_proto::RspSignalStrength();
    rsp->ParseFromArray(buffer->data(), buffer->length());
    const ril_proto::RILGWSignalStrength& gwST = rsp->gw_signalstrength();
    const ril_proto::RILCDMASignalStrength& cdmaST = rsp->cdma_signalstrength();
    const ril_proto::RILEVDOSignalStrength& evdoST = rsp->evdo_signalstrength();
    const ril_proto::RILLTESignalStrength& lteST = rsp->lte_signalstrength();

    // Copy the response message from response to format defined in ril.h
    RIL_SignalStrength_v6 curSignalStrength;

    curSignalStrength.GW_SignalStrength.signalStrength = gwST.signal_strength();
    curSignalStrength.GW_SignalStrength.bitErrorRate = gwST.bit_error_rate();
    curSignalStrength.CDMA_SignalStrength.dbm = cdmaST.dbm();
    curSignalStrength.CDMA_SignalStrength.ecio = cdmaST.ecio();
    curSignalStrength.EVDO_SignalStrength.dbm = evdoST.dbm();
    curSignalStrength.EVDO_SignalStrength.ecio = evdoST.ecio();
    curSignalStrength.EVDO_SignalStrength.signalNoiseRatio = evdoST.signal_noise_ratio();
    curSignalStrength.LTE_SignalStrength.signalStrength = lteST.signal_strength();
    curSignalStrength.LTE_SignalStrength.rsrp = lteST.rsrp();
    curSignalStrength.LTE_SignalStrength.rsrq = lteST.rsrq();
    curSignalStrength.LTE_SignalStrength.rssnr = lteST.rssnr();
    curSignalStrength.LTE_SignalStrength.cqi = lteST.cqi();

    DBG("print response signal strength: ");
    DBG("gw signalstrength = %d", curSignalStrength.GW_SignalStrength.signalStrength);
    DBG("gw_bitErrorRate = %d", curSignalStrength.GW_SignalStrength.bitErrorRate);
    DBG("cdma_dbm = %d", curSignalStrength.CDMA_SignalStrength.dbm);
    DBG("cdma_ecio = %d", curSignalStrength.CDMA_SignalStrength.ecio);
    DBG("evdo_dbm = %d", curSignalStrength.EVDO_SignalStrength.dbm);
    DBG("evdo_ecio = %d", curSignalStrength.EVDO_SignalStrength.ecio);
    DBG("evdo_signalNoiseRatio = %d", curSignalStrength.EVDO_SignalStrength.signalNoiseRatio);
    DBG("lte_signalStrength = %d", curSignalStrength.LTE_SignalStrength.signalStrength);
    DBG("lte_rsrp = %d", curSignalStrength.LTE_SignalStrength.rsrp);
    DBG("lte_rsrq = %d", curSignalStrength.LTE_SignalStrength.rsrq);
    DBG("lte_rssnr = %d", curSignalStrength.LTE_SignalStrength.rssnr);
    DBG("lte_cqi = %d", curSignalStrength.LTE_SignalStrength.cqi);
}

/**
 * Handle RIL_REQUEST_SIGNAL_STRENGTH response
 */
RIL_Errno RspSignalStrength(
        int cmd, RIL_Token token, RIL_Errno rilErrno, Buffer *buffer) { // 19
    DBG("RspSignalStrength E");

    DBG("cmd = %d, token=%p, rilErrno=%d", cmd, token, rilErrno);

    // Retrieve response from response message
    ril_proto::RspSignalStrength *rsp = new ril_proto::RspSignalStrength();
    rsp->ParseFromArray(buffer->data(), buffer->length());
    const ril_proto::RILGWSignalStrength& gwST = rsp->gw_signalstrength();
    const ril_proto::RILCDMASignalStrength& cdmaST = rsp->cdma_signalstrength();
    const ril_proto::RILEVDOSignalStrength& evdoST = rsp->evdo_signalstrength();
    const ril_proto::RILLTESignalStrength& lteST = rsp->lte_signalstrength();

    // Copy the response message from response to format defined in ril.h
    RIL_SignalStrength_v6 curSignalStrength;
    unmarshallRilSignalStrength(buffer, &curSignalStrength);

    // Complete the request
    s_rilenv->OnRequestComplete(token, rilErrno, &curSignalStrength, sizeof(curSignalStrength));

    DBG("RspSignalStrength X rilErrno=%d", rilErrno);
    return rilErrno;
}

/**
 * Handle RIL_REQUEST_OPERATOR response
 */
RIL_Errno RspOperator(
        int cmd, RIL_Token token, RIL_Errno rilErrno, Buffer *buffer) { // 22
    int status;

    DBG("RspOperator E");

    ril_proto::RspOperator *rsp = new ril_proto::RspOperator();
    rsp->ParseFromArray(buffer->data(), buffer->length());
    const char *result[3] = { NULL, NULL, NULL };
    if (rsp->has_long_alpha_ons()) {
        DBG("long_alpha_ons=%s", rsp->long_alpha_ons().c_str());
        result[0] = rsp->long_alpha_ons().c_str();
    }
    if (rsp->has_short_alpha_ons()) {
        DBG("short_alpha_ons=%s", rsp->short_alpha_ons().c_str());
        result[1] = rsp->short_alpha_ons().c_str();
    }
    if (rsp->has_mcc_mnc()) {
        DBG("mcc_mnc=%s", rsp->mcc_mnc().c_str());
        result[2] = rsp->mcc_mnc().c_str();
    }

    // Complete the request
    s_rilenv->OnRequestComplete(token, rilErrno, result, sizeof(result));

    DBG("RspOperator X rilErrno=%d", rilErrno);
    return rilErrno;
}

// ----------------- Handle unsolicited response ----------------------------------------
 /**
 * Handle RIL_UNSOL_SIGNAL_STRENGTH response
 */
void UnsolRspSignalStrength(int cmd, Buffer* buffer) {

    DBG("UnsolRspSignalStrength E");
    ALOGE("unsolicited response command: %d", cmd);
    // Retrieve response from response message
    ril_proto::RspSignalStrength *rsp = new ril_proto::RspSignalStrength();
    rsp->ParseFromArray(buffer->data(), buffer->length());
    const ril_proto::RILGWSignalStrength& gwST = rsp->gw_signalstrength();
    const ril_proto::RILCDMASignalStrength& cdmaST = rsp->cdma_signalstrength();
    const ril_proto::RILEVDOSignalStrength& evdoST = rsp->evdo_signalstrength();

    // Copy the response message from response to format defined in ril.h
    RIL_SignalStrength_v6 curSignalStrength;
    unmarshallRilSignalStrength(buffer, &curSignalStrength);

    s_rilenv->OnUnsolicitedResponse(cmd, &curSignalStrength, sizeof(curSignalStrength));
    DBG("UnsolRspSignalStrength X");
}

/**
 * Maps for converting request complete and unsoliciated response
 * protobufs to ril data arrays.
 */
typedef RIL_Errno (*RspConversion)(
                int cmd, RIL_Token token, RIL_Errno rilErrno, Buffer* buffer);
typedef std::map<int, RspConversion> RspConversionMap;
RspConversionMap rilRspConversionMap;

typedef void (*UnsolRspConversion)(int cmd, Buffer* buffer);
typedef std::map<int, UnsolRspConversion> UnsolRspConversionMap;
UnsolRspConversionMap unsolRilRspConversionMap;

/**
 * Send a ril request complete response.
 */
v8::Handle<v8::Value> SendRilRequestComplete(const v8::Arguments& args) {
    DBG("SendRilRequestComplete E");
    v8::HandleScope handle_scope;
    v8::Handle<v8::Value> retValue;

    int cmd;
    RIL_Errno rilErrno;
    RIL_Token token;
    Buffer* buffer;

    /**
     * Get the arguments. There should be at least 3, cmd,
     * ril error code and token. Optionally a Buffer containing
     * the protobuf representation of the data to return.
     */
    if (args.Length() < REQUEST_COMPLETE_REQUIRED_CMDS) {
        // Expecting a cmd, ERROR and token
        ALOGE("SendRilRequestComplete X %d parameters"
             " expecting at least %d: rilErrno, cmd, and token",
                args.Length(), REQUEST_COMPLETE_REQUIRED_CMDS);
        return v8::Undefined();
    }
    v8::Handle<v8::Value> v8RilErrCode(
                    args[REQUEST_COMPLETE_RIL_ERR_CODE_INDEX]->ToObject());
    rilErrno = RIL_Errno(v8RilErrCode->NumberValue());

    v8::Handle<v8::Value> v8Cmd(
                    args[REQUEST_COMPLETE_CMD_INDEX]->ToObject());
    cmd = int(v8Cmd->NumberValue());

    v8::Handle<v8::Value> v8Token(
                    args[REQUEST_COMPLETE_TOKEN_INDEX]->ToObject());
    token = RIL_Token(int64_t(v8Token->NumberValue()));

    if (args.Length() >= (REQUEST_COMPLETE_DATA_INDEX+1)) {
        buffer = ObjectWrap::Unwrap<Buffer>(
                    args[REQUEST_COMPLETE_DATA_INDEX]->ToObject());
    } else {
        buffer = NULL;
    }

    DBG("SendRilRequestComplete: rilErrno=%d, cmd=%d, token=%p", rilErrno, cmd, token);
    RspConversionMap::iterator itr;
    itr = rilRspConversionMap.find(cmd);
    if (itr != rilRspConversionMap.end()) {
        itr->second(cmd, token, rilErrno, buffer);
    } else {
        if ((buffer == NULL) || (buffer->length() <= 0)) {
            // Nothing to convert
            rilErrno = RIL_E_SUCCESS;
        } else {
            // There was a buffer but we don't support the resonse yet.
            ALOGE("SendRilRequestComplete: No conversion routine for cmd %d,"
                    " return RIL_E_REQUEST_NOT_SUPPORTED", cmd);
            rilErrno = RIL_E_REQUEST_NOT_SUPPORTED;
        }
        // Complete the request
        s_rilenv->OnRequestComplete(token, rilErrno, NULL, 0);
    }

    DBG("SendRilRequestComplete X rillErrno=%d", rilErrno);
    return v8::Undefined();
}

/**
 * Send an unsolicited response.
 */
v8::Handle<v8::Value> SendRilUnsolicitedResponse(const v8::Arguments& args) {
    DBG("SendRilUnsolicitedResponse E");
    v8::HandleScope handle_scope;
    v8::Handle<v8::Value> retValue;

    int status;
    void *data;
    size_t datalen;

    int cmd;
    Buffer* buffer;

    /**
     * Get the cmd number and data arguments
     */
    if (args.Length() < UNSOL_RESPONSE_REQUIRED_CMDS) {
        // Expecting a cmd
        ALOGE("SendRilUnsolicitedResponse X %d parameters"
             " expecting at least a cmd",
                args.Length());
        return v8::Undefined();
    }
    v8::Handle<v8::Value> v8RilErrCode(args[UNSOL_RESPONSE_CMD_INDEX]->ToObject());
    cmd = int(v8RilErrCode->NumberValue());

    // data is optional
    if (args.Length() >= (UNSOL_RESPONSE_DATA_INDEX+1)) {
        buffer = ObjectWrap::Unwrap<Buffer>(args[UNSOL_RESPONSE_DATA_INDEX]->ToObject());
    } else {
        buffer = NULL;
    }

    UnsolRspConversionMap::iterator itr;
    itr = unsolRilRspConversionMap.find(cmd);
    if (itr != unsolRilRspConversionMap.end()) {
        itr->second(cmd, buffer);
    } else {
        if ((buffer == NULL) || (buffer->length() <= 0)) {
            // Nothing to convert
            data = NULL;
            datalen = 0;
        } else {
            // There was a buffer but we don't support the response yet.
            ALOGE("SendRilUnsolicitedResponse: No conversion routine for cmd %d,"
                    " return RIL_E_REQUEST_NOT_SUPPORTED", cmd);
            data = NULL;
            datalen = 0;
        }
        s_rilenv->OnUnsolicitedResponse(cmd, NULL, 0);
    }

    DBG("SendRilUnsolicitedResponse X");
    return v8::Undefined();
}

int responsesInit(v8::Handle<v8::Context> context) {
    ALOGD("responsesInit E");
    int status = STATUS_OK;

    rilRspConversionMap[RIL_REQUEST_GET_SIM_STATUS] = RspGetSimStatus; // 1
    rilRspConversionMap[RIL_REQUEST_ENTER_SIM_PIN] = RspEnterSimPinData; // 2
    rilRspConversionMap[RIL_REQUEST_GET_CURRENT_CALLS] = RspGetCurrentCalls; // 9
    rilRspConversionMap[RIL_REQUEST_GET_IMSI] = RspString; // 11
    rilRspConversionMap[RIL_REQUEST_HANGUP] = RspWithNoData; // 12
    rilRspConversionMap[RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND] = RspWithNoData; // 13
    rilRspConversionMap[RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND] = RspWithNoData; // 14
    rilRspConversionMap[RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE] = RspWithNoData; // 15
    rilRspConversionMap[RIL_REQUEST_CONFERENCE] = RspWithNoData;  // 16
    rilRspConversionMap[RIL_REQUEST_LAST_CALL_FAIL_CAUSE] = RspIntegers;  // 18
    rilRspConversionMap[RIL_REQUEST_SIGNAL_STRENGTH] = RspSignalStrength; // 19
    rilRspConversionMap[RIL_REQUEST_VOICE_REGISTRATION_STATE] = RspStrings; // 20
    rilRspConversionMap[RIL_REQUEST_DATA_REGISTRATION_STATE] = RspStrings; // 21
    rilRspConversionMap[RIL_REQUEST_OPERATOR] = RspOperator; // 22
    rilRspConversionMap[RIL_REQUEST_GET_IMEI] = RspString; // 38
    rilRspConversionMap[RIL_REQUEST_GET_IMEISV] = RspString; // 39
    rilRspConversionMap[RIL_REQUEST_ANSWER] = RspWithNoData; // 39
    rilRspConversionMap[RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE] = RspIntegers; // 45
    rilRspConversionMap[RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC] = RspWithNoData; // 46
    rilRspConversionMap[RIL_REQUEST_BASEBAND_VERSION] = RspString; // 51
    rilRspConversionMap[RIL_REQUEST_SEPARATE_CONNECTION] = RspWithNoData;  // 52
    rilRspConversionMap[RIL_REQUEST_SET_MUTE] = RspWithNoData;  // 53
    rilRspConversionMap[RIL_REQUEST_SCREEN_STATE] = RspWithNoData; // 61

    unsolRilRspConversionMap[RIL_UNSOL_SIGNAL_STRENGTH] = UnsolRspSignalStrength;  // 1009


    ALOGD("responsesInit X: status=%d", status);
    return STATUS_OK;
}