//---------------------------------------------------------------------------
//
// SCSI Target Emulator PiSCSI
// for Raspberry Pi
//
// Copyright (C) 2022-2023 Uwe Seimet
//
//---------------------------------------------------------------------------

#include "mocks.h"
#include "shared/piscsi_exceptions.h"
#include "devices/host_services.h"

using namespace std;

void HostServices_SetUpModePages(map<int, vector<byte>>& pages)
{
	EXPECT_EQ(1, pages.size()) << "Unexpected number of mode pages";
	EXPECT_EQ(10, pages[32].size());
}

TEST(HostServicesTest, TestUnitReady)
{
	auto [controller, services] = CreateDevice(SCHS);

    EXPECT_CALL(*controller, Status());
    services->Dispatch(scsi_command::eCmdTestUnitReady);
    EXPECT_EQ(status::good, controller->GetStatus());
}

TEST(HostServicesTest, Inquiry)
{
	TestInquiry::Inquiry(SCHS, device_type::processor, scsi_level::spc_3, "PiSCSI  Host Services   ", 0x1f, false);
}

TEST(HostServicesTest, StartStopUnit)
{
	auto [controller, services] = CreateDevice(SCHS);
	// Required by the bullseye clang++ compiler
	auto s = services;

    // STOP
    EXPECT_CALL(*controller, Status());
    services->Dispatch(scsi_command::eCmdStartStop);
    EXPECT_EQ(status::good, controller->GetStatus());

    // LOAD
	controller->SetCmdByte(4, 0x02);
    EXPECT_CALL(*controller, Status());
    services->Dispatch(scsi_command::eCmdStartStop);
    EXPECT_EQ(status::good, controller->GetStatus());

    // UNLOAD
	controller->SetCmdByte(4, 0x03);
    EXPECT_CALL(*controller, Status());
    services->Dispatch(scsi_command::eCmdStartStop);
    EXPECT_EQ(status::good, controller->GetStatus());

    // START
	controller->SetCmdByte(4, 0x01);
	EXPECT_THAT([&] { s->Dispatch(scsi_command::eCmdStartStop); }, Throws<scsi_exception>(AllOf(
			Property(&scsi_exception::get_sense_key, sense_key::illegal_request),
			Property(&scsi_exception::get_asc, asc::invalid_field_in_cdb))));
}

TEST(HostServicesTest, ModeSense6)
{
	auto [controller, services] = CreateDevice(SCHS);
	// Required by the bullseye clang++ compiler
	auto s = services;

	EXPECT_TRUE(services->Init({}));

	EXPECT_THAT([&] { s->Dispatch(scsi_command::eCmdModeSense6); }, Throws<scsi_exception>(AllOf(
			Property(&scsi_exception::get_sense_key, sense_key::illegal_request),
			Property(&scsi_exception::get_asc, asc::invalid_field_in_cdb))))
    	<< "Unsupported mode page was returned";

	controller->SetCmdByte(2, 0x20);
	EXPECT_THAT([&] { s->Dispatch(scsi_command::eCmdModeSense6); }, Throws<scsi_exception>(AllOf(
			Property(&scsi_exception::get_sense_key, sense_key::illegal_request),
			Property(&scsi_exception::get_asc, asc::invalid_field_in_cdb))))
    	<< "Block descriptors are not supported";

	controller->SetCmdByte(1, 0x08);
    // ALLOCATION LENGTH
	controller->SetCmdByte(4, 255);
    EXPECT_CALL(*controller, DataIn());
    services->Dispatch(scsi_command::eCmdModeSense6);
	vector<uint8_t>& buffer = controller->GetBuffer();
	// Major version 1
	EXPECT_EQ(0x01, buffer[6]);
	// Minor version 0
	EXPECT_EQ(0x00, buffer[7]);
	// Year
	EXPECT_NE(0x00, buffer[8]);
	// Day
	EXPECT_NE(0x00, buffer[10]);

    // ALLOCATION LENGTH
	controller->SetCmdByte(4, 2);
    EXPECT_CALL(*controller, DataIn());
    services->Dispatch(scsi_command::eCmdModeSense6);
	buffer = controller->GetBuffer();
	EXPECT_EQ(0x02, buffer[0]);
}

TEST(HostServicesTest, ModeSense10)
{
	auto [controller, services] = CreateDevice(SCHS);
	// Required by the bullseye clang++ compiler
	auto s = services;
	
	EXPECT_TRUE(services->Init({}));

	EXPECT_THAT([&] { s->Dispatch(scsi_command::eCmdModeSense10); }, Throws<scsi_exception>(AllOf(
			Property(&scsi_exception::get_sense_key, sense_key::illegal_request),
			Property(&scsi_exception::get_asc, asc::invalid_field_in_cdb))))
    	<< "Unsupported mode page was returned";

	controller->SetCmdByte(2, 0x20);
	EXPECT_THAT([&] { s->Dispatch(scsi_command::eCmdModeSense10); }, Throws<scsi_exception>(AllOf(
			Property(&scsi_exception::get_sense_key, sense_key::illegal_request),
			Property(&scsi_exception::get_asc, asc::invalid_field_in_cdb))))
    	<< "Block descriptors are not supported";

	controller->SetCmdByte(1, 0x08);
    // ALLOCATION LENGTH
	controller->SetCmdByte(8, 255);
    EXPECT_CALL(*controller, DataIn());
    services->Dispatch(scsi_command::eCmdModeSense10);
	vector<uint8_t>& buffer = controller->GetBuffer();
	// Major version 1
	EXPECT_EQ(0x01, buffer[10]);
	// Minor version 0
	EXPECT_EQ(0x00, buffer[11]);
	// Year
	EXPECT_NE(0x00, buffer[12]);
	// Day
	EXPECT_NE(0x00, buffer[14]);

    // ALLOCATION LENGTH
	controller->SetCmdByte(8, 2);
    EXPECT_CALL(*controller, DataIn());
    services->Dispatch(scsi_command::eCmdModeSense10);
	buffer = controller->GetBuffer();
	EXPECT_EQ(0x02, buffer[1]);
}

TEST(HostServicesTest, SetUpModePages)
{
	MockHostServices services(0);
	map<int, vector<byte>> pages;

	// Non changeable
	services.SetUpModePages(pages, 0x3f, false);
	HostServices_SetUpModePages(pages);

	// Changeable
	pages.clear();
	services.SetUpModePages(pages, 0x3f, true);
	HostServices_SetUpModePages(pages);
}
