/*
 * Copyright (C) 2023 Huawei Device Co., Ltd.
 * 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 <regex>
#include <sstream>

#include "gsm_sms_param_decode.h"

#include "gsm_pdu_hex_value.h"
#include "gsm_sms_common_utils.h"
#include "gsm_user_data_pdu.h"
#include "securec.h"
#include "telephony_log_wrapper.h"
#include "text_coder.h"

namespace OHOS {
namespace Telephony {
static constexpr uint8_t SLIDE_DATA_STEP = 2;
static constexpr uint8_t BCD_TO_DIGITAL = 2;
static constexpr uint8_t MATCH_INDEX_ONE = 1;
static constexpr uint8_t MATCH_INDEX_TWO = 2;
static constexpr uint8_t MATCH_INDEX_THREE = 3;
static constexpr uint8_t MATCH_INDEX_FOUR = 4;
static constexpr uint8_t MATCH_INDEX_FIVE = 5;

bool GsmSmsParamDecode::DecodeAddressPdu(SmsReadBuffer &buffer, struct AddressNumber *resultNum)
{
    if (resultNum == nullptr) {
        TELEPHONY_LOGE("nullptr error");
        return false;
    }
    if (memset_s(resultNum->address, sizeof(resultNum->address), 0x00, sizeof(resultNum->address)) != EOK) {
        TELEPHONY_LOGE("memset_s error!");
        return false;
    }

    uint8_t oneByte = 0;
    if (!buffer.ReadByte(oneByte) || oneByte >= BCD_TO_DIGITAL * (MAX_ADDRESS_LEN + 1)) {
        TELEPHONY_LOGE("get data error. %{public}d", oneByte);
        return false;
    }
    uint8_t addrLen = oneByte;
    uint8_t bcdLen = 0;
    if (addrLen % SLIDE_DATA_STEP == 0) {
        bcdLen = addrLen / SLIDE_DATA_STEP;
    } else {
        bcdLen = addrLen / SLIDE_DATA_STEP + 1;
    }

    if (!buffer.PickOneByte(oneByte)) {
        TELEPHONY_LOGE("get data error.");
        return false;
    }
    resultNum->ton = (oneByte & HEX_VALUE_70) >> HEX_VALUE_04;

    if (resultNum->ton == TYPE_ALPHA_NUMERIC) {
        return DecodeAddressAlphaNum(buffer, resultNum, bcdLen, addrLen);
    } else if (resultNum->ton == TYPE_INTERNATIONAL) {
        return DecodeAddressInternationalNum(buffer, resultNum, bcdLen);
    } else {
        return DecodeAddressDefaultNum(buffer, resultNum, bcdLen);
    }
}

bool GsmSmsParamDecode::DecodeAddressAlphaNum(
    SmsReadBuffer &buffer, struct AddressNumber *resultNum, uint8_t bcdLen, uint8_t addrLen)
{
    uint8_t tmresultNum[MAX_ADDRESS_LEN] = { 0 };
    uint8_t dataLen = (addrLen * HEX_VALUE_04) / HEX_VALUE_07;
    TELEPHONY_LOGI("DecodeAddressAlphaNum, addrLen:%{public}d, dataLen: %{public}d", addrLen, dataLen);
    uint8_t addressPduArr[MAX_ADDRESS_LEN + 1] = { 0 };
    uint8_t oneByte = 0;
    if (!buffer.ReadByte(oneByte)) {
        TELEPHONY_LOGE("get data error.");
        return false;
    }
    uint8_t encodeLen = (addrLen % HEX_VALUE_02 == 0) ? (addrLen / HEX_VALUE_02) :
        (addrLen / HEX_VALUE_02 + HEX_VALUE_01);
    TELEPHONY_LOGI("DecodeAddressAlphaNum, encodeLen %{public}d", encodeLen);
    for (uint8_t i = 0; i < encodeLen; i++) {
        if (!buffer.ReadByte(oneByte)) {
            TELEPHONY_LOGE("get data error.");
            return false;
        }
        addressPduArr[i] = oneByte;
    }
    if (!GsmSmsCommonUtils::Unpack7bitCharForMiddlePart(addressPduArr, dataLen, tmresultNum)) {
        TELEPHONY_LOGE("unpack 7bit char error!");
        return false;
    }
    MsgLangInfo langInfo;
    langInfo.bSingleShift = false;
    langInfo.bLockingShift = false;
    TextCoder::Instance().Gsm7bitToUtf8(
        reinterpret_cast<uint8_t *>(resultNum->address), MAX_ADDRESS_LEN, tmresultNum, dataLen, langInfo);
    return true;
}

bool GsmSmsParamDecode::DecodeAddressInternationalNum(
    SmsReadBuffer &buffer, struct AddressNumber *resultNum, uint8_t bcdLen)
{
    buffer.MoveForward(1);
    uint8_t oneByte = 0;
    uint8_t index = 0;
    uint8_t bcdArr[MAX_ADDRESS_LEN + 1] = { 0 };
    while (index < bcdLen && index < MAX_ADDRESS_LEN) {
        if (!buffer.ReadByte(oneByte)) {
            TELEPHONY_LOGE("get data error.");
            return false;
        }
        bcdArr[index++] = oneByte;
    }

    GsmSmsCommonUtils utils;
    std::string addrNum;
    if (!utils.BcdToDigit(bcdArr, bcdLen, addrNum, SMS_MAX_ADDRESS_LEN)) {
        TELEPHONY_LOGE("BcdToDigit fail!");
        return false;
    }
    std::string newStrAddress = regProcessSmsAddrs(addrNum);
    for (uint8_t i = 0; i < newStrAddress.size() && i < MAX_ADDRESS_LEN; i++) {
        resultNum->address[i] = newStrAddress[i];
    }
    return true;
}

bool GsmSmsParamDecode::DecodeAddressDefaultNum(SmsReadBuffer &buffer, struct AddressNumber *resultNum, uint8_t bcdLen)
{
    uint8_t index = 0;
    buffer.MoveForward(1);
    uint8_t oneByte = 0;
    uint8_t bcdArr[MAX_ADDRESS_LEN + 1] = { 0 };
    while (index < bcdLen && index < MAX_ADDRESS_LEN) {
        if (!buffer.ReadByte(oneByte)) {
            TELEPHONY_LOGE("get data error.");
            return false;
        }
        bcdArr[index++] = oneByte;
    }
    GsmSmsCommonUtils utils;
    std::string addrNum;
    if (!utils.BcdToDigit(bcdArr, bcdLen, addrNum, SMS_MAX_ADDRESS_LEN)) {
        TELEPHONY_LOGE("BcdToDigit fail!");
        return false;
    }
    for (uint8_t i = 0; i < addrNum.size() && i < MAX_ADDRESS_LEN; i++) {
        resultNum->address[i] = addrNum[i];
    }
    return true;
}


std::string GsmSmsParamDecode::regProcessSmsAddrs(std::string &smsAddrs)
{
    std::regex pattern(R"((^[#*])(.*)([#*])(.*)(#)$)");
    std::smatch match;
    std::ostringstream ret;
    if (smsAddrs.empty()) {
        ret << '+' <<smsAddrs;
        return ret.str();
    }
    if (std::regex_match(smsAddrs, match, pattern)) {
        if (match[MATCH_INDEX_TWO] == "") {
            ret << match[MATCH_INDEX_ONE] << match[MATCH_INDEX_THREE] <<
                match[MATCH_INDEX_FOUR] << match[MATCH_INDEX_FIVE] << '+';
        } else {
            ret << match[MATCH_INDEX_ONE] << match[MATCH_INDEX_TWO] <<
                match[MATCH_INDEX_THREE] << '+' << match[MATCH_INDEX_FOUR] << match[MATCH_INDEX_FIVE];
        }
    } else {
        std::regex pattern2(R"((^[#*])(.*)([#*])(.*))");
        if (std::regex_match(smsAddrs, match, pattern2)) {
            ret << match[MATCH_INDEX_ONE] << match[MATCH_INDEX_TWO] <<
                match[MATCH_INDEX_THREE] << '+' << match[MATCH_INDEX_FOUR];
        } else {
            ret << '+' << smsAddrs;
        }
    }
    return ret.str();
}

void GsmSmsParamDecode::DecodeSmscPdu(uint8_t *srcAddr, uint8_t addrLen, enum TypeOfNum ton, std::string &desAddr)
{
    if (srcAddr == nullptr || addrLen == 0) {
        TELEPHONY_LOGE("smsc data error.");
        return;
    }

    GsmSmsCommonUtils utils;
    if (ton == TYPE_INTERNATIONAL) {
        if (!utils.BcdToDigit(srcAddr, addrLen, desAddr, SMS_MAX_ADDRESS_LEN)) {
            TELEPHONY_LOGE("BcdToDigit fail!");
            return;
        }
        std::string newStrAddress = regProcessSmsAddrs(desAddr);
        desAddr = newStrAddress;
    } else {
        if (!utils.BcdToDigit(srcAddr, addrLen, desAddr, SMS_MAX_ADDRESS_LEN)) {
            TELEPHONY_LOGE("BcdToDigit fail!");
            return;
        }
    }
}

uint8_t GsmSmsParamDecode::DecodeSmscPdu(const uint8_t *pTpdu, uint8_t pduLen, struct AddressNumber &desAddrObj)
{
    if (pTpdu == nullptr || pduLen == 0) {
        TELEPHONY_LOGE("nullptr error.");
        return 0;
    }
    if (memset_s(desAddrObj.address, sizeof(desAddrObj.address), 0x00, sizeof(desAddrObj.address)) != EOK) {
        TELEPHONY_LOGE("memset_s error!");
        return 0;
    }

    uint8_t offset = 0;
    uint8_t addrLen = pTpdu[offset++];
    if (addrLen == 0) {
        TELEPHONY_LOGI("smsc is 00.");
        return offset;
    }
    if (addrLen >= pduLen || offset >= pduLen) {
        TELEPHONY_LOGI("smsc absent.");
        return 0;
    }
    desAddrObj.ton = (pTpdu[offset] & HEX_VALUE_70) >> HEX_VALUE_04;
    desAddrObj.npi = pTpdu[offset++] & HEX_VALUE_0F;

    GsmSmsCommonUtils utils;
    if (desAddrObj.ton == TYPE_INTERNATIONAL) {
        if (addrLen > SMS_MAX_ADDRESS_LEN || (offset + addrLen) >= pduLen) {
            TELEPHONY_LOGE("addrLen invalid.");
            return 0;
        }
        std::string addrNum;
        utils.BcdToDigit(&(pTpdu[offset]), addrLen, addrNum, SMS_MAX_ADDRESS_LEN);
        std::string newStrAddress = regProcessSmsAddrs(addrNum);
        for (uint8_t i = 0; i < newStrAddress.size() && i < MAX_ADDRESS_LEN; i++) {
            desAddrObj.address[i] = newStrAddress[i];
        }
    } else {
        if (addrLen > SMS_MAX_ADDRESS_LEN || (offset + addrLen) >= pduLen) {
            TELEPHONY_LOGE("addrLen invalid.");
            return 0;
        }
        std::string addrNum;
        utils.BcdToDigit(&(pTpdu[offset]), addrLen, addrNum, SMS_MAX_ADDRESS_LEN);
        for (uint8_t i = 0; i < addrNum.size() && i < MAX_ADDRESS_LEN; i++) {
            desAddrObj.address[i] = addrNum[i];
        }
    }

    offset += (addrLen - 1);
    return offset;
}

bool GsmSmsParamDecode::DecodeDcsPdu(SmsReadBuffer &buffer, struct SmsDcs *smsDcs)
{
    if (smsDcs == nullptr) {
        TELEPHONY_LOGE("smsDcs nullptr.");
        return false;
    }
    uint8_t dcs = 0;
    if (!buffer.ReadByte(dcs)) {
        TELEPHONY_LOGE("get data error.");
        return false;
    }

    smsDcs->bMWI = false;
    smsDcs->bIndActive = false;
    smsDcs->indType = SMS_OTHER_INDICATOR;
    if (((dcs & HEX_VALUE_C0) >> HEX_VALUE_06) == 0) {
        DecodeDcsGeneralGroupPdu(dcs, smsDcs);
    } else if (((dcs & HEX_VALUE_F0) >> HEX_VALUE_04) == HEX_VALUE_0F) {
        DecodeDcsClassGroupPdu(dcs, smsDcs);
    } else if (((dcs & HEX_VALUE_C0) >> HEX_VALUE_06) == HEX_VALUE_01) {
        DecodeDcsDeleteGroupPdu(dcs, smsDcs);
    } else if (((dcs & HEX_VALUE_F0) >> HEX_VALUE_04) == HEX_VALUE_0C) {
        DecodeDcsDiscardGroupPdu(dcs, smsDcs);
    } else if (((dcs & HEX_VALUE_F0) >> HEX_VALUE_04) == HEX_VALUE_0D) {
        DecodeDcsStoreGsmGroupPdu(dcs, smsDcs);
    } else if (((dcs & HEX_VALUE_F0) >> HEX_VALUE_04) == HEX_VALUE_0E) {
        DecodeDcsStoreUCS2GroupPdu(dcs, smsDcs);
    } else {
        DecodeDcsUnknownGroupPdu(dcs, smsDcs);
    }
    return true;
}

void GsmSmsParamDecode::DecodeDcsGeneralGroupPdu(uint8_t dcs, struct SmsDcs *smsDcs)
{
    if (smsDcs == nullptr) {
        TELEPHONY_LOGE("smsDcs is nullptr.");
        return;
    }

    smsDcs->codingGroup = CODING_GENERAL_GROUP;
    smsDcs->bCompressed = (dcs & HEX_VALUE_20) >> HEX_VALUE_05;
    smsDcs->codingScheme = GetMsgCodingScheme((dcs & HEX_VALUE_0C) >> HEX_VALUE_02);
    if (((dcs & HEX_VALUE_10) >> HEX_VALUE_04) == 0) {
        smsDcs->msgClass = SMS_CLASS_UNKNOWN;
    } else {
        smsDcs->msgClass = GetMsgClass(dcs & HEX_VALUE_03);
    }
}

void GsmSmsParamDecode::DecodeDcsClassGroupPdu(uint8_t dcs, struct SmsDcs *smsDcs)
{
    if (smsDcs == nullptr) {
        TELEPHONY_LOGE("smsDcs is nullptr.");
        return;
    }

    smsDcs->codingGroup = SMS_CLASS_GROUP;
    smsDcs->bCompressed = false;
    smsDcs->codingScheme = GetMsgCodingScheme((dcs & HEX_VALUE_0C) >> HEX_VALUE_02);
    smsDcs->msgClass = GetMsgClass(dcs & HEX_VALUE_03);
}

void GsmSmsParamDecode::DecodeDcsDeleteGroupPdu(uint8_t dcs, struct SmsDcs *smsDcs)
{
    if (smsDcs == nullptr) {
        TELEPHONY_LOGE("smsDcs is nullptr.");
        return;
    }

    smsDcs->codingGroup = CODING_DELETION_GROUP;
    smsDcs->bCompressed = false;
    smsDcs->msgClass = SMS_CLASS_UNKNOWN;
}

void GsmSmsParamDecode::DecodeDcsDiscardGroupPdu(uint8_t dcs, struct SmsDcs *smsDcs)
{
    if (smsDcs == nullptr) {
        TELEPHONY_LOGE("smsDcs is null.");
        return;
    }

    smsDcs->codingGroup = CODING_DISCARD_GROUP;
    GetMwiType(dcs, *smsDcs);
}

void GsmSmsParamDecode::DecodeDcsStoreGsmGroupPdu(uint8_t dcs, struct SmsDcs *smsDcs)
{
    if (smsDcs == nullptr) {
        TELEPHONY_LOGE("smsDcs is null.");
        return;
    }

    smsDcs->codingGroup = CODING_STORE_GROUP;
    smsDcs->codingScheme = DATA_CODING_7BIT;
    GetMwiType(dcs, *smsDcs);
}

void GsmSmsParamDecode::DecodeDcsStoreUCS2GroupPdu(uint8_t dcs, struct SmsDcs *smsDcs)
{
    if (smsDcs == nullptr) {
        TELEPHONY_LOGE("smsDcs is null.");
        return;
    }

    smsDcs->codingGroup = CODING_STORE_GROUP;
    smsDcs->codingScheme = DATA_CODING_UCS2;
    GetMwiType(dcs, *smsDcs);
}

void GsmSmsParamDecode::DecodeDcsUnknownGroupPdu(uint8_t dcs, struct SmsDcs *smsDcs)
{
    if (smsDcs == nullptr) {
        TELEPHONY_LOGE("smsDcs is null.");
        return;
    }

    smsDcs->codingGroup = CODING_UNKNOWN_GROUP;
    smsDcs->bCompressed = (dcs & HEX_VALUE_20) >> HEX_VALUE_05;
    smsDcs->codingScheme = GetMsgCodingScheme((dcs & HEX_VALUE_0C) >> HEX_VALUE_02);
    smsDcs->msgClass = SMS_CLASS_UNKNOWN;
}

enum SmsMessageClass GsmSmsParamDecode::GetMsgClass(uint8_t dcs)
{
    return (enum SmsMessageClass)(dcs & HEX_VALUE_03);
}

enum DataCodingScheme GsmSmsParamDecode::GetMsgCodingScheme(uint8_t dcs)
{
    return (enum DataCodingScheme)(dcs & HEX_VALUE_03);
}

enum SmsIndicatorType GsmSmsParamDecode::GetMsgIndicatorType(const uint8_t dcs)
{
    return (enum SmsIndicatorType)(dcs & HEX_VALUE_03);
}

void GsmSmsParamDecode::GetMwiType(const uint8_t dcs, struct SmsDcs &smsDcs)
{
    smsDcs.bCompressed = false;
    smsDcs.msgClass = SMS_CLASS_UNKNOWN;
    smsDcs.bMWI = true;
    smsDcs.bIndActive = (((dcs & HEX_VALUE_08) >> HEX_VALUE_03) == HEX_VALUE_01) ? true : false;
    smsDcs.indType = GetMsgIndicatorType(dcs & HEX_VALUE_03);
}

bool GsmSmsParamDecode::DecodeTimePdu(SmsReadBuffer &buffer, struct SmsTimeStamp *timeStamp)
{
    if (timeStamp == nullptr) {
        TELEPHONY_LOGE("nullptr error.");
        return false;
    }
    /* decode in ABSOLUTE time type. */
    timeStamp->format = SMS_TIME_ABSOLUTE;

    uint8_t pickByte = 0;
    if (!buffer.PickOneByte(pickByte)) {
        TELEPHONY_LOGE("get data error.");
        return false;
    }
    uint8_t oneByte = 0;
    if (!buffer.ReadByte(oneByte)) {
        TELEPHONY_LOGE("get data error.");
        return false;
    }

    timeStamp->time.absolute.year =
        (pickByte & HEX_VALUE_0F) * HEX_VALUE_0A + ((oneByte & HEX_VALUE_F0) >> HEX_VALUE_04);

    if (!buffer.PickOneByte(pickByte)) {
        TELEPHONY_LOGE("get data error.");
        return false;
    }
    if (!buffer.ReadByte(oneByte)) {
        TELEPHONY_LOGE("get data error.");
        return false;
    }
    timeStamp->time.absolute.month =
        (pickByte & HEX_VALUE_0F) * HEX_VALUE_0A + ((oneByte & HEX_VALUE_F0) >> HEX_VALUE_04);
    return DecodeTimePduPartData(buffer, timeStamp);
}

bool GsmSmsParamDecode::DecodeTimePduPartData(SmsReadBuffer &buffer, struct SmsTimeStamp *timeStamp)
{
    uint8_t pickByte = 0;
    if (!buffer.PickOneByte(pickByte)) {
        TELEPHONY_LOGE("get data error.");
        return false;
    }

    uint8_t oneByte = 0;
    if (!buffer.ReadByte(oneByte)) {
        TELEPHONY_LOGE("get data error.");
        return false;
    }
    timeStamp->time.absolute.day =
        (pickByte & HEX_VALUE_0F) * HEX_VALUE_0A + ((oneByte & HEX_VALUE_F0) >> HEX_VALUE_04);

    if (!buffer.PickOneByte(pickByte)) {
        TELEPHONY_LOGE("get data error.");
        return false;
    }
    if (!buffer.ReadByte(oneByte)) {
        TELEPHONY_LOGE("get data error.");
        return false;
    }
    timeStamp->time.absolute.hour =
        (pickByte & HEX_VALUE_0F) * HEX_VALUE_0A + ((oneByte & HEX_VALUE_F0) >> HEX_VALUE_04);

    if (!buffer.PickOneByte(pickByte)) {
        TELEPHONY_LOGE("get data error.");
        return false;
    }
    if (!buffer.ReadByte(oneByte)) {
        TELEPHONY_LOGE("get data error.");
        return false;
    }
    timeStamp->time.absolute.minute =
        (pickByte & HEX_VALUE_0F) * HEX_VALUE_0A + ((oneByte & HEX_VALUE_F0) >> HEX_VALUE_04);
    return DecodeTimePduData(buffer, timeStamp);
}

bool GsmSmsParamDecode::DecodeTimePduData(SmsReadBuffer &buffer, struct SmsTimeStamp *timeStamp)
{
    if (timeStamp == nullptr) {
        TELEPHONY_LOGE("nullptr error.");
        return false;
    }

    uint8_t pickByte = 0;
    if (!buffer.PickOneByte(pickByte)) {
        TELEPHONY_LOGE("get data error.");
        return false;
    }

    uint8_t oneByte = 0;
    if (!buffer.ReadByte(oneByte)) {
        TELEPHONY_LOGE("get data error.");
        return false;
    }
    timeStamp->time.absolute.second =
        (pickByte & HEX_VALUE_0F) * HEX_VALUE_0A + ((oneByte & HEX_VALUE_F0) >> HEX_VALUE_04);

    if (!buffer.PickOneByte(pickByte)) {
        TELEPHONY_LOGE("get data error.");
        return false;
    }
    if (!buffer.PickOneByte(oneByte)) {
        TELEPHONY_LOGE("get data error.");
        return false;
    }
    timeStamp->time.absolute.timeZone =
        (pickByte & HEX_VALUE_07) * HEX_VALUE_0A + ((oneByte & HEX_VALUE_F0) >> HEX_VALUE_04);

    if (!buffer.ReadByte(oneByte)) {
        TELEPHONY_LOGE("get data error.");
        return false;
    }
    if (oneByte & HEX_VALUE_08) {
        timeStamp->time.absolute.timeZone *= (-1);
    }
    return true;
}
} // namespace Telephony
} // namespace OHOS