import {describe, it, expect} from 'deccjsunit/index'
import huks from '@ohos.security.huks'

let IV = "0000000000000000";
let AAD = "0000000000000000";
let NONCE = "000000000000";
let AEAD = "0000000000000000";

let HksKeyAlg = {
    HKS_ALG_AES: 20,
}

let HksKeyPurpose = {
    HKS_KEY_PURPOSE_ENCRYPT: 1,
    HKS_KEY_PURPOSE_DECRYPT: 2
}

let HksKeyPadding = {
    HKS_PADDING_NONE: 0,
    HKS_PADDING_PKCS7: 5
}

let HksKeyDigest = {
    HKS_DIGEST_NONE: 0,
    HKS_DIGEST_MD5: 1,
    HKS_DIGEST_SHA1: 10,
    HKS_DIGEST_SHA224: 11,
    HKS_DIGEST_SHA256: 12,
    HKS_DIGEST_SHA384: 13,
    HKS_DIGEST_SHA512: 14,
}

let HksKeySize = {
    HKS_AES_KEY_SIZE_128: 128,
    HKS_AES_KEY_SIZE_192: 192,
    HKS_AES_KEY_SIZE_256: 256,
}

let HksTagType = {
    HKS_TAG_TYPE_UINT: 2 << 28,
    HKS_TAG_TYPE_BYTES: 5 << 28
}

let HksCipherMode = {
    HKS_MODE_ECB: 1,
    HKS_MODE_CBC: 2,
    HKS_MODE_CTR: 3,
    HKS_MODE_OFB: 4,
    HKS_MODE_CCM: 31,
    HKS_MODE_GCM: 32,
};

let HksTag = {
    HKS_TAG_ALGORITHM: HksTagType.HKS_TAG_TYPE_UINT | 1,
    HKS_TAG_PURPOSE: HksTagType.HKS_TAG_TYPE_UINT | 2,
    HKS_TAG_KEY_SIZE: HksTagType.HKS_TAG_TYPE_UINT | 3,
    HKS_TAG_DIGEST: HksTagType.HKS_TAG_TYPE_UINT | 4,
    HKS_TAG_PADDING: HksTagType.HKS_TAG_TYPE_UINT | 5,
    HKS_TAG_BLOCK_MODE: HksTagType.HKS_TAG_TYPE_UINT | 6,
    HKS_TAG_ASSOCIATED_DATA: HksTagType.HKS_TAG_TYPE_BYTES | 8,
    HKS_TAG_NONCE: HksTagType.HKS_TAG_TYPE_BYTES | 9,
    HKS_TAG_IV: HksTagType.HKS_TAG_TYPE_BYTES | 10,
    HKS_TAG_AGREE_PUBLIC_KEY: HksTagType.HKS_TAG_TYPE_BYTES | 22,
}

let HuksCipher001 = {
    HuksKeyAlgAES: { "tag": HksTag.HKS_TAG_ALGORITHM, "value": HksKeyAlg.HKS_ALG_AES },
    HuksKeyPurpose: {
        "tag": HksTag.HKS_TAG_PURPOSE,
        "value": HksKeyPurpose.HKS_KEY_PURPOSE_ENCRYPT | HksKeyPurpose.HKS_KEY_PURPOSE_DECRYPT
    },
    HuksKeyPurposeENCRYPT: { "tag": HksTag.HKS_TAG_PURPOSE, "value": HksKeyPurpose.HKS_KEY_PURPOSE_ENCRYPT },
    HuksKeyPurposeDECRYPT: { "tag": HksTag.HKS_TAG_PURPOSE, "value": HksKeyPurpose.HKS_KEY_PURPOSE_DECRYPT },
    HuksKeyAESSize128: { "tag": HksTag.HKS_TAG_KEY_SIZE, "value": HksKeySize.HKS_AES_KEY_SIZE_128 },
    HuksKeyAESSize192: { "tag": HksTag.HKS_TAG_KEY_SIZE, "value": HksKeySize.HKS_AES_KEY_SIZE_192 },
    HuksKeyAESSize256: { "tag": HksTag.HKS_TAG_KEY_SIZE, "value": HksKeySize.HKS_AES_KEY_SIZE_256 },
    HuksKeyAESPADDINGNONE: { "tag": HksTag.HKS_TAG_PADDING, "value": HksKeyPadding.HKS_PADDING_NONE },
    HuksKeyAESPADDINGPKCS7: { "tag": HksTag.HKS_TAG_PADDING, "value": HksKeyPadding.HKS_PADDING_PKCS7 },
    HuksKeyAESBLOCKMODE: { "tag": HksTag.HKS_TAG_BLOCK_MODE, "value": HksCipherMode.HKS_MODE_CBC },
    HuksKeyAESBLOCKMODECTR: { "tag": HksTag.HKS_TAG_BLOCK_MODE, "value": HksCipherMode.HKS_MODE_CTR },
    HuksKeyAESBLOCKMODEECB: { "tag": HksTag.HKS_TAG_BLOCK_MODE, "value": HksCipherMode.HKS_MODE_ECB },
    HuksKeyAESDIGESTNONE: { "tag": HksTag.HKS_TAG_DIGEST, "value": HksKeyDigest.HKS_DIGEST_NONE },
}
let defaultData = "0";
let srcData65 = "Hks_AES_Cipher_Test_00000000000000000000000000000000000000000000000000000_string";
let srcData65Kb = stringToUint8Array(srcData65);

let srcData63 = "Hks_AES_Cipher_Test_000000000000000000000_string";
let srcData63Kb = stringToUint8Array(srcData63);

function stringToUint8Array(str) {
    var arr = [];
    for (var i = 0, j = str.length; i < j; ++i) {
        arr.push(str.charCodeAt(i));
    }
    var tmpUint8Array = new Uint8Array(arr);
    return tmpUint8Array
}

function Uint8ArrayToString(fileData) {
    var dataString = "";
    for (var i = 0; i < fileData.length; i++) {
        dataString += String.fromCharCode(fileData[i]);
    }
    return dataString
}

let updateResult = new Array();
let encryptFinishData;
let encryptedData;
var handle = {};
var handle1;
var handle2;

let genHuksOptions = {
    "properties": new Array(
        HuksCipher001.HuksKeyAlgAES,
        HuksCipher001.HuksKeyPurpose),
    "inData": new Uint8Array(defaultData),
};


async function publicGenerateKeyFunc(srcKeyAlies, genHuksOptionsNONECBC) {
    console.log(`test GenerateHuksOptions: ${JSON.stringify(genHuksOptionsNONECBC)}`);
    await huks.generateKey(srcKeyAlies, genHuksOptionsNONECBC).then((data) => {
        console.log(`test generateKey data: ${JSON.stringify(data)}`);
        expect(data.errorCode == 0).assertTrue()
    }).catch((err) => {
        console.log("test generateKey err information: " + JSON.stringify(err))
        expect(null).assertFail();
    });
}

async function publicInitFunc(srcKeyAlies, HuksOptions) {
    console.log(`test init HuksOptions: ${JSON.stringify(HuksOptions)}`);
    await huks.init(srcKeyAlies, HuksOptions).then((data) => {
        console.log(`test init data: ${JSON.stringify(data)}`);
        handle1 = data.handle1;
        handle2 = data.handle2;
        handle = {
            "handle1": handle1,
            "handle2": handle2
        };
        expect(data.errorCode == 0).assertTrue()
    }).catch((err) => {
        console.log("test init err information: " + JSON.stringify(err))
        expect(null).assertFail();
    });
}

async function publicUpdateFunc(HuksOptions,thirdInderfaceName,isEncrypt) {
    console.log(`test update before handle: ${JSON.stringify(handle)} HuksOptions: ${JSON.stringify(HuksOptions)}`)
    let dateSize = 64
    let _HuksOptions_inData = HuksOptions.inData;
    let inDataArray = HuksOptions.inData;
    console.log("test update finish HuksOptions inData: " + Array.from(inDataArray).length);
    if (Array.from(inDataArray).length < dateSize) {
        await update(handle, HuksOptions);
        HuksOptions.inData = new Uint8Array(new Array());
        await publicFinishAbortFunc(HuksOptions, thirdInderfaceName, isEncrypt,0,_HuksOptions_inData.length);
    } else {
        let count = Math.floor(Array.from(inDataArray).length / dateSize);
        let remainder = Array.from(inDataArray).length % dateSize;
        console.log("test count " + count+ "remainder " + remainder)
        for (let i = 0;i < count; i++) {
            HuksOptions.inData = new Uint8Array(Array.from(_HuksOptions_inData).slice(dateSize * i, dateSize * (i + 1)));
            console.log("test " + Uint8ArrayToString(new Uint8Array(Array.from(_HuksOptions_inData).slice(dateSize * i, dateSize * (i + 1)))));
            await update(handle, HuksOptions);
        }
        HuksOptions.inData = _HuksOptions_inData
        if (remainder !== 0) {
            HuksOptions.inData = new Uint8Array(Array.from(_HuksOptions_inData).slice(dateSize * count, Uint8ArrayToString(inDataArray).length));
            console.log("test " + Uint8ArrayToString(new Uint8Array(Array.from(_HuksOptions_inData).slice(dateSize * count, Uint8ArrayToString(inDataArray).length))));
        }else{
            HuksOptions.inData = new Uint8Array(new Array());
        }
        await publicFinishAbortFunc(HuksOptions, thirdInderfaceName, isEncrypt,remainder,_HuksOptions_inData.length);
    }
}

async function update(handle, HuksOptions) {
    console.log(`test update data ${JSON.stringify(HuksOptions)}`);
    console.log(`test update data ${JSON.stringify(HuksOptions.inData.length)}`);
    await huks.update(handle, HuksOptions).then(async (data) => {
        console.log(`test update data ${JSON.stringify(data)}`);
        if (updateResult.length !== 0) {
            console.log(`test update outDatalength ${updateResult.length}`);
            updateResult = updateResult.concat(Array.from(data.outData));
            console.log(`test update outDatalength ${updateResult.length}`);
        } else {
            console.log(`test update outDatalength ${updateResult.length}`);
            updateResult = Array.from(data.outData);
            console.log(`test update outDatalength ${updateResult.length}`);
        }
        expect(data.errorCode == 0).assertTrue()
    }).catch((err) => {
        console.log("test update err information: " + err)
        expect(null).assertFail();
    });
}

async function publicFinishAbortFunc(HuksOptions,thirdInderfaceName,isEncrypt,remainder,dataLength){
    if (thirdInderfaceName == "finish") {
        HuksOptions.outData = new Uint8Array(new Array(encryptedData.length*2));
        console.log(`test remainder ${remainder}`)
        await finish(HuksOptions, isEncrypt);
    } else if (thirdInderfaceName == "abort") {
        HuksOptions.outData = new Uint8Array(new Array(encryptedData.length*2));
        await abort(HuksOptions);
    }
}

async function finish(HuksOptions, isEncrypt) {
    await huks.finish(handle, HuksOptions).then((data) => {
        console.log(`test finish data: ${JSON.stringify(data)}`);
        let finishData;
        if(encryptedData.length > 64){
            finishData = Uint8ArrayToString(updateResult.concat(Array.from(data.outData)));
            updateResult =  updateResult.concat(Array.from(data.outData));
        }else{
            finishData = Uint8ArrayToString(updateResult);
        }
        if (isEncrypt) {
            if (finishData === Uint8ArrayToString(encryptedData)) {
                console.log(`test finish Encrypt fail ${Uint8ArrayToString(encryptedData)}`);
                console.log(`test finish Encrypt fail ${Uint8ArrayToString(finishData)}`);
                expect(null).assertFail();
            } else {
                console.log(`test finish Encrypt success ${Uint8ArrayToString(encryptedData)}`);
                console.log(`test finish Encrypt success ${Uint8ArrayToString(finishData)}`);
                expect(data.errorCode == 0).assertTrue();
            }
        } else {
            if (finishData === Uint8ArrayToString(encryptedData)) {
                console.log(`test finish Decrypt success ${Uint8ArrayToString(encryptedData)}`);
                console.log(`test finish Decrypt success ${Uint8ArrayToString(finishData)}`);
                expect(data.errorCode == 0).assertTrue();
            } else {
                console.log(`test finish Decrypt fail ${Uint8ArrayToString(encryptedData)}`);
                console.log(`test finish Decrypt fail ${Uint8ArrayToString(finishData)}`);
                expect(null).assertFail();
            }
        }
    }).catch((err) => {
        console.log("test finish err information: " + JSON.stringify(err))
        expect(null).assertFail();
    });
}

async function abort(HuksOptions) {
    await huks.abort(handle, HuksOptions).then((data) => {
        console.log(`test abort data: ${JSON.stringify(data)}`);
        expect(data.errorCode == 0).assertTrue();
    }).catch((err) => {
        console.log("test abort err information: " + JSON.stringify(err))
        expect(null).assertFail();
    });
}

async function publicDeleteKeyFunc(srcKeyAlies, genHuksOptionsNONECBC) {
    await huks.deleteKey(srcKeyAlies, genHuksOptionsNONECBC).then((data) => {
        console.log(`test deleteKey data: ${JSON.stringify(data)}`);
        expect(data.errorCode == 0).assertTrue()
    }).catch((err) => {
        console.log("test deleteKey err information: " + JSON.stringify(err))
        expect(null).assertFail();
    });
}

async function publicCipherFunc(srcKeyAlies, genHuksOptionsNONECBC, HuksOptions, thirdInderfaceName, isEncrypt) {
    try {
        updateResult = new Array();
        if (isEncrypt) {
            await publicGenerateKeyFunc(srcKeyAlies, genHuksOptionsNONECBC);
            encryptedData = HuksOptions.inData;
        }
        await publicInitFunc(srcKeyAlies, HuksOptions);
        await publicUpdateFunc(HuksOptions,thirdInderfaceName,isEncrypt);
        if (!isEncrypt || (isEncrypt && thirdInderfaceName == "abort")) {
            await publicDeleteKeyFunc(srcKeyAlies, genHuksOptionsNONECBC);
        }
    } catch (e) {
        expect(null).assertFail();
    }
}

describe('SecurityHuksCipherAESPromiseJsunit', function () {
    it('testCipherAES001', 0, async function (done) {
        const srcKeyAlies = 'testCipherAESSize128PADDINGNONEMODECBCKeyAlias001'
        genHuksOptions.properties.splice(2, 1, HuksCipher001.HuksKeyAESSize128);
        genHuksOptions.properties.splice(3, 1, HuksCipher001.HuksKeyAESBLOCKMODE);
        genHuksOptions.properties.splice(4, 1, HuksCipher001.HuksKeyAESPADDINGNONE);
        let HuksOptions = {
            "properties": new Array(HuksCipher001.HuksKeyAlgAES, HuksCipher001.HuksKeyPurposeENCRYPT, HuksCipher001.HuksKeyAESSize128, HuksCipher001.HuksKeyAESPADDINGNONE, HuksCipher001.HuksKeyAESBLOCKMODE, HuksCipher001.HuksKeyAESDIGESTNONE, { "tag": HksTag.HKS_TAG_IV, "value": stringToUint8Array(IV) }),
            "inData": srcData63Kb,
            "outData": stringToUint8Array("0")
        }
        await publicCipherFunc(srcKeyAlies, genHuksOptions, HuksOptions, "finish", true);
        HuksOptions = {
            "properties": new Array(HuksCipher001.HuksKeyAlgAES, HuksCipher001.HuksKeyPurposeDECRYPT, HuksCipher001.HuksKeyAESSize128, HuksCipher001.HuksKeyAESPADDINGNONE, HuksCipher001.HuksKeyAESBLOCKMODE, HuksCipher001.HuksKeyAESDIGESTNONE, { "tag": HksTag.HKS_TAG_IV, "value": stringToUint8Array(IV) }),
            "inData": new Uint8Array(updateResult),
            "outData": stringToUint8Array("0")
        }
        await publicCipherFunc(srcKeyAlies, genHuksOptions, HuksOptions, "finish", false)
        done();
    });

    it('testCipherAES002', 0, async function (done) {
        const srcKeyAlies = 'testCipherAESSize128PADDINGNONEMODECBCKeyAlias002'
        let HuksOptions = {
            "properties": new Array(HuksCipher001.HuksKeyAlgAES, HuksCipher001.HuksKeyPurposeENCRYPT, HuksCipher001.HuksKeyAESSize128, HuksCipher001.HuksKeyAESPADDINGNONE, HuksCipher001.HuksKeyAESBLOCKMODE, HuksCipher001.HuksKeyAESDIGESTNONE, { "tag": HksTag.HKS_TAG_IV, "value": stringToUint8Array(IV) }),
            "inData": new Uint8Array(updateResult),
            "outData": stringToUint8Array("0")
        }
        await publicCipherFunc(srcKeyAlies, genHuksOptions, HuksOptions, "abort", true);
        done();
    })

    it('testCipherAES003', 0, async function (done) {
        const srcKeyAlies = 'testCipherAESSize128PADDINGNONEMODECBCKeyAlias001'
        let HuksOptions = {
            "properties": new Array(HuksCipher001.HuksKeyAlgAES, HuksCipher001.HuksKeyPurposeENCRYPT, HuksCipher001.HuksKeyAESSize128, HuksCipher001.HuksKeyAESPADDINGNONE, HuksCipher001.HuksKeyAESBLOCKMODE, HuksCipher001.HuksKeyAESDIGESTNONE,
                { "tag": HksTag.HKS_TAG_IV, "value": stringToUint8Array(IV) }),
            "inData": srcData63Kb,
            "outData": stringToUint8Array("0")
        }
        await publicCipherFunc(srcKeyAlies, genHuksOptions, HuksOptions, "finish", true)
        HuksOptions = {
            "properties": new Array(HuksCipher001.HuksKeyAlgAES, HuksCipher001.HuksKeyPurposeDECRYPT, HuksCipher001.HuksKeyAESSize128, HuksCipher001.HuksKeyAESPADDINGNONE, HuksCipher001.HuksKeyAESBLOCKMODE, HuksCipher001.HuksKeyAESDIGESTNONE, { "tag": HksTag.HKS_TAG_IV, "value": stringToUint8Array(IV) }),
            "inData": new Uint8Array(updateResult),
            "outData": stringToUint8Array("0")
        }
        await publicCipherFunc(srcKeyAlies, genHuksOptions, HuksOptions, "finish", false)
        done();
    });

    it('testCipherAES004', 0, async function (done) {
        const srcKeyAlies = 'testCipherAESSize128PADDINGNONEMODECBCKeyAlias002'
        let HuksOptions = {
            "properties": new Array(HuksCipher001.HuksKeyAlgAES, HuksCipher001.HuksKeyPurposeENCRYPT, HuksCipher001.HuksKeyAESSize128, HuksCipher001.HuksKeyAESPADDINGNONE, HuksCipher001.HuksKeyAESBLOCKMODE, HuksCipher001.HuksKeyAESDIGESTNONE, { "tag": HksTag.HKS_TAG_IV, "value": stringToUint8Array(IV) }),
            "inData": new Uint8Array(updateResult),
            "outData": stringToUint8Array("0")
        }
        await publicCipherFunc(srcKeyAlies, genHuksOptions, HuksOptions, "abort", true);
        done();
    });
})