// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "nvs_partition.hpp"
#include "nvs_encrypted_partition.hpp"
#include "spi_flash_emulation.h"
#include "nvs.h"

class PartitionEmulation : public nvs::Partition {
public:
    PartitionEmulation(SpiFlashEmulator *spi_flash_emulator,
            uint32_t address,
            uint32_t size,
            const char *partition_name = NVS_DEFAULT_PART_NAME)
        : partition_name(partition_name), flash_emu(spi_flash_emulator), address(address), size(size)
    {
        assert(partition_name);
        assert(flash_emu);
        assert(size);
    }

    const char *get_partition_name() override
    {
        return partition_name;
    }

    esp_err_t read_raw(size_t src_offset, void* dst, size_t size) override
    {
        if (!flash_emu->read(reinterpret_cast<uint32_t*>(dst), src_offset, size)) {
            return ESP_ERR_FLASH_OP_FAIL;
        }

        return ESP_OK;
    }

    esp_err_t read(size_t src_offset, void* dst, size_t size) override
    {
        if (!flash_emu->read(reinterpret_cast<uint32_t*>(dst), src_offset, size)) {
            return ESP_ERR_FLASH_OP_FAIL;
        }

        return ESP_OK;
    }

    esp_err_t write_raw(size_t dst_offset, const void* src, size_t size) override
    {
        if (!flash_emu->write(dst_offset, reinterpret_cast<const uint32_t*>(src), size)) {
            return ESP_ERR_FLASH_OP_FAIL;
        }

        return ESP_OK;
    }

    esp_err_t write(size_t dst_offset, const void* src, size_t size) override
    {
        if (!flash_emu->write(dst_offset, reinterpret_cast<const uint32_t*>(src), size)) {
            return ESP_ERR_FLASH_OP_FAIL;
        }

        return ESP_OK;
    }

    esp_err_t erase_range(size_t dst_offset, size_t size) override
    {
        if (size % SPI_FLASH_SEC_SIZE != 0) {
            return ESP_ERR_INVALID_SIZE;
        }

        if (dst_offset % SPI_FLASH_SEC_SIZE != 0) {
            return ESP_ERR_INVALID_ARG;
        }

        size_t start_sector = dst_offset / SPI_FLASH_SEC_SIZE;
        size_t num_sectors = size / SPI_FLASH_SEC_SIZE;
        for (size_t sector = start_sector; sector < (start_sector + num_sectors); sector++) {
            if (!flash_emu->erase(sector)) {
                return ESP_ERR_FLASH_OP_FAIL;
            }
        }

        return ESP_OK;
    }

    uint32_t get_address() override
    {
        return address;
    }

    uint32_t get_size() override
    {
        return size;
    }

private:
    const char *partition_name;

    SpiFlashEmulator *flash_emu;

    uint32_t address;

    uint32_t size;
};

struct PartitionEmulationFixture {
    PartitionEmulationFixture(uint32_t start_sector = 0,
            uint32_t sector_size = 1,
            const char *partition_name = NVS_DEFAULT_PART_NAME)
        : emu(start_sector + sector_size),
          part(&emu, start_sector * SPI_FLASH_SEC_SIZE, sector_size * SPI_FLASH_SEC_SIZE, partition_name) {
    }

    ~PartitionEmulationFixture() { }

    SpiFlashEmulator emu;

    PartitionEmulation part;
};

struct EncryptedPartitionFixture {
    EncryptedPartitionFixture(nvs_sec_cfg_t *cfg,
            uint32_t start_sector = 0,
            uint32_t sector_size = 1,
            const char *partition_name = NVS_DEFAULT_PART_NAME)
        : esp_partition(), emu(start_sector + sector_size),
          part(&esp_partition) {
        esp_partition.address = start_sector * SPI_FLASH_SEC_SIZE;
        esp_partition.size = sector_size * SPI_FLASH_SEC_SIZE;
        strncpy(esp_partition.label, partition_name, PART_NAME_MAX_SIZE);
        assert(part.init(cfg) == ESP_OK);
    }

    ~EncryptedPartitionFixture() { }

    esp_partition_t esp_partition;

    SpiFlashEmulator emu;

    nvs::NVSEncryptedPartition part;
};