// SPDX-License-Identifier: Apache-2.0
/*
 * Copyright (C) 2021 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
 */

#include <iostream>
#include <vector>

#include "display.hpp"
#include "flashInterface.hpp"
#include "spiFlash.hpp"

FlashInterface::FlashInterface():_spif_verbose(0), _spif_rd_burst(0),
	_spif_verify(false), _skip_load_bridge(false)
{}

FlashInterface::FlashInterface(const std::string &filename, int8_t verbose,
		uint32_t rd_burst, bool verify, bool skip_load_bridge,
		bool skip_reset):
	_spif_verbose(verbose), _spif_rd_burst(rd_burst),
	_spif_verify(verify), _skip_load_bridge(skip_load_bridge),
	_skip_reset(skip_reset), _spif_filename(filename)
{}

/* spiFlash generic acces */
bool FlashInterface::detect_flash()
{
	bool ret = true;

	printInfo("Detect flash:");

	/* move device to spi access */
	if (!prepare_flash_access()) {
		printError("Fail");
		return false;
	}

	/* spi flash access */
	try {
		// instanciate call (display flash ID is automatic)
		SPIFlash flash(this, false, _spif_verbose);
		// display status register
		flash.display_status_reg();

		printSuccess("Done");
	} catch (std::exception &e) {
		printError("Fail");
		printError(e.what());
		ret = false;
	}

	/* reload bitstream */
	return post_flash_access() && ret;
}

bool FlashInterface::protect_flash(uint32_t len)
{
	bool ret = true;
	printInfo("protect_flash:");

	/* move device to spi access */
	if (!prepare_flash_access()) {
		printError("Fail");
		return false;
	}

	/* spi flash access */
	try {
		SPIFlash flash(this, false, _spif_verbose);

		/* configure flash protection */
		ret = (flash.enable_protection(len) == 0);
		if (!ret)
			printError("Fail");
		else
			printSuccess("Done");
	} catch (std::exception &e) {
		printError("Fail");
		printError(e.what());
		ret = false;
	}

	/* reload bitstream */
	return post_flash_access() && ret;
}

bool FlashInterface::unprotect_flash()
{
	bool ret = true;

	/* move device to spi access */
	if (!prepare_flash_access()) {
		printError("SPI Flash prepare access failed");
		return false;
	}

	/* spi flash access */
	try {
		SPIFlash flash(this, false, _spif_verbose);

		/* configure flash protection */
		printInfo("unprotect_flash:");
		ret = (flash.disable_protection() == 0);
		if (!ret)
			printError("Fail");
		else
			printSuccess("Done");
	} catch (std::exception &e) {
		printError("SPI Flash access failed: ", false);
		printError(e.what());
		ret = false;
	}

	/* reload bitstream */
	return post_flash_access() && ret;
}

bool FlashInterface::set_quad_bit(bool set_quad)
{
	bool ret = true;

	/* move device to spi access */
	if (!prepare_flash_access()) {
		printError("SPI Flash prepare access failed");
		return false;
	}

	/* spi flash access */
	try {
		SPIFlash flash(this, false, _spif_verbose);

		/* configure flash protection */
		printInfo("set_quad_bit:");
		ret = flash.set_quad_bit(set_quad);
		if (!ret)
			printError("Fail");
		else
			printSuccess("Done");
	} catch (std::exception &e) {
		printError("SPI Flash access failed: ", false);
		printError(e.what());
		ret = false;
	}

	/* reload bitstream */
	return post_flash_access() && ret;
}

bool FlashInterface::bulk_erase_flash()
{
	bool ret = true;
	printInfo("bulk_erase:");

	/* move device to spi access */
	if (!prepare_flash_access()) {
		printError("Fail");
		return false;
	}

	/* spi flash access */
	try {
		SPIFlash flash(this, false, _spif_verbose);

		/* bulk erase flash */
		ret = (flash.bulk_erase() == 0);
		if (!ret)
			printError("Fail");
		else
			printSuccess("Done");
	} catch (std::exception &e) {
		printError("Fail");
		printError(e.what());
		ret = false;
	}

	/* reload bitstream */
	return post_flash_access() && ret;
}

bool FlashInterface::write(const std::vector<FlashDataSection>&sections,
		bool unprotect_flash, bool full_erase)
{
	bool ret = true;
	if (!prepare_flash_access())
		return false;

	/* test SPI */
	try {
		SPIFlash flash(this, unprotect_flash, _spif_verbose);
		flash.read_status_reg();
		if (!flash.erase_and_prog(sections, full_erase))
			ret = false;
		if (_spif_verify && ret)
			printInfo("Verify not supported in this mode");
			//ret = flash.verify(offset, data, len, _spif_rd_burst);
	} catch (std::exception &e) {
		printError(e.what());
		ret = false;
	}

	bool ret2 = post_flash_access();
	return ret && ret2;
}

bool FlashInterface::write(uint32_t offset, const uint8_t *data, uint32_t len,
		bool unprotect_flash)
{
	bool ret = true;
	if (!prepare_flash_access())
		return false;

	/* test SPI */
	try {
		SPIFlash flash(this, unprotect_flash, _spif_verbose);
		restore_flash_access_frequency();
		flash.read_status_reg();
		if (flash.erase_and_prog(offset, data, len) == -1)
			ret = false;
		if (_spif_verify && ret)
			ret = flash.verify(offset, data, len, _spif_rd_burst);
	} catch (std::exception &e) {
		printError(e.what());
		ret = false;
	}

	bool ret2 = post_flash_access();
	return ret && ret2;
}

bool FlashInterface::read(uint8_t *data, uint32_t base_addr, uint32_t len)
{
	bool ret = true;
	/* enable SPI flash access */
	if (!prepare_flash_access())
		return false;

	try {
		SPIFlash flash(this, false, _spif_verbose);
		ret = flash.read(base_addr, data, len);
	} catch (std::exception &e) {
		printError(e.what());
		ret = false;
	}

	/* reload bitstream */
	return post_flash_access() && ret == 0;
}

bool FlashInterface::dump(uint32_t base_addr, uint32_t len)
{
	bool ret = true;
	/* enable SPI flash access */
	if (!prepare_flash_access())
		return false;

	try {
		SPIFlash flash(this, false, _spif_verbose);
		ret = flash.dump(_spif_filename, base_addr, len, _spif_rd_burst);
	} catch (std::exception &e) {
		printError(e.what());
		ret = false;
	}

	/* reload bitstream */
	return post_flash_access() && ret;
}
