/*
* Copyright 2017 Huy Cuong Nguyen
* Copyright 2007 ZXing authors
*/
// SPDX-License-Identifier: Apache-2.0

use crate::qrcode::{
    cpp_port::Type,
    decoder::{ErrorCorrectionLevel, FormatInformation},
};

const MASKED_TEST_FORMAT_INFO: u32 = 0x2BED;
const MASKED_TEST_FORMAT_INFO2: u32 =
    ((0x2BED << 1) & 0b1111111000000000) | 0b100000000 | (0x2BED & 0b11111111); // insert the 'Dark Module'
const UNMASKED_TEST_FORMAT_INFO: u32 = MASKED_TEST_FORMAT_INFO ^ 0x5412;
const MICRO_MASKED_TEST_FORMAT_INFO: u32 = 0x3BBA;
// const MICRO_UNMASKED_TEST_FORMAT_INFO: u32 = MICRO_MASKED_TEST_FORMAT_INFO ^ 0x4445;
const RMQR_MASKED_TEST_FORMAT_INFO: u32 = 0x20137;
const RMQR_MASKED_TEST_FORMAT_INFO_SUB: u32 = 0x1F1FE;

// Removed as unused
//const FORMAT_INFO_MASK_QR_MODEL1: u32 = 0x2825;
//const FORMAT_INFO_MASK_MICRO: u32 = 0x4445;
const FORMAT_INFO_MASK_RMQR: u32 = 0x1FAB2; // Finder pattern side
const FORMAT_INFO_MASK_RMQR_SUB: u32 = 0x20A7B; // Finder sub pattern side

fn DoFormatInformationTest(formatInfo: u32, expectedMask: u8, expectedECL: ErrorCorrectionLevel) {
    let parsedFormat = FormatInformation::DecodeMQR(formatInfo);
    assert!(parsedFormat.isValid());
    assert_eq!(expectedMask, parsedFormat.data_mask);
    assert_eq!(expectedECL, parsedFormat.error_correction_level);
}

// Helper for rMQR to unset `numBits` number of bits
fn RMQRUnsetBits(formatInfoBits: u32, numBits: u32) -> u32 {
    let mut formatInfoBits = formatInfoBits;
    let mut numBits = numBits as i32;
    for i in 0..18 {
        // for (int i = 0; i < 18 && numBits; i++) {
        if (formatInfoBits & (1 << i)) != 0 {
            formatInfoBits ^= 1 << i;
            numBits -= 1;
        }
        if numBits == 0 {
            break;
        }
    }
    formatInfoBits
}

fn cpp_eq(rhs: &FormatInformation, lhs: &FormatInformation) {
    assert!(rhs.cpp_eq(lhs))
}

#[test]
fn Decode() {
    // Normal case
    let expected = FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO2);
    assert!(expected.isValid());
    assert_eq!(0x07, expected.data_mask);
    assert_eq!(ErrorCorrectionLevel::Q, expected.error_correction_level);
    // where the code forgot the mask!
    // assert_eq!(
    //     expected,
    //     FormatInformation::DecodeQR(UNMASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO2)
    // );
    cpp_eq(
        &expected,
        &FormatInformation::DecodeQR(UNMASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO2),
    )
}

#[test]
fn DecodeWithBitDifference() {
    let expected = FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO2);
    // 1,2,3,4 bits difference
    cpp_eq(
        &expected,
        &FormatInformation::DecodeQR(
            MASKED_TEST_FORMAT_INFO ^ 0x01,
            MASKED_TEST_FORMAT_INFO2 ^ 0x01,
        ),
    );
    cpp_eq(
        &expected,
        &FormatInformation::DecodeQR(
            MASKED_TEST_FORMAT_INFO ^ 0x03,
            MASKED_TEST_FORMAT_INFO2 ^ 0x03,
        ),
    );
    cpp_eq(
        &expected,
        &FormatInformation::DecodeQR(
            MASKED_TEST_FORMAT_INFO ^ 0x07,
            MASKED_TEST_FORMAT_INFO2 ^ 0x07,
        ),
    );
    let unexpected = FormatInformation::DecodeQR(
        MASKED_TEST_FORMAT_INFO ^ 0x0F,
        MASKED_TEST_FORMAT_INFO2 ^ 0x0F,
    );
    assert!(!&expected.cpp_eq(&unexpected));
    assert!(!(unexpected.isValid() && unexpected.qr_type() == Type::Model2));
}

#[test]
fn DecodeWithMisread() {
    let expected = FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO2);
    cpp_eq(
        &expected,
        &FormatInformation::DecodeQR(
            MASKED_TEST_FORMAT_INFO ^ 0x03,
            MASKED_TEST_FORMAT_INFO2 ^ 0x0F,
        ),
    );
}

#[test]
fn DecodeMicro() {
    // Normal cases.
    DoFormatInformationTest(0x4445, 0x0, ErrorCorrectionLevel::L);
    DoFormatInformationTest(0x4172, 0x1, ErrorCorrectionLevel::L);
    DoFormatInformationTest(0x5fc0, 0x2, ErrorCorrectionLevel::L);
    DoFormatInformationTest(0x5af7, 0x3, ErrorCorrectionLevel::L);
    DoFormatInformationTest(0x6793, 0x0, ErrorCorrectionLevel::M);
    DoFormatInformationTest(0x62a4, 0x1, ErrorCorrectionLevel::M);
    DoFormatInformationTest(0x3e8d, 0x2, ErrorCorrectionLevel::Q);
    DoFormatInformationTest(MICRO_MASKED_TEST_FORMAT_INFO, 0x3, ErrorCorrectionLevel::Q);

    // where the code forgot the mask!
    //	DoFormatInformationTest(MICRO_UNMASKED_TEST_FORMAT_INFO, 0x3, ErrorCorrectionLevel::Quality);
}

// This doesn't work as expected because the implementation of the decode tries with
// and without the mask (0x4445).  This effectively adds a tolerance of 5 bits to the Hamming
// distance calculation.
#[test]
fn DecodeMicroWithBitDifference() {
    let expected = FormatInformation::DecodeMQR(MICRO_MASKED_TEST_FORMAT_INFO);

    // 1,2,3 bits difference
    cpp_eq(
        &expected,
        &FormatInformation::DecodeMQR(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x01),
    );
    cpp_eq(
        &expected,
        &FormatInformation::DecodeMQR(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x03),
    );
    cpp_eq(
        &expected,
        &FormatInformation::DecodeMQR(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x07),
    );

    // Bigger bit differences can return valid FormatInformation objects but the data mask and error
    // correction levels do not match.
    //	EXPECT_TRUE(FormatInformation::DecodeFormatInformation(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x3f).isValid());
    //	EXPECT_NE(expected.dataMask(), FormatInformation::DecodeFormatInformation(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x3f).dataMask());
    //	EXPECT_NE(expected.errorCorrectionLevel(),
    //			  FormatInformation::DecodeFormatInformation(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x3f).errorCorrectionLevel());
}

#[test]
fn DecodeRMQR() {
    // Normal case
    let expected = FormatInformation::DecodeRMQR(
        RMQR_MASKED_TEST_FORMAT_INFO,
        RMQR_MASKED_TEST_FORMAT_INFO_SUB,
    );
    assert!(expected.isValid());
    assert_eq!(4, expected.data_mask);
    assert_eq!(ErrorCorrectionLevel::H, expected.error_correction_level);
    assert_eq!(FORMAT_INFO_MASK_RMQR, expected.mask);
    // Not catered for: where the code forgot the mask!
}

#[test]
fn DecodeRMQRWithBitDifference() {
    let expected = FormatInformation::DecodeRMQR(
        RMQR_MASKED_TEST_FORMAT_INFO,
        RMQR_MASKED_TEST_FORMAT_INFO_SUB,
    );
    assert_eq!(expected.error_correction_level, ErrorCorrectionLevel::H);
    // 1,2,3,4,5 bits difference
    cpp_eq(
        &expected,
        &FormatInformation::DecodeRMQR(
            RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO, 1),
            RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO_SUB, 1),
        ),
    );
    cpp_eq(
        &expected,
        &FormatInformation::DecodeRMQR(
            RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO, 2),
            RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO_SUB, 2),
        ),
    );
    cpp_eq(
        &expected,
        &FormatInformation::DecodeRMQR(
            RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO, 3),
            RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO_SUB, 3),
        ),
    );
    cpp_eq(
        &expected,
        &FormatInformation::DecodeRMQR(
            RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO, 4),
            RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO_SUB, 4),
        ),
    );
    let unexpected = FormatInformation::DecodeRMQR(
        RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO, 5),
        RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO_SUB, 5),
    );
    assert!(!(expected == unexpected));
    assert!(!(unexpected.isValid()));
    assert!(unexpected.qr_type() == Type::RectMicro); // Note `mask` (used to determine type) set regardless
}

#[test]
fn DecodeRMQRWithMisread() {
    let expected = FormatInformation::DecodeRMQR(
        RMQR_MASKED_TEST_FORMAT_INFO,
        RMQR_MASKED_TEST_FORMAT_INFO_SUB,
    );
    {
        let actual = FormatInformation::DecodeRMQR(
            RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO, 2),
            RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO_SUB, 4),
        );
        cpp_eq(&expected, &actual);
        assert_eq!(actual.mask, FORMAT_INFO_MASK_RMQR);
    }
    {
        let actual = FormatInformation::DecodeRMQR(
            RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO, 5),
            RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO_SUB, 4),
        );
        cpp_eq(&expected, &actual);
        assert_eq!(actual.mask, FORMAT_INFO_MASK_RMQR_SUB);
    }
}
