#include "skip.h"

#include "error.h"
#include "file.h"
#include "str.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct sw_skip *sw_skip_self;

struct sw_skip *sw_skip_create() {
	struct sw_skip *skip;

	skip = calloc(1, sizeof(struct sw_skip));

	return skip;
}

struct sw_skip *sw_skip_load(char *filename) {
	FILE *fil;
	struct sw_skip *skip;
	char magic[5] = {0};
	u8 version, compression;
	u16 i;
	char str[257];
	u8 str_len;
	u32 off;

	skip = sw_skip_create();
	fil = fopen(filename, "rb");
	fseek(fil, -4, SEEK_END);
	fread(&off, 4, 1, fil);
	fseek(fil, -(i32)off, SEEK_END);

	fread(magic, 1, 4, fil);
	if(strcmp("SWZP", magic) != 0) {
		sw_log("not a .skip file");
		return 0;
	}

	fread(&version, 1, 1, fil);
	if(version > 0) {
		sw_error("version %i of .skip not supported", version);
	}

	fread(&compression, 1, 1, fil);
	if(compression > 0) {
		sw_error("unsupported .skip compression method");
	}

	fread(&skip->data_size, 1, 4, fil);
	fseek(fil, 4, SEEK_CUR); /* ignore compressed size */
	fread(&skip->file_count, 1, 2, fil);

	skip->files = malloc(sizeof(skip->files[0]) * skip->file_count);

	for(i = 0; i < skip->file_count; ++i) {
		memset(str, 0, 257);
		fread(&skip->files[i].offset, 1, 4, fil);
		fread(&skip->files[i].size, 1, 4, fil);
		fread(&str_len, 1, 1, fil);
		fread(str, str_len, 1, fil);
		str[str_len] = 0;

		skip->files[i].name = malloc(str_len + 1);
		strcpy(skip->files[i].name, str);
	}

	skip->data = malloc(skip->data_size);
	fread(skip->data, 1, skip->data_size, fil);

	fclose(fil);
	return skip;
}

static void sw_skip_save_to(struct sw_skip *skip, FILE *fil);

void sw_skip_destroy(struct sw_skip *skip) {
	i32 i;

	for(i = 0; i < skip->file_count; ++i) {
		free(skip->files[i].name);
	}
	free(skip->files);
	free(skip->data);
	free(skip);
}

void sw_skip_add(struct sw_skip *skip, u8 *data, u32 data_size, char *name) {
	u32 off;

	off = skip->data_size;

	skip->data_size += data_size;
	skip->data = realloc(skip->data, skip->data_size);
	memcpy(skip->data + off, data, data_size);

	skip->file_count += 1;
	skip->files =
	    realloc(skip->files, sizeof(skip->files[0]) * skip->file_count);
	skip->files[skip->file_count - 1].name = sw_str_dup(name);
	skip->files[skip->file_count - 1].offset = off;
	skip->files[skip->file_count - 1].size = data_size;
}

void sw_skip_add_file(struct sw_skip *skip, char *name, char *filename) {
	FILE *fil;
	u32 len;
	u8 *data;

	fil = fopen(filename, "rb");
	fseek(fil, 0, SEEK_END);
	len = ftell(fil);
	fseek(fil, 0, SEEK_SET);

	data = malloc(len);
	fread(data, 1, len, fil);
	fclose(fil);

	sw_skip_add(skip, data, len, name);

	free(data);
}

void sw_skip_attach(struct sw_skip *skip, char *filename) {
	FILE *fil;

	fil = fopen(filename, "rb+");
	fseek(fil, 0, SEEK_END);
	sw_skip_save_to(skip, fil);
}

void sw_skip_dettach(char *filename) {
	FILE *fil;
	char magic[5] = {0};
	u32 off;
	u32 len;
	u8 *data;

	fil = fopen(filename, "rb");
	fseek(fil, -4, SEEK_END);
	fread(&off, 4, 1, fil);
	fseek(fil, -(i32)off, SEEK_END);

	fread(magic, 1, 4, fil);
	sw_log("magic: [%s]", magic);
	sw_log("off: %i", off);
	if(strcmp("SWZP", magic) == 0) {
		sw_log("detaching .skip from %s", filename);
		len = ftell(fil) - 4;
		fseek(fil, 0, SEEK_SET);
		data = malloc(len);
		fread(data, 1, len, fil);
		fclose(fil);
		fil = fopen(filename, "wb");
		fwrite(data, 1, len, fil);
	}
	fclose(fil);
}

u8 *sw_skip_get(struct sw_skip *skip, char *name, u32 *out_size) {
	u32 i;

	for(i = 0; i < skip->file_count; ++i) {
		if(strcmp(name, skip->files[i].name) == 0) {
			if(out_size) {
				*out_size = skip->files[i].size;
			}
			return skip->data + skip->files[i].offset;
		}
	}
	return 0;
}

char *sw_skip_get_string(struct sw_skip *skip, char *name) {
	u8 *data;
	u32 size;
	char *str;

	data = sw_skip_get(skip, name, &size);
	if(data == 0) {
		return 0;
	}

	str = malloc(size + 1);
	memcpy(str, data, size);
	str[size] = 0;

	return str;
}

void sw_skip_save(struct sw_skip *skip, char *filename) {
	sw_skip_save_to(skip, fopen(filename, "wb"));
}

static void sw_skip_save_to(struct sw_skip *skip, FILE *fil) {
	u16 i;
	u32 start;

	start = ftell(fil);
	sw_fwrite_str_nozero(fil, "SWZP");
	sw_fwrite_u8(fil, 0);                 /* version */
	sw_fwrite_u8(fil, 0);                 /* compression */
	sw_fwrite_u32(fil, skip->data_size);  /* uncompressed size */
	sw_fwrite_u32(fil, skip->data_size);  /* compressed size */
	sw_fwrite_u16(fil, skip->file_count); /* file count */

	for(i = 0; i < skip->file_count; ++i) {
		sw_fwrite_u32(fil, skip->files[i].offset);       /* offset */
		sw_fwrite_u32(fil, skip->files[i].size);         /* file size */
		sw_fwrite_str_prefix8(fil, skip->files[i].name); /* file name */
	}

	fwrite(skip->data, 1, skip->data_size, fil);

	sw_fwrite_u32(fil, ftell(fil) + 4 - start); /* file length */

	fclose(fil);
}
