#include "CppUTest/TestHarness.h"
#include "CppUTestExt/MockSupport.h"

extern "C"
{
#include "Flash.h"
#include "m28w160ect.h"
}

TEST_GROUP(Flash)
{
    IOAddress address;
    IOData data;
    int result;

    void setup()
    {
        address = 0x1000;
        data = 0xBEEF;
        result = -1;
        Flash_Create();
    }

    void teardown()
    {
        Flash_Destroy();
        mock("IO").checkExpectations();
        mock().clear();
    }

    void Expect_IOWrite(IOAddress addr, IOData data)
    {
        mock("IO")
            .expectOneCall("IOWrite")
            .withParameter("addr", (int)addr)
            .withParameter("data", (int)data);
    }

    void Expect_IORead(IOAddress addr, IOData data)
    {
        mock("IO")
            .expectOneCall("IORead")
            .withParameter("addr", (int)addr)
            .andReturnValue((int)data);
    }

};

TEST(Flash, ProgramSucceedsReadyImmediately)
{
    Expect_IOWrite(CommandRegister, ProgramCommand);
    Expect_IOWrite(address, data);
    Expect_IORead(StatusRegister, ReadyBit);
    Expect_IORead(address, data);

    result = Flash_Program(address, data);

    LONGS_EQUAL(FlashSuccess, result);
}

TEST(Flash, ProgramSucceedsNotImmediatelyReady)
{
    Expect_IOWrite(CommandRegister, ProgramCommand);
    Expect_IOWrite(address, data);
    Expect_IORead(StatusRegister, 0);
    Expect_IORead(StatusRegister, 0);
    Expect_IORead(StatusRegister, 0);
    Expect_IORead(StatusRegister, ReadyBit);
    Expect_IORead(address, data);

    result = Flash_Program(address, data);

    LONGS_EQUAL(FlashSuccess, result);
}

TEST(Flash, ProgramFails_VppError)
{
    Expect_IOWrite(CommandRegister, ProgramCommand);
    Expect_IOWrite(address, data);
    Expect_IORead(StatusRegister, ReadyBit | VppErrorBit);
    Expect_IOWrite(CommandRegister, Reset);

    result = Flash_Program(address, data);

    LONGS_EQUAL(FlashVppError, result);
}

TEST(Flash, ProgramFails_ProgramError)
{
    Expect_IOWrite(CommandRegister, ProgramCommand);
    Expect_IOWrite(address, data);
    Expect_IORead(StatusRegister, ReadyBit | ProgramErrorBit);
    Expect_IOWrite(CommandRegister, Reset);

    result = Flash_Program(address, data);

    LONGS_EQUAL(FlashProgramError, result);
}

TEST(Flash, ProgramFails_ProtectedBlockError)
{
    Expect_IOWrite(CommandRegister, ProgramCommand);
    Expect_IOWrite(address, data);
    Expect_IORead(StatusRegister, ReadyBit | BlockProtectionErrorBit);
    Expect_IOWrite(CommandRegister, Reset);

    result = Flash_Program(address, data);

    LONGS_EQUAL(FlashProgramProtectedBlockError, result);
}

TEST(Flash, ProgramFails_FlashUnknownProgramError)
{
    Expect_IOWrite(CommandRegister, ProgramCommand);
    Expect_IOWrite(address, data);
    Expect_IORead(StatusRegister, ReadyBit |  EraseSuspendBit | EraseErrorBit | ProgramSuspendBit | ReservedBit);
    Expect_IOWrite(CommandRegister, Reset);

    result = Flash_Program(address, data);

    LONGS_EQUAL(FlashUnknownProgramError, result);
}

TEST(Flash, ProgramFails_FlashReadBackError)
{
    Expect_IOWrite(CommandRegister, ProgramCommand);
    Expect_IOWrite(address, data);
    Expect_IORead(StatusRegister, ReadyBit);
    Expect_IORead(address, data-1);

    result = Flash_Program(address, data);

    LONGS_EQUAL(FlashReadBackError, result);
}

TEST(Flash, ProgramSucceeds_IgnoresOtherBitsUntilReady)
{
    Expect_IOWrite(CommandRegister, ProgramCommand);
    Expect_IOWrite(address, data);
    Expect_IORead(StatusRegister, (IOData)~ReadyBit);
    Expect_IORead(StatusRegister, ReadyBit);
    Expect_IORead(address, data);

    result = Flash_Program(address, data);

    LONGS_EQUAL(FlashSuccess, result);
}
