/*
 *                            COPYRIGHT
 *
 *  pcb-rnd, interactive printed circuit board design
 *  Copyright (C) 2016, 2017, 2018 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/pcb-rnd
 *    lead developer: http://repo.hu/projects/pcb-rnd/contact.html
 *    mailing list: pcb-rnd (at) list.repo.hu (send "subscribe")
 */

#include "config.h"

#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <genvector/gds_char.h>
#include <genht/htsp.h>
#include <genht/hash.h>
#include <librnd/core/error.h>
#include "wget_common.h"
#include "gedasymbols.h"
#include <librnd/core/plugins.h>
#include "plug_footprint.h"
#include <librnd/core/compat_misc.h>
#include <librnd/core/safe_fs.h>
#include <librnd/core/paths.h>
#include "fp_wget_conf.h"
#include "globalconst.h"

#define REQUIRE_PATH_PREFIX "wget@gedasymbols"

#define CGI_URL "http://www.gedasymbols.org/scripts/global_list.cgi"
#define FP_URL "http://www.gedasymbols.org/"
#define FP_DL "?dl"
static const char *url_idx_md5 = CGI_URL "?md5";
static const char *url_idx_list = CGI_URL;


int fp_gedasymbols_load_dir(pcb_plug_fp_t *ctx, const char *path, int force)
{
	FILE *f;
	int fctx;
	char *md5_last, *md5_new;
	char line[1024];
	fp_get_mode mode;
	gds_t vpath;
	int vpath_base_len;
	fp_get_mode wmode = FP_WGET_OFFLINE;
	pcb_fplibrary_t *l;
	char last_sum_fn[RND_PATH_MAX], *cache_dir;

	if (strncmp(path, REQUIRE_PATH_PREFIX, strlen(REQUIRE_PATH_PREFIX)) != 0)
		return -1;

	cache_dir = rnd_build_fn(NULL, conf_fp_wget.plugins.fp_wget.cache_dir);

	rnd_snprintf(last_sum_fn, sizeof(last_sum_fn), "%s" RND_DIR_SEPARATOR_S "gedasymbols.last", cache_dir);

	gds_init(&vpath);
	gds_append_str(&vpath, REQUIRE_PATH_PREFIX);

	l = pcb_fp_mkdir_p(vpath.array);
	if (l != NULL)
		l->data.dir.backend = ctx;

	if (force || (conf_fp_wget.plugins.fp_wget.auto_update_gedasymbols))
		wmode &= ~FP_WGET_OFFLINE;

	if (fp_wget_open(url_idx_md5, cache_dir, &f, &fctx, wmode) != 0) {
		if (wmode & FP_WGET_OFFLINE) /* accept that we don't have the index in offline mode */
			goto quit;
		goto err;
	}

	md5_new = load_md5_sum(f);
	fp_wget_close(&f, &fctx);

	if (md5_new == NULL)
		goto err;

	f = rnd_fopen(NULL, last_sum_fn, "r");
	md5_last = load_md5_sum(f);
	if (f != NULL)
		fclose(f);

/*	printf("old='%s' new='%s'\n", md5_last, md5_new);*/

	if (!md5_cmp_free(last_sum_fn, md5_last, md5_new)) {
/*		printf("no chg.\n");*/
		mode = FP_WGET_OFFLINE; /* use the cache */
	}
	else
		mode = 0;

	if (fp_wget_open(url_idx_list, cache_dir, &f, &fctx, mode) != 0) {
		rnd_message(RND_MSG_ERROR, "gedasymbols: failed to download the new list\n");
		rnd_remove(NULL, last_sum_fn); /* make sure it is downloaded next time */
		goto err;
	}

	gds_append(&vpath, '/');
	vpath_base_len = vpath.used;

	while(fgets(line, sizeof(line), f) != NULL) {
		char *end, *fn;

		if (*line == '#')
			continue;
		end = strchr(line, '|');
		if (end == NULL)
			continue;
		*end = '\0';

		/* split path and fn; path stays in vpath.array, fn is a ptr to the file name */
		gds_truncate(&vpath, vpath_base_len);
		gds_append_str(&vpath, line);
		end = vpath.array + vpath.used - 1;
		while((end > vpath.array) && (*end != '/')) { end--; vpath.used--; }
		*end = '\0';
		vpath.used--;
		end++;
		fn = end;

		/* add to the database */
		l = pcb_fp_mkdir_p(vpath.array);
		l = pcb_fp_append_entry(l, fn, PCB_FP_FILE, NULL, 0, NULL);
		fn[-1] = '/';
		l->data.fp.loc_info = rnd_strdup(vpath.array);
	}
	fp_wget_close(&f, &fctx);

	quit:;
	free(cache_dir);
	gds_uninit(&vpath);
	return 0;

	err:;
	free(cache_dir);
	gds_uninit(&vpath);
	return -1;
}

#define FIELD_WGET_CTX 0

static int search_gedasyms(char *out, int out_len, FILE *f, const char *fn)
{
	char *line, line_[8192];

	*out = '\0';

	if (f == NULL)
		return -1;

	while((line = fgets(line_, sizeof(line_), f)) != NULL) {
		char *sep;
		sep = strchr(line, '|');
		if (sep == NULL)
			continue;
		*sep = '\0';
		if ((strstr(line, fn) != NULL) && (strlen(line) < out_len)) {
			strcpy(out, line);
			return 0;
		}
	}
	return -1;
}


FILE *fp_gedasymbols_fopen(pcb_plug_fp_t *ctx, const char *path, const char *name, pcb_fp_fopen_ctx_t *fctx, pcb_data_t *dst)
{
	gds_t s;
	char tmp[1024], *cache_dir;
	FILE *f = NULL;
	int from_path = (path != NULL) && (strcmp(path, REQUIRE_PATH_PREFIX) == 0);

	if (!from_path) {
		if (strncmp(name, REQUIRE_PATH_PREFIX, strlen(REQUIRE_PATH_PREFIX)) == 0)
			name+=strlen(REQUIRE_PATH_PREFIX);
		else
			return NULL;
	}

	cache_dir = rnd_build_fn(NULL, conf_fp_wget.plugins.fp_wget.cache_dir);

	if (*name == '/')
		name++;

	if (from_path) {
		if (fp_wget_search(tmp, sizeof(tmp), name, !conf_fp_wget.plugins.fp_wget.auto_update_gedasymbols, url_idx_list, cache_dir, search_gedasyms) != 0)
			goto bad;
		name = tmp;
	}

	gds_init(&s);
	gds_append_str(&s, FP_URL);
	gds_append_str(&s, name);
	gds_append_str(&s, FP_DL);

	fp_wget_open(s.array, cache_dir, &f, &(fctx->field[FIELD_WGET_CTX].i), FP_WGET_UPDATE);

	gds_uninit(&s);

	bad:;
	fctx->backend = ctx;

	free(cache_dir);
	return f;
}

void fp_gedasymbols_fclose(pcb_plug_fp_t *ctx, FILE * f, pcb_fp_fopen_ctx_t *fctx)
{
	fp_wget_close(&f, &(fctx->field[FIELD_WGET_CTX].i));
}


static pcb_plug_fp_t fp_gedasymbols;

void fp_gedasymbols_uninit(void)
{
	RND_HOOK_UNREGISTER(pcb_plug_fp_t, pcb_plug_fp_chain, &fp_gedasymbols);
}

void fp_gedasymbols_init(void)
{
	fp_gedasymbols.plugin_data = NULL;
	fp_gedasymbols.load_dir = fp_gedasymbols_load_dir;
	fp_gedasymbols.fp_fopen = fp_gedasymbols_fopen;
	fp_gedasymbols.fp_fclose = fp_gedasymbols_fclose;

	RND_HOOK_REGISTER(pcb_plug_fp_t, pcb_plug_fp_chain, &fp_gedasymbols);
}
