#include <stdio.h>

#include <analyzer/analyzer_main.hpp>
#include <analyzer/plugins/plugin.hpp>
#include <analyzer/ras-data/ras-data-parser.hpp>
#include <hei_util.hpp>
#include <util/pdbg.hpp>
#include <util/trace.hpp>

#include "gtest/gtest.h"

namespace analyzer
{
// Forward reference of filterRootCause
bool filterRootCause(AnalysisType i_type,
                     const libhei::IsolationData& i_isoData,
                     libhei::Signature& o_rootCause,
                     const RasDataParser& i_rasData);
} // namespace analyzer

using namespace analyzer;

// Processor side FIRs
static const auto eqCoreFir = static_cast<libhei::NodeId_t>(
    libhei::hash<libhei::NodeId_t>("EQ_CORE_FIR"));

static const auto mc_dstl_fir = static_cast<libhei::NodeId_t>(
    libhei::hash<libhei::NodeId_t>("MC_DSTL_FIR"));

// Explorer OCMB FIRs
static const auto rdfFir =
    static_cast<libhei::NodeId_t>(libhei::hash<libhei::NodeId_t>("RDFFIR"));

// Odyssey OCMB FIRs
static const auto srq_fir =
    static_cast<libhei::NodeId_t>(libhei::hash<libhei::NodeId_t>("SRQ_FIR"));

static const auto rdf_fir =
    static_cast<libhei::NodeId_t>(libhei::hash<libhei::NodeId_t>("RDF_FIR"));

static const auto odp_fir =
    static_cast<libhei::NodeId_t>(libhei::hash<libhei::NodeId_t>("ODP_FIR"));

TEST(RootCauseFilter, Filter1)
{
    pdbg_targets_init(nullptr);

    RasDataParser rasData{};

    // Test 1: Test a checkstop with a UE root cause on an OCMB

    // Checkstop signature on the proc
    auto proc0 = util::pdbg::getTrgt("/proc0");
    libhei::Chip procChip0{proc0, P10_20};

    // EQ_CORE_FIR[14]: ME = 0 checkstop
    libhei::Signature checkstopSig{procChip0, eqCoreFir, 0, 14,
                                   libhei::ATTN_TYPE_CHIP_CS};

    // MC_DSTL_FIR[1]: AFU initiated Recoverable Attn on Subchannel A
    libhei::Signature reAttnSig{procChip0, mc_dstl_fir, 0, 1,
                                libhei::ATTN_TYPE_RECOVERABLE};

    // Root cause signature on the ocmb
    auto ocmb0 =
        util::pdbg::getTrgt("proc0/pib/perv12/mc0/mi0/mcc0/omi0/ocmb0");
    libhei::Chip ocmbChip0{ocmb0, EXPLORER_20};

    // RDFFIR[14]: Mainline read UE
    libhei::Signature ueSig{ocmbChip0, rdfFir, 0, 14,
                            libhei::ATTN_TYPE_RECOVERABLE};

    // Add the signatures to the isolation data
    libhei::IsolationData isoData{};
    isoData.addSignature(checkstopSig);
    isoData.addSignature(reAttnSig);
    isoData.addSignature(ueSig);

    libhei::Signature rootCause;
    bool attnFound = filterRootCause(AnalysisType::SYSTEM_CHECKSTOP, isoData,
                                     rootCause, rasData);
    EXPECT_TRUE(attnFound);
    EXPECT_EQ(ueSig.toUint32(), rootCause.toUint32());

    // Test 2: Test a checkstop with an unknown RE attn on an OCMB

    // Add the signatures to the isolation data
    isoData.flush();
    isoData.addSignature(checkstopSig);
    isoData.addSignature(reAttnSig);

    attnFound = filterRootCause(AnalysisType::SYSTEM_CHECKSTOP, isoData,
                                rootCause, rasData);
    EXPECT_TRUE(attnFound);
    EXPECT_EQ(reAttnSig.toUint32(), rootCause.toUint32());

    // Test 3: Test a checkstop with an unknown UCS attn on an OCMB

    // MC_DSTL_FIR[0]: AFU initiated Checkstop on Subchannel A
    libhei::Signature ucsAttnSig{procChip0, mc_dstl_fir, 0, 0,
                                 libhei::ATTN_TYPE_UNIT_CS};

    isoData.flush();
    isoData.addSignature(checkstopSig);
    isoData.addSignature(ucsAttnSig);

    attnFound = filterRootCause(AnalysisType::SYSTEM_CHECKSTOP, isoData,
                                rootCause, rasData);
    EXPECT_TRUE(attnFound);
    EXPECT_EQ(ucsAttnSig.toUint32(), rootCause.toUint32());

    // Test 4: Test a checkstop with a non-root cause recoverable from an OCMB

    // RDFFIR[42]: SCOM recoverable register parity error
    libhei::Signature reSig{ocmbChip0, rdfFir, 0, 42,
                            libhei::ATTN_TYPE_RECOVERABLE};

    isoData.flush();
    isoData.addSignature(checkstopSig);
    isoData.addSignature(reAttnSig);
    isoData.addSignature(reSig);

    attnFound = filterRootCause(AnalysisType::SYSTEM_CHECKSTOP, isoData,
                                rootCause, rasData);
    EXPECT_TRUE(attnFound);
    EXPECT_EQ(checkstopSig.toUint32(), rootCause.toUint32());

    // Test 5: Test a firmware initiated channel fail due to an IUE threshold on
    // a Odyssey OCMB
    libhei::Chip odyChip0{ocmb0, ODYSSEY_10};

    libhei::Signature fwInitChnlFail{odyChip0, srq_fir, 0, 46,
                                     libhei::ATTN_TYPE_CHIP_CS};
    libhei::Signature mainlineIue{odyChip0, rdf_fir, 0, 18,
                                  libhei::ATTN_TYPE_RECOVERABLE};

    isoData.flush();
    isoData.addSignature(fwInitChnlFail);
    isoData.addSignature(mainlineIue);

    attnFound = filterRootCause(AnalysisType::SYSTEM_CHECKSTOP, isoData,
                                rootCause, rasData);
    EXPECT_TRUE(attnFound);
    EXPECT_EQ(mainlineIue.toUint32(), rootCause.toUint32());

    // Test 6: Test a UE that is the side effect of an ODP data corruption error
    // on an Odyssey OCMB
    libhei::Signature mainlineUe{odyChip0, rdf_fir, 0, 15,
                                 libhei::ATTN_TYPE_RECOVERABLE};
    libhei::Signature odpRootCause{odyChip0, odp_fir, 0, 6,
                                   libhei::ATTN_TYPE_RECOVERABLE};

    isoData.flush();
    isoData.addSignature(mainlineUe);
    isoData.addSignature(odpRootCause);

    attnFound = filterRootCause(AnalysisType::SYSTEM_CHECKSTOP, isoData,
                                rootCause, rasData);

    EXPECT_TRUE(attnFound);
    EXPECT_EQ(odpRootCause.toUint32(), rootCause.toUint32());
}