/* kernel headers */
#include <minix/blockdriver.h>

/* usr headers */
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>

/* local headers */
#include "mmclog.h"
#include "mmchost.h"
#include "sdmmcreg.h"

/*
 * Define a structure to be used for logging
 */
static struct mmclog log = {
	.name = "mmc_host_memory",
	.log_level = LEVEL_TRACE,
	.log_func = default_log
};

/* This is currently a dummy driver using an in-memory structure */
#define DUMMY_SIZE_IN_BLOCKS 0xFFFu
#define DUMMY_BLOCK_SIZE 512
static char dummy_data[DUMMY_BLOCK_SIZE * DUMMY_SIZE_IN_BLOCKS];

static struct sd_card *
init_dummy_sdcard(struct sd_slot *slot)
{
	int i;
	struct sd_card *card;

	assert(slot != NULL);

	mmc_log_info(&log, "Using a dummy card \n");

	card = &slot->card;
	memset(card, 0, sizeof(struct sd_card));
	card->slot = slot;

	for (i = 0; i < MINOR_PER_DISK + PARTITONS_PER_DISK; i++) {
		card->part[i].dv_base = 0;
		card->part[i].dv_size = 0;
	}

	for (i = 0; i < PARTITONS_PER_DISK * SUBPARTITION_PER_PARTITION; i++) {
		card->subpart[i].dv_base = 0;
		card->subpart[i].dv_size = 0;
	}

	card->part[0].dv_base = 0;
	card->part[0].dv_size = DUMMY_BLOCK_SIZE * DUMMY_SIZE_IN_BLOCKS;
	return card;
}

int
dummy_host_init(struct mmc_host *host)
{
	return 0;
}

void
dummy_set_log_level(int level)
{
	if (level >= 0 && level <= 4) {
		log.log_level = level;
	}
}

int
dummy_host_set_instance(struct mmc_host *host, int instance)
{
	mmc_log_info(&log, "Using instance number %d\n", instance);
	if (instance != 0) {
		return EIO;
	}
	return OK;
}

int
dummy_host_reset(struct mmc_host *host)
{
	return 0;
}

int
dummy_card_detect(struct sd_slot *slot)
{
	return 1;
}

struct sd_card *
dummy_card_initialize(struct sd_slot *slot)
{
	slot->card.blk_size = DUMMY_BLOCK_SIZE;
	slot->card.blk_count = DUMMY_SIZE_IN_BLOCKS;
	slot->card.state = SD_MODE_DATA_TRANSFER_MODE;

	memset(slot->card.part, 0, sizeof(slot->card.part));
	memset(slot->card.subpart, 0, sizeof(slot->card.subpart));
	slot->card.part[0].dv_base = 0;
	slot->card.part[0].dv_size = DUMMY_BLOCK_SIZE * DUMMY_SIZE_IN_BLOCKS;
	return &slot->card;
}

int
dummy_card_release(struct sd_card *card)
{
	assert(card->open_ct == 1);
	card->open_ct--;
	card->state = SD_MODE_UNINITIALIZED;
	/* TODO:Set card state */
	return OK;
}

/* read count blocks into existing buf */
int
dummy_host_read(struct sd_card *card,
    uint32_t blknr, uint32_t count, unsigned char *buf)
{
	memcpy(buf, &dummy_data[blknr * DUMMY_BLOCK_SIZE],
	    count * DUMMY_BLOCK_SIZE);
	return OK;
}

/* write count blocks */
int
dummy_host_write(struct sd_card *card,
    uint32_t blknr, uint32_t count, unsigned char *buf)
{
	memcpy(&dummy_data[blknr * DUMMY_BLOCK_SIZE], buf,
	    count * DUMMY_BLOCK_SIZE);
	return OK;
}

void
host_initialize_host_structure_dummy(struct mmc_host *host)
{
	/* Initialize the basic data structures host slots and cards */
	int i;

	host->host_set_instance = dummy_host_set_instance;
	host->host_init = dummy_host_init;
	host->set_log_level = dummy_set_log_level;
	host->host_reset = dummy_host_reset;
	host->card_detect = dummy_card_detect;
	host->card_initialize = dummy_card_initialize;
	host->card_release = dummy_card_release;
	host->read = dummy_host_read;
	host->write = dummy_host_write;

	/* initialize data structures */
	for (i = 0; i < sizeof(host->slot) / sizeof(host->slot[0]); i++) {
		// @TODO set initial card and slot state
		host->slot[i].host = host;
		host->slot[i].card.slot = &host->slot[i];
	}
	init_dummy_sdcard(&host->slot[0]);
}
