#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include "podule_api.h"
#include "cdrom.h"

enum
{
	ID_PATH,
	ID_BLOCKS,
	ID_SIZE,
	ID_ID_0,
	ID_ID_1,
	ID_ID_2,
	ID_ID_3,
	ID_ID_4,
	ID_ID_5,
	ID_ID_6,
	ID_TYPE_0,
	ID_TYPE_1,
	ID_TYPE_2,
	ID_TYPE_3,
	ID_TYPE_4,
	ID_TYPE_5,
	ID_TYPE_6
};

static int MAX_BLOCKS = (1 << 24) - 1;
static int SECTOR_SIZE = 512;
#define MAX_SIZE (MAX_BLOCKS / (1024 * 1024 / SECTOR_SIZE))

static const podule_callbacks_t *podule_callbacks;

static int changed_blocks(void *window_p, const struct podule_config_item_t *item, void *new_data)
{
	char *new_s = new_data;
	char *temp_s;
	char size_s[80];
	int blocks;
	int size;

	temp_s = podule_callbacks->config_get_current(window_p, ID_BLOCKS);
	blocks = atoi(temp_s);
	if (blocks > MAX_BLOCKS)
	{
		blocks = MAX_BLOCKS;
		snprintf(size_s, sizeof(size_s), "%i", blocks);
		podule_callbacks->config_set_current(window_p, ID_BLOCKS, size_s);
	}

	size = blocks / (1024 * 1024 / SECTOR_SIZE);
	snprintf(size_s, sizeof(size_s), "%i", size);
	podule_callbacks->config_set_current(window_p, ID_SIZE, size_s);

	return 0;
}

static int changed_size(void *window_p, const struct podule_config_item_t *item, void *new_data)
{
	char *new_s = new_data;
	char *temp_s;
	char size_s[80];
	int blocks;
	int size;

	temp_s = podule_callbacks->config_get_current(window_p, ID_SIZE);
	size = atoi(temp_s);
	if (size > MAX_SIZE)
	{
		size = MAX_SIZE;
		snprintf(size_s, sizeof(size_s), "%i", size);
		podule_callbacks->config_set_current(window_p, ID_SIZE, size_s);
	}

	blocks = size * (1024 * 1024 / 512);

	snprintf(size_s, sizeof(size_s), "%i", blocks);
	podule_callbacks->config_set_current(window_p, ID_BLOCKS, size_s);

	return 0;
}

int change_path(void *window_p, const struct podule_config_item_t *item, void *new_data)
{
	char fn[256];

	if (!podule_callbacks->config_file_selector(window_p, "Please enter a file name",
			NULL, NULL, NULL, "HDF files (*.hdf)|*.hdf", fn, sizeof(fn), CONFIG_FILESEL_SAVE))
	{
//                rpclog("Filename: %s\n", fn);

		podule_callbacks->config_set_current(window_p, ID_PATH, fn);
	}
	return 0;
}

static int new_blocks;
static char new_fn[256];
static int new_drive_valid;

static void new_drive_init(void *window_p)
{
	char size_s[80];

	snprintf(size_s, sizeof(size_s), "%i", 102400);
	podule_callbacks->config_set_current(window_p, ID_BLOCKS, size_s);
}

static int new_drive_close(void *window_p)
{
	FILE *f;
	char *temp_s;
	int blocks;
//        int size;

	temp_s = podule_callbacks->config_get_current(window_p, ID_BLOCKS);
	blocks = atoi(temp_s);

	temp_s = podule_callbacks->config_get_current(window_p, ID_PATH);

	f = fopen(temp_s, "wb");
	if (f)
	{
		/*Write out disc image*/
		uint8_t data[512];
		int c;

		memset(data, 0, SECTOR_SIZE);
		for (c = 0; c < blocks; c++)
			fwrite(data, SECTOR_SIZE, 1, f);
		fclose(f);

		new_blocks = blocks;
		strncpy(new_fn, temp_s, sizeof(new_fn));
		new_drive_valid = 1;
	}

	return 0;
}

static podule_config_t scsi_new_drive_config =
{
	.title = "New drive",
	.init = new_drive_init,
	.close = new_drive_close,
	.items =
	{
		{
			.description = "Path :",
			.type = CONFIG_STRING,
			.flags = CONFIG_FLAGS_DISABLED,
			.default_string = "",
			.id = ID_PATH
		},
		{
			.description = "Change path...",
			.type = CONFIG_BUTTON,
			.function = change_path
		},
		{
			.description = "Blocks :",
			.type = CONFIG_STRING,
			.default_string = "102400",
			.id = ID_BLOCKS,
			.function = changed_blocks
		},
		{
			.description = "Size (MB) :",
			.type = CONFIG_STRING,
			.default_string = "50",
			.id = ID_SIZE,
			.function = changed_size
		},
		{
			.type = -1
		}
	}
};

static int config_new_drive(void *window_p, const struct podule_config_item_t *item, void *new_data)
{
	new_drive_valid = 0;

	podule_callbacks->config_open(window_p, &scsi_new_drive_config, NULL);

	if (new_drive_valid)
	{
		char temp_s[80];

		snprintf(temp_s, sizeof(temp_s), "%i", new_blocks);
		podule_callbacks->config_set_current(window_p, ID_BLOCKS, temp_s);
		snprintf(temp_s, sizeof(temp_s), "%i", new_blocks / (1024 * 1024 / 512));
		podule_callbacks->config_set_current(window_p, ID_SIZE, temp_s);
		podule_callbacks->config_set_current(window_p, ID_PATH, new_fn);

		return 1;

	}

	return 0;
}

static void drive_load_open(void *window_p)
{
	char temp_s[80];

	snprintf(temp_s, sizeof(temp_s), "%i", new_blocks);
	podule_callbacks->config_set_current(window_p, ID_BLOCKS, temp_s);
	snprintf(temp_s, sizeof(temp_s), "%i", new_blocks / (1024 * 1024 / SECTOR_SIZE));
	podule_callbacks->config_set_current(window_p, ID_SIZE, temp_s);
}

static int drive_load_close(void *window_p)
{
	char *temp_s;

	temp_s = podule_callbacks->config_get_current(window_p, ID_BLOCKS);
	new_blocks = atoi(temp_s);

	return 1;
}

static podule_config_t scsi_load_drive_config =
{
	.title = "Drive geometry",
	.init = drive_load_open,
	.close = drive_load_close,
	.items =
	{
		{
			.description = "Blocks :",
			.type = CONFIG_STRING,
			.default_string = "102400",
			.id = ID_BLOCKS,
			.function = changed_blocks
		},
		{
			.description = "Size (MB) :",
			.type = CONFIG_STRING,
			.default_string = "50",
			.id = ID_SIZE,
			.function = changed_size
		},
		{
			.type = -1
		}
	}
};

static int config_load_drive(void *window_p, const struct podule_config_item_t *item, void *new_data)
{
	char fn[256];

	if (!podule_callbacks->config_file_selector(window_p, "Please select a hard drive image",
			NULL, NULL, NULL, "HDF files (*.hdf)|*.hdf", fn, sizeof(fn), CONFIG_FILESEL_LOAD))
	{
		FILE *f;
		int filesize;
		int log2secsize, density;
		char temp_s[256];

//                rpclog("Filename: %s\n", fn);

		f = fopen(fn, "rb");
		if (!f)
			return 0;
		fseek(f, -1, SEEK_END);
		filesize = ftell(f) + 1;
		fclose(f);

		snprintf(temp_s, sizeof(temp_s), "%i", filesize / 512);
		podule_callbacks->config_set_current(window_p, ID_BLOCKS, temp_s);
		snprintf(temp_s, sizeof(temp_s), "%i", filesize / (1024 * 1024));
		podule_callbacks->config_set_current(window_p, ID_SIZE, temp_s);
		podule_callbacks->config_set_current(window_p, ID_PATH, fn);

		return 1;
	}

	return 0;
}

static int config_eject_drive(void *window_p, const struct podule_config_item_t *item, void *new_data)
{
	podule_callbacks->config_set_current(window_p, ID_BLOCKS, "");
	podule_callbacks->config_set_current(window_p, ID_SIZE, "");
	podule_callbacks->config_set_current(window_p, ID_PATH, "");

	return 1;
}

static void drive_edit_open(void *window_p)
{
	changed_blocks(window_p, NULL, NULL);
}

static podule_config_t scsi_drive_edit_config =
{
	.title = "Drive configuration",
	.init = drive_edit_open,
	.items =
	{
		{
			.name = "fn",
			.description = "Path :",
			.type = CONFIG_STRING,
			.flags = CONFIG_FLAGS_DISABLED | CONFIG_FLAGS_NAME_PREFIXED,
			.id = ID_PATH
		},
		{
			.name = "size",
			.description = "Blocks :",
			.type = CONFIG_STRING,
			.flags = CONFIG_FLAGS_DISABLED | CONFIG_FLAGS_NAME_PREFIXED,
			.id = ID_BLOCKS
		},
		{
			.description = "Size (MB) :",
			.type = CONFIG_STRING,
			.flags = CONFIG_FLAGS_DISABLED,
			.default_string = "50",
			.id = ID_SIZE,
			.function = changed_size
		},
		{
			.description = "New drive...",
			.type = CONFIG_BUTTON,
			.function = config_new_drive
		},
		{
			.description = "Load drive...",
			.type = CONFIG_BUTTON,
			.function = config_load_drive
		},
		{
			.description = "Eject drive",
			.type = CONFIG_BUTTON,
			.function = config_eject_drive
		},
		{
			.type = -1
		}
	}
};

static podule_config_t scsi_cdrom_config =
{
	.title = "CD-ROM drive configuration",
	.items =
	{
		{
			.name = "fn",
			.description = "Host CD-ROM drive",
			.type = CONFIG_SELECTION_STRING,
			.flags = CONFIG_FLAGS_NAME_PREFIXED,
			.selection = NULL,
			.default_string = ""
		},
		{
			.type = -1
		}
	}
};

static podule_config_t scsi_invalid_config =
{
	.title = "No drive to configure",
	.items =
	{
		{
			.type = -1
		}
	}
};

static int config_drive(void *window_p, const struct podule_config_item_t *item, void *new_data)
{
	char prefix[9];
	const char *type;

	switch (item->id)
	{
		case ID_ID_0:
		sprintf(prefix, "device0_");
		type = podule_callbacks->config_get_current(window_p, ID_TYPE_0);
		break;
		case ID_ID_1:
		sprintf(prefix, "device1_");
		type = podule_callbacks->config_get_current(window_p, ID_TYPE_1);
		break;
		case ID_ID_2:
		sprintf(prefix, "device2_");
		type = podule_callbacks->config_get_current(window_p, ID_TYPE_2);
		break;
		case ID_ID_3:
		sprintf(prefix, "device3_");
		type = podule_callbacks->config_get_current(window_p, ID_TYPE_3);
		break;
		case ID_ID_4:
		sprintf(prefix, "device4_");
		type = podule_callbacks->config_get_current(window_p, ID_TYPE_4);
		break;
		case ID_ID_5:
		sprintf(prefix, "device5_");
		type = podule_callbacks->config_get_current(window_p, ID_TYPE_5);
		break;
		case ID_ID_6:
		sprintf(prefix, "device6_");
		type = podule_callbacks->config_get_current(window_p, ID_TYPE_6);
		break;
	}

	if (!strcmp(type, "Hard drive"))
		podule_callbacks->config_open(window_p, &scsi_drive_edit_config, prefix);
	else if (!strcmp(type, "CD-ROM drive"))
		podule_callbacks->config_open(window_p, &scsi_cdrom_config, prefix);
	else
		podule_callbacks->config_open(window_p, &scsi_invalid_config, NULL);

	return 0;
}

int change_type(void *window_p, const struct podule_config_item_t *item, void *new_data)
{
	if (!strcmp(new_data, "CD-ROM drive"))
	{
		/*Only one CD-ROM drive allowed currently, so remove any duplicates*/
		int c;

		for (c = ID_TYPE_0; c <= ID_TYPE_6; c++)
		{
			if (c != item->id)
			{
				const char *type = podule_callbacks->config_get_current(window_p, c);

				if (!strcmp(type, "CD-ROM drive"))
					podule_callbacks->config_set_current(window_p, c, "None");
			}
		}

	}

	return 0;
}

static void scsi_podule_config_init(void *window_p)
{
}

static podule_config_selection_t device_type_sel[] =
{
	{
		.description = "None",
		.value_string = "none"
	},
	{
		.description = "Hard drive",
		.value_string = "hd"
	},
	{
		.description = "CD-ROM drive",
		.value_string = "cd"
	},
	{
		.description = "",
		.value_string = ""
	}
};

podule_config_t scsi_podule_config =
{
	.title = "SCSI device configuration",
	.init = scsi_podule_config_init,
	.items =
	{
		{
			.name = "device0_type",
			.description = "ID 0 type",
			.type = CONFIG_SELECTION_STRING,
			.selection = device_type_sel,
			.default_string = "none",
			.function = change_type,
			.id = ID_TYPE_0
		},
		{
			.description = "Configure ID 0...",
			.type = CONFIG_BUTTON,
			.function = config_drive,
			.id = ID_ID_0
		},
		{
			.name = "device1_type",
			.description = "ID 1 type",
			.type = CONFIG_SELECTION_STRING,
			.selection = device_type_sel,
			.default_string = "none",
			.function = change_type,
			.id = ID_TYPE_1
		},
		{
			.description = "Configure ID 1...",
			.type = CONFIG_BUTTON,
			.function = config_drive,
			.id = ID_ID_1
		},
		{
			.name = "device2_type",
			.description = "ID 2 type",
			.type = CONFIG_SELECTION_STRING,
			.selection = device_type_sel,
			.default_string = "none",
			.function = change_type,
			.id = ID_TYPE_2
		},
		{
			.description = "Configure ID 2...",
			.type = CONFIG_BUTTON,
			.function = config_drive,
			.id = ID_ID_2
		},
		{
			.name = "device3_type",
			.description = "ID 3 type",
			.type = CONFIG_SELECTION_STRING,
			.selection = device_type_sel,
			.default_string = "none",
			.function = change_type,
			.id = ID_TYPE_3
		},
		{
			.description = "Configure ID 3...",
			.type = CONFIG_BUTTON,
			.function = config_drive,
			.id = ID_ID_3
		},
		{
			.name = "device4_type",
			.description = "ID 4 type",
			.type = CONFIG_SELECTION_STRING,
			.selection = device_type_sel,
			.default_string = "none",
			.function = change_type,
			.id = ID_TYPE_4
		},
		{
			.description = "Configure ID 4...",
			.type = CONFIG_BUTTON,
			.function = config_drive,
			.id = ID_ID_4
		},
		{
			.name = "device5_type",
			.description = "ID 5 type",
			.type = CONFIG_SELECTION_STRING,
			.selection = device_type_sel,
			.default_string = "none",
			.function = change_type,
			.id = ID_TYPE_5
		},
		{
			.description = "Configure ID 5...",
			.type = CONFIG_BUTTON,
			.function = config_drive,
			.id = ID_ID_5
		},
		{
			.name = "device6_type",
			.description = "ID 6 type",
			.type = CONFIG_SELECTION_STRING,
			.selection = device_type_sel,
			.default_string = "none",
			.function = change_type,
			.id = ID_TYPE_6
		},
		{
			.description = "Configure ID 6...",
			.type = CONFIG_BUTTON,
			.function = config_drive,
			.id = ID_ID_6
		},
		{
			.type = -1
		}
	}
};

void scsi_config_init(const podule_callbacks_t *callbacks)
{
	scsi_cdrom_config.items[0].selection = cdrom_devices_config();
	podule_callbacks = callbacks;
}
