// Copyright (c) 2020 Metrolab Technology S.A., Geneva, Switzerland (www.metrolab.com)
// See the included file LICENSE.txt for the licensing conditions.

//////////////////////////////////////////////////////////////////////////
/// \file
/// \brief Test IEEE488 Instrument: Interrupt methods.

#pragma once

#include <gtest/gtest.h>
#include <regex>

#include "IEEE488InstrumentTest.h"

using namespace testing;

class IEEE488InstrumentInterruptTest : public ::testing::Test
{
protected:
    static IEEE4888_TEST_RESOURCE_MANAGER_CLASS *	pResourceManager;
    static IEEE4888_TEST_INSTRUMENT_CLASS *         pInstrument;

    static void SetUpTestCase()
    {
        ASSERT_EQ(true, ConnectToIEEE488Instrument(pResourceManager, pInstrument));
        ASSERT_NE(nullptr, pResourceManager);
        ASSERT_NE(nullptr, pInstrument);
        ASSERT_EQ(true, pInstrument->IsOpen());
        ASSERT_EQ(true, pInstrument->Clear());
    }

    static void TearDownTestCase()
    {
        delete pInstrument;
        pInstrument = nullptr;
        delete pResourceManager;
        pResourceManager = nullptr;
    }
};

IEEE4888_TEST_RESOURCE_MANAGER_CLASS *  IEEE488InstrumentInterruptTest::pResourceManager    = nullptr;
IEEE4888_TEST_INSTRUMENT_CLASS *        IEEE488InstrumentInterruptTest::pInstrument         = nullptr;

/// \brief Test EnableEvent and DisableEvent.
TEST_F(IEEE488InstrumentInterruptTest, EnableDisable)
{
    // Enable and disable.
    ASSERT_EQ(true, pInstrument->EnableEvent());
    ASSERT_EQ(true, pInstrument->DisableEvent());

    // Test a repeat, and that a second enable or a second disable works.
    ASSERT_EQ(true, pInstrument->EnableEvent());
    ASSERT_EQ(true, pInstrument->EnableEvent());

    ASSERT_EQ(true, pInstrument->DisableEvent());
    ASSERT_EQ(true, pInstrument->DisableEvent());
}

/// \brief Test WaitOnEvent.
TEST_F(IEEE488InstrumentInterruptTest, WaitOnEvent)
{
    // Enable interrupts.
    ASSERT_EQ(true, pInstrument->EnableEvent());
    ASSERT_EQ(true, pInstrument->Write("*SRE 16"));

    // Generate two interrupts.
    CSCPIBuffer l_Buffer;
    ASSERT_EQ(true, pInstrument->Write("*IDN?"));
    ASSERT_EQ(true, pInstrument->Read(l_Buffer));

    ASSERT_EQ(true, pInstrument->Write("*IDN?"));
    ASSERT_EQ(true, pInstrument->Read(l_Buffer));

    // Service the first one without timeout.
    ASSERT_EQ(true, pInstrument->WaitOnEvent(0));

    // Service the second one with 1-second timeout,
    ASSERT_EQ(true, pInstrument->WaitOnEvent(1000u));

    // Try to service another interrupt, to test the timeout.
    ASSERT_EQ(false, pInstrument->WaitOnEvent(1000u));

    // Disable interrupts and verify that we don't receive interrupts.
    ASSERT_EQ(true, pInstrument->DisableEvent());

    ASSERT_EQ(true, pInstrument->Write("*IDN?"));
    ASSERT_EQ(true, pInstrument->Read(l_Buffer));

    ASSERT_EQ(false, pInstrument->WaitOnEvent(1000u));
}

/// \brief Test DiscardEvents.
TEST_F(IEEE488InstrumentInterruptTest, DiscardInterrupts)
{
    // Enable interrupts.
    ASSERT_EQ(true, pInstrument->EnableEvent());
    ASSERT_EQ(true, pInstrument->Write("*SRE 16"));

    // Generate two interrupts.
    CSCPIBuffer l_Buffer;
    ASSERT_EQ(true, pInstrument->Write("*IDN?"));
    ASSERT_EQ(true, pInstrument->Read(l_Buffer));

    ASSERT_EQ(true, pInstrument->Write("*IDN?"));
    ASSERT_EQ(true, pInstrument->Read(l_Buffer));

    // Wait a moment to ensure that the interrupts have been serviced.
    std::this_thread::sleep_for(std::chrono::seconds(1));

    // Discard the events.
    ASSERT_EQ(true, pInstrument->DiscardEvents());

    // Try to service another interrupt, to verify that the interrupts have been discarded.
    ASSERT_EQ(false, pInstrument->WaitOnEvent(1000u));
}

/// \brief Test mixing Status Byte and Service Requests.
/// Note: in USBTMC, both of these arrive on the Interrupt-In endpoint.
TEST_F(IEEE488InstrumentInterruptTest, MixSRQandSTB)
{
    // Enable interrupts.
    ASSERT_EQ(true, pInstrument->EnableEvent());
    ASSERT_EQ(true, pInstrument->Write("*SRE 16"));

    // Generate two interrupts.
    CSCPIBuffer l_Buffer;
    ASSERT_EQ(true, pInstrument->Write("*IDN?"));
    ASSERT_EQ(true, pInstrument->Read(l_Buffer));

    ASSERT_EQ(true, pInstrument->Write("*IDN?"));
    ASSERT_EQ(true, pInstrument->Read(l_Buffer));

    // Read STB.
    U16 l_StatusByte;
    ASSERT_EQ(true, pInstrument->ReadSTB(l_StatusByte));

    // Generate two more interrupts.
    ASSERT_EQ(true, pInstrument->Write("*IDN?"));
    ASSERT_EQ(true, pInstrument->Read(l_Buffer));

    ASSERT_EQ(true, pInstrument->Write("*IDN?"));
    ASSERT_EQ(true, pInstrument->Read(l_Buffer));

    // Recover the four interrupts..
    ASSERT_EQ(true, pInstrument->WaitOnEvent(1000u));
    ASSERT_EQ(true, pInstrument->WaitOnEvent(1000u));
    ASSERT_EQ(true, pInstrument->WaitOnEvent(1000u));
    ASSERT_EQ(true, pInstrument->WaitOnEvent(1000u));

    // Try to service another interrupt, to verify that there are no more.
    ASSERT_EQ(false, pInstrument->WaitOnEvent(1000u));
}
