/*
 *                            COPYRIGHT
 *
 *  camv-rnd - electronics-related CAM viewer
 *  Copyright (C) 2019 Tibor 'Igor2' Palinkas
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 31 Milk Street, # 960789 Boston, MA 02196 USA.
 *
 *  Contact:
 *    Project page: http://repo.hu/projects/camv-rnd
 *    lead developer: http://repo.hu/projects/camv-rnd/contact.html
 *    mailing list: pcb-rnd (at) list.repo.hu (send "subscribe")
 */

#include <librnd/config.h>

#include <stdlib.h>
#include <genvector/vtp0.h>

#include <librnd/core/error.h>
#include <librnd/core/event.h>
#include <librnd/core/safe_fs.h>

#include "plug_io.h"


static vtp0_t camv_io;
static int camv_io_sorted = 0;

static int cmp_io_prio(const void *v1, const void *v2)
{
	const camv_io_t *io1 = v1, *io2 = v2;

	if (io1->prio > io2->prio)
		return 1;
	return -1;
}

static void sort_io(void)
{
	if (camv_io_sorted)
		return;
	qsort(camv_io.array, camv_io.used, sizeof(void *), cmp_io_prio);
	camv_io_sorted = 1;
}

void camv_io_reg(camv_io_t *io)
{
	vtp0_append(&camv_io, io);
	camv_io_sorted = 0;
}

void camv_io_unreg(camv_io_t *io)
{
	size_t n;
	for(n = 0; n < camv_io.used; n++) {
		if (camv_io.array[n] == io) {
			vtp0_remove(&camv_io, n , 1);
			return;
		}
	}
}

static void post_load(camv_design_t *camv)
{
	camv_data_bbox(camv);
	camv->hidlib.dwg.X1 = camv->bbox.x1;
	camv->hidlib.dwg.Y1 = camv->bbox.y1;
	camv->hidlib.dwg.X2 = camv->bbox.x2;
	camv->hidlib.dwg.Y2 = camv->bbox.y2;
}

int camv_io_load(camv_design_t *camv, const char *fn)
{
	const camv_io_t *io;
	int n;
	FILE *f;
	
	f = rnd_fopen(&camv->hidlib, fn, "rb");
	if (f == NULL) {
		rnd_message(RND_MSG_ERROR, "Can not open '%s' for read\n", fn);
		return -1;
	}

	sort_io();

	for(n = 0; n < camv_io.used; n++) {
		io = camv_io.array[n];
		if (io->load == NULL)
			continue;
		rewind(f);
		if ((io->test_load == NULL) || (io->test_load(camv, fn, f) != 0)) {
			int res;
			rewind(f);

			rnd_event(&camv->hidlib, RND_EVENT_LOAD_PRE, "s", fn);
			res = io->load(camv, fn, f);
			rnd_event(&camv->hidlib, RND_EVENT_LOAD_POST, "si", fn, res);

			if (res == 0) {
				fclose(f);
				post_load(camv);
				return 0;
			}
		}
	}

	return -1;
}

/*** save ***/

typedef struct {
	int prio;
	camv_io_t *io;
} prio_t;
#define GVT(x) vtpr_ ## x
#define GVT_ELEM_TYPE prio_t
#define GVT_SIZE_TYPE int
#define GVT_DOUBLING_THRS 32
#define GVT_START_SIZE 8
#define GVT_FUNC
#define GVT_SET_NEW_BYTES_TO 0
#include <genvector/genvector_impl.h>
#define GVT_REALLOC(vect, ptr, size)  realloc(ptr, size)
#define GVT_FREE(vect, ptr)           free(ptr)
#include <genvector/genvector_impl.c>

static int prio_cmp(const void *d1, const void *d2)
{
	const prio_t *p1 = d1, *p2 = d2;
	if (p1->prio <= p2->prio) return 1;
	return -1;
}

typedef enum {
	LIST_SAVE
#if 0
	LIST_EXPORT
#endif
} io_list_dir_t;

static void camv_plug_io_list(vtpr_t *res, const char *fn, const char *fmt, camv_io_type_t type, io_list_dir_t dir)
{
	int n, p;
	prio_t *pr;

	for(n = 0; n < vtp0_len(&camv_io); n++) {
		camv_io_t *io = camv_io.array[n];
		if (io == NULL) continue;
		switch(dir) {
			case LIST_SAVE:
				if (io->save_prio == NULL) continue;
				p = io->save_prio(fn, fmt, type);
				break;
#if 0
			case LIST_EXPORT:
				if (io->export_prio == NULL) continue;
				p = io->export_prio(fn, fmt, type);
				break;
#endif
		}

		if (p > 0) {
			pr = vtpr_alloc_append(res, 1);
			pr->prio = p;
			pr->io = io;
		}
	}
	qsort(res->array, vtpr_len(res), sizeof(prio_t), prio_cmp);
}

int camv_save_design(camv_design_t *camv, const char *fn_, const char *fmt)
{
	vtpr_t prios;
	int n, ret = -1, len;
	char *fn, *ext = NULL;

	if ((fmt == NULL) || (*fmt == '\0'))
		return -1;

	len = strlen(fn_);
	fn = malloc(len+1);
	memcpy(fn, fn_, len+1);
	if (fn[len-1] == '*')
		ext = fn + len - 1;

	vtpr_init(&prios);
	camv_plug_io_list(&prios, fn, fmt, CAMV_IOTYP_DESIGN, LIST_SAVE);
	for(n = 0; n < vtpr_len(&prios); n++) {
		prio_t *pr = &prios.array[n];
		if (ext != NULL) {
			if (pr->io->ext_save_design != NULL)
				strcpy(ext, pr->io->ext_save_design);
			else
				*ext = '\0';
			break;
		}

		rnd_event(&camv->hidlib, RND_EVENT_SAVE_PRE, "s", fmt);
		ret = pr->io->save_design(camv, fn);
		rnd_event(&camv->hidlib, RND_EVENT_SAVE_POST, "si", fmt, ret);

		if (ret == 0)
			break;
	}

	free(fn);
	vtpr_uninit(&prios);
	return ret;
}
