#include "blue-dream.h"

t_dword lbyte(t_dword x) { return x & 0x00ff; }
t_dword hbyte(t_dword x) { return x & 0xff00; }
t_dword lword(t_dword x) { return x & 0x0000ffff; }
t_dword hword(t_dword x) { return x & 0xffff0000; }

t_dword bswap(t_dword x) { return hword(x) | lword(lbyte(x) << 8) | (hbyte(x) >> 8); }
t_dword wswap(t_dword x) { return (lword(x) << 16) | (hword(x) >> 16); }

t_unicode *create_string(int length)
{
	if (length == 0) return NULL;

	t_unicode *x = (t_unicode *)malloc((length + 1) * sizeof(t_unicode));
	if (x == NULL) return NULL;

	int index;
	for (index = 0; index < length; index++)
		x[index] = ' ';

	x[length] = '\0';
	return x;
}

t_unicode *hex_string(int x)
{
	int y_length = snprintf(NULL, 0, "%#010x", x);
	t_unicode *y = (t_unicode *)malloc((y_length + 1) * sizeof(t_unicode));

	int result = snprintf(y, y_length + 1, "%#010x", x);
	if (result <= 0) return NULL;

	return y;
}

t_unicode *int_string(int x)
{
	int y_length = snprintf(NULL, 0, "%d", x);
	t_unicode *y = (t_unicode *)malloc((y_length + 1) * sizeof(t_unicode));

	int result = snprintf(y, y_length + 1, "%d", x);
	if (result <= 0) return NULL;

	return y;
}

t_unicode *float_string(float x)
{
	int y_length = snprintf(NULL, 0, "%f", x);
	t_unicode *y = (t_unicode *)malloc((y_length + 1) * sizeof(t_unicode));

	int result = snprintf(y, y_length + 1, "%f", x);
	if (result <= 0) return NULL;

	return y;
}

t_unicode *copy_string(const t_unicode *x)
{
	if (x == NULL) return NULL;

	t_unicode *y;

	size_t length = strlen(x);
	y = create_string((int)length);
	if (y == NULL) return NULL;

	memmove(y, x, length);
	return (y);
}

t_unicode *copy_append_string(const t_unicode *x, const t_unicode *y)
{
	if (x == NULL || y == NULL)
		return NULL;

	size_t x_length = strlen(x);
	size_t y_length = strlen(y);

	size_t length = (int)(x_length + y_length);
	t_unicode *z = create_string((int)length);
	if (z == NULL) return NULL;

	int index;
	for (index = 0; index < x_length; index++)
		z[index] = x[index];

	int offset = index;
	for (index = 0; index < y_length; index++)
		z[offset + index] = y[index];

	return z;
}

t_unicode *append_string_x(t_unicode *x, const t_unicode *y)
{
	if (x == NULL || y == NULL)
		return NULL;

	size_t x_length = strlen(x);
	size_t y_length = strlen(y);

	int length = (int)(x_length + y_length);
	t_unicode *z = create_string(length);
	if (z == NULL) return NULL;

	int index;
	for (index = 0; index < x_length; index++)
		z[index] = x[index];

	int offset = index;
	for (index = 0; index < y_length; index++)
		z[offset + index] = y[index];

	free(x);
	x = NULL;

	return z;
}

t_unicode *append_string_y(const t_unicode *x, t_unicode *y)
{
	if (x == NULL || y == NULL)
		return NULL;

	size_t x_length = strlen(x);
	size_t y_length = strlen(y);

	int length = (int)(x_length + y_length);
	t_unicode *z = create_string(length);

	int index;
	for (index = 0; index < x_length; index++)
		z[index] = x[index];

	int offset = index;
	for (index = 0; index < y_length; index++)
		z[offset + index] = y[index];

	free(y);
	y = NULL;

	return z;
}

t_unicode *append_string_xy(t_unicode *x, t_unicode *y)
{
	if (x == NULL || y == NULL)
		return NULL;

	size_t x_length = strlen(x);
	size_t y_length = strlen(y);

	int length = (int)(x_length + y_length);
	t_unicode *z = create_string(length);

	int index;
	for (index = 0; index < x_length; index++)
		z[index] = x[index];

	int offset = index;
	for (index = 0; index < y_length; index++)
		z[offset + index] = y[index];

	free(x);
	free(y);

	x = NULL;
	y = NULL;

	return z;
}

t_unicode *copy_append_nl(const t_unicode *v, const t_unicode *w)
{
	if (v == NULL) return NULL;

	t_unicode *x = NULL;

	if (w == NULL) x = copy_string(v);
	else x = copy_append_string(v, w);

	if (x == NULL) return NULL;

    x = append_string_x(x, "\n");
	if (x == NULL) return NULL;

	return x;
}

t_unicode *copy_append_nl_hex(const t_unicode *v, const t_unicode *w, int x)
{
	if (v == NULL) return NULL;

	t_unicode *y = NULL;
	
	if (w == NULL) y = copy_string(v);
	else y = copy_append_string(v, w);

	if (y == NULL) return NULL;

	t_unicode *z = hex_string(x);
	if (z == NULL) return NULL;

	y = append_string_xy(y, z);
	if (y == NULL) return NULL;

	y = append_string_x(y, "\n");
	if (y == NULL) return NULL;

	return y;
}

t_unicode *copy_append_nl_int(const t_unicode *v, const t_unicode *w, int x)
{
	if (v == NULL) return NULL;

	t_unicode *y = NULL;

	if (w == NULL) y = copy_string(v);
	else y = copy_append_string(v, w);

	if (y == NULL) return NULL;

	t_unicode *z = int_string(x);
	if (z == NULL) return NULL;

	y = append_string_xy(y, z);
	if (y == NULL) return NULL;

	y = append_string_x(y, "\n");
	if (y == NULL) return NULL;

	return y;
}

t_unicode *copy_append_nl_float(const t_unicode *v, const t_unicode *w, float x)
{
	if (v == NULL) return NULL;

	t_unicode *y = NULL;

	if (w == NULL) y = copy_string(v);
	else y = copy_append_string(v, w);

	if (y == NULL) return NULL;

	t_unicode *z = float_string(x);
	if (z == NULL) return NULL;

	y = append_string_xy(y, z);
	if (y == NULL) return NULL;

	y = append_string_x(y, "\n");
	if (y == NULL) return NULL;

	return y;
}

t_unicode *append_nl(t_unicode *w, const t_unicode *x)
{
	if (w == NULL) return NULL;
	t_unicode *z = copy_append_nl(w, x);

	free(w);
	return w;
}

t_unicode *append_nl_hex(t_unicode *w, const t_unicode *x, int y)
{
	if (w == NULL) return NULL;

	t_unicode *z = copy_append_nl_hex(w, x, y);
	if (z == NULL) return NULL;

	free(w);
	return z;
}

t_unicode *append_nl_int(t_unicode *w, const t_unicode *x, int y)
{
	if (w == NULL) return NULL;

	t_unicode *z = copy_append_nl_int(w, x, y);
	if (z == NULL) return NULL;

	free(w);
	return z;
}

t_unicode *append_nl_float(t_unicode *w, const t_unicode *x, float y)
{
	if (w == NULL) return NULL;

	t_unicode *z = copy_append_nl_float(w, x, y);
	if (z == NULL) return NULL;

	free(w);
	return z;
}

int append_log(const t_unicode *filename, const t_unicode *x)
{
	if (x == NULL) return -1;

	FILE *file = fopen(filename, "a");
	if (file == NULL) return -2;

	size_t size = strlen(x);
	size_t result = fwrite(x, sizeof(t_unicode), size, file);

	if (feof(file))
	{
		fclose(file);
		return -3;
	}

	if (ferror(file))
	{
		fclose(file);
		return -4;
	}

	if (NOT_EQUAL(result, size))
	{
		fclose(file);
		return -5;
	}

	fclose(file);
	return 0;
}

int append_log_nl(const t_unicode *filename, const t_unicode *x)
{
	t_unicode *z = copy_append_nl(x, NULL);
	if (z == NULL) return NULL;

	int result = append_log(filename, z);
	free(z);

	return result;
}

int append_log_nl_hex(const t_unicode *filename, const t_unicode *x, int y)
{
	t_unicode *z = copy_append_nl_hex(x, NULL, y);
	if (z == NULL) return NULL;

	int result = append_log(filename, z);
	free(z);

	return result;
}

int append_log_nl_int(const t_unicode *filename, const t_unicode *x, int y)
{
	t_unicode *z = copy_append_nl_int(x, NULL, y);
	if (z == NULL) return NULL;

	int result = append_log(filename, z);
	free(z);

	return result;
}

int append_log_nl_float(const t_unicode *filename, const t_unicode *x, float y)
{
	t_unicode *z = copy_append_nl_float(x, NULL, y);
	if (z == NULL) return NULL;

	int result = append_log(filename, z);
	free(z);

	return result;
}

int log_hex(const t_unicode *x, int y)
{
	t_unicode *z = copy_append_nl_hex(x, NULL, y);
	if (z == NULL) return NULL;

	int result = append_log(DIAGNOSTICS_LOG, z);
	free(z);

	return result;
}

int log_int(const t_unicode *x, int y)
{
	t_unicode *z = copy_append_nl_int(x, NULL, y);
	if (z == NULL) return NULL;

	int result = append_log(DIAGNOSTICS_LOG, z);
	free(z);

	return result;
}

int dump_memory(const t_byte *x, size_t size)
{
	if (x == NULL) return -1;

	FILE *file = fopen(MEMORY_DUMP, "wb");
	if (file == NULL) return -2;

	size_t result = fwrite(x, sizeof(t_byte), size, file);

	if (feof(file))
	{
		fclose(file);
		return -3;
	}

	if (ferror(file))
	{
		fclose(file);
		return -4;
	}

	if (NOT_EQUAL(result, size))
	{
		fclose(file);
		return -5;
	}

	fclose(file);
	return 0;
}