/*
 *                            COPYRIGHT
 *
 *  sch-rnd - modular/flexible schematics editor - eeschema file format support
 *  Copyright (C) 2022, 2023 Tibor 'Igor2' Palinkas
 *
 *  (Supported by NLnet NGI0 Entrust Fund in 2024)
 *
 *  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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *  Contact:
 *    Project page: http://repo.hu/projects/sch-rnd
 *    contact lead developer: http://www.repo.hu/projects/sch-rnd/contact.html
 *    mailing list: http://www.repo.hu/projects/sch-rnd/contact.html
 */


#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <genht/htsp.h>
#include <genht/hash.h>
#include <genht/ht_utils.h>
#include <libcschem/config.h>
#include <libcschem/cnc_pen.h>
#include <libcschem/cnc_grp.h>
#include <libcschem/cnc_line.h>
#include <libcschem/cnc_poly.h>
#include <libcschem/cnc_text.h>
#include <libcschem/cnc_conn.h>
#include <libcschem/cnc_any_obj.h>
#include <libcschem/operation.h>
#include <libcschem/project.h>
#include <libcschem/util_parse.h>
#include <librnd/core/compat_misc.h>
#include <librnd/core/rnd_printf.h>
#include <librnd/core/safe_fs_dir.h>
#include <librnd/core/compat_fs.h>
#include <librnd/core/paths.h>
#include <sch-rnd/buffer.h>

#include <plugins/lib_alien/read_helper.h>
#include <plugins/lib_alien/read_postproc.h>

#include "io_eeschema_conf.h"
extern conf_io_eeschema_t io_eeschema_conf;

#include "read.h"

/*#define dbg_printf printf*/

#ifndef dbg_printf
#define dbg_printf nope_printf
static int nope_printf(const char *fmt, ...) { return 0; }
#endif

#define error(ctx, args) \
	do { \
		rnd_message(RND_MSG_ERROR, "eeschema parse error at %s:%ld:\n", ctx->fn, ctx->lineno); \
		rnd_msg_error args; \
	} while(0)

typedef struct read_ctx_s {
	FILE *f;
	const char *fn;
	long ver;            /* file version */
	long lineno;
	int level;

	csch_sheet_t *sheet;
	csch_project_t *proj;

	csch_alien_read_ctx_t alien;
} read_ctx_t;

/* TODO: implement low level parsers here; each function should get a
   read_ctx_t* as first arg */

/*** file level parsing and entry points ***/

static void alien_setup(read_ctx_t *ctx)
{
	ctx->alien.sheet = ctx->sheet;
	ctx->alien.coord_factor = io_eeschema_conf.plugins.io_eeschema.coord_mult;
	ctx->alien.fmt_prefix = "io_eeschema";
}

static int io_eeschema_postproc(read_ctx_t *ctx)
{
/* enable this if your format has an automatic text rotation so that
   all text objects are readable from bottom and right (0 and 90 deg
   text rots only)
	if (io_eeschema_conf.plugins.io_eeschema.emulate_text_ang_180) {
		csch_cgrp_update(ctx->sheet, &ctx->sheet->direct, 1);
		csch_alien_postproc_text_autorot(&ctx->alien, &ctx->sheet->direct, 1, 0);
	}
*/

	/* use this for attribute conversion instead of hardwired C code: read
	   and store attributes as they are in the native format and let this
	   configurable postproc deal with things like symbols's "rename
	   refdes attribute to name attribute" - this is more flexible and
	   user configurable this way */
	return csch_alien_postproc_sheet(&ctx->alien);
}
/* load a single sheet from f/fn into preallocated empty sheet dst;
   fmt is an optional hint on the format (safe to ignore). Return 0
   on success */
int io_eeschema_load_sheet(FILE *f, const char *fn, const char *fmt, csch_sheet_t *dst)
{
	int res = -1;
	read_ctx_t ctx = {0};

	ctx.f = f;
	ctx.fn = fn;
	ctx.sheet = dst;
	ctx.lineno = 1;

#if 0
TODO:
	if (read_ver(&ctx) != 0)
		return -1;
#endif

	/* verify version - support some recent version(s) please */
	if (ctx.ver != 2) {
		error((&ctx), ("wrong version of eeschema schematics: only file version 2 is supported, yours is %d\n", ctx.ver));
		return -1;
	}

	alien_setup(&ctx);
	csch_alien_sheet_setup(&ctx.alien, 1);

	for(;;) {
		int r;
		
/*		r = read_next eeschema_object_from_ctx(&ctx, &ctx.sheet->direct, 0);*/
		r = -1;
		if (r < 0) /* error */
			return r;

		if (r == 1) { /* reached eof without error */
			res = 0;
			break;
		}
	}

	if (res == 0)
		res = io_eeschema_postproc(&ctx);

	if ((res == 0) && io_eeschema_conf.plugins.io_eeschema.auto_normalize)
		csch_alien_postproc_normalize(&ctx.alien);

	return res;
}

static csch_cgrp_t *load_sym_(read_ctx_t *ctx)
{
	csch_source_arg_t *src;
	csch_cgrp_t *resgrp = NULL;
	int rv = 0;

#if 0
TODO
	if (read_ver(ctx) != 0)
		return NULL;
#endif

	if ((ctx->ver != 1) && (ctx->ver != 2)) {
		error(ctx, ("wrong version of eeschema symbol: only file version 1 and 2 are supported, yours is %d\n", ctx->ver));
		return NULL;
	}

	resgrp = csch_cgrp_alloc(ctx->sheet, &ctx->sheet->direct, csch_oid_new(ctx->sheet, &ctx->sheet->direct));
	src = csch_attrib_src_c(ctx->fn, ctx->lineno, 0, NULL);
	csch_cobj_attrib_set(ctx->sheet, resgrp, CSCH_ATP_HARDWIRED, "role", "symbol", src);

	/* read the file */
	for(;;) {
		int cmd, r;

		cmd = fgetc(ctx->f);
		if (cmd == EOF)
			break;
		ungetc(cmd, ctx->f);

/*		r = read_next_object_from_your_file(ctx, resgrp, 1);*/
		r = -1;
		if (r != 0) {
			error(ctx, ("Error in eeschema symbol data\n"));
			rv = -1;
			break;
		}
	}

	if (rv == 0) {
		csch_cgrp_update(ctx->sheet, resgrp, 1);
		csch_sheet_bbox_update(ctx->sheet);
	}
	else {
		csch_cgrp_free(resgrp);
		resgrp = NULL;
	}

	return resgrp;
}

/* IO API function (load symbol from lib) */
csch_cgrp_t *io_eeschema_load_grp(FILE *f, const char *fn, const char *fmt, csch_sheet_t *sheet)
{
	read_ctx_t ctx = {0};
	csch_cgrp_t *grp;

	if (htip_get(&sheet->direct.id2obj, 1) != NULL) {
		rnd_message(RND_MSG_ERROR, "Error loading '%s': there's already a group1 in destination sheet\n", fn);
		return NULL;
	}

	ctx.f = f;
	ctx.fn = fn;
	ctx.sheet = sheet;
	ctx.lineno = 1;

	alien_setup(&ctx);

	grp = load_sym_(&ctx);
	if (io_eeschema_postproc(&ctx) != 0)
		rnd_message(RND_MSG_ERROR, "io_eeschema: failed to postprocess newly loaded symbol\n");

	return grp;
}

/* Read as little as possible from f/fn and return 0 if there's any chance
   of this file being the right format or return -1 if it's surely not a
   valid file in the supported format. fmt is a hint on the expected file
   format family. Look at type to figure if the format needs to be a sheet
   or a symbol */
int io_eeschema_test_parse(FILE *f, const char *fn, const char *fmt, csch_plug_io_type_t type)
{
	return -1;
}
