/*
 *                            COPYRIGHT
 *
 *  sch-rnd - modular/flexible schematics editor - sch-rnd (executable)
 *  Copyright (C) 2022, 2023, 2024 Tibor 'Igor2' Palinkas
 *
 *  (Supported by NLnet NGI0 PET Fund in 2022, Entrust in 2023)
 *
 *  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/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
 */

/* Generic actions usually related to the currently open file(s) */

#include <libcschem/config.h>

#include <liblihata/tree.h>

#include <libcschem/concrete.h>
#include <libcschem/plug_library.h>
#include <libcschem/event.h>
#include <libcschem/cnc_grp.h>
#include <libcschem/operation.h>
#include <libcschem/project.h>
#include <libcschem/plug_io.h>
#include <libcschem/util_loclib.h>
#include <librnd/core/actions.h>
#include <librnd/hid/hid.h>
#include <librnd/hid/hid_export.h>
#include <librnd/core/compat_misc.h>
#include <librnd/core/compat_fs.h>
#include <librnd/core/compat_lrealpath.h>
#include <librnd/core/safe_fs.h>

#include "sheet.h"
#include "conf_core.h"
#include "build_run.h"
#include "multi.h"
#include "project.h"

/* Return 1 if any of the currently loaded sheets have changes on them */
static int any_sheet_changed(void)
{
	csch_sheet_t *sheet;
	for(sheet = gdl_first(&rnd_designs); sheet != NULL; sheet = sheet->hidlib.link.next)
		if (sheet->changed)
			return 1;

	return 0;
}

static const char csch_acts_Quit[] = "Quit()";
static const char csch_acth_Quit[] = "Quits sch-rnd after confirming.";
static fgw_error_t csch_act_Quit(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	const char *force = NULL;
	RND_ACT_MAY_CONVARG(1, FGW_STR, Quit, force = argv[1].val.str);

	if ((force != NULL) && (rnd_strcasecmp(force, "force") == 0))
		exit(0);

	if (!any_sheet_changed() || (rnd_hid_message_box(RND_ACT_DESIGN, "warning", "Close: lose data", "Exiting sch-rnd will lose unsaved sheet modifications.", "cancel", 0, "ok", 1, NULL) == 1))
		sch_rnd_quit_app();

	RND_ACT_IRES(-1);
	return 0;
}

static const char csch_acts_SymlibRehash[] = "SymlibRehash()";
static const char csch_acth_SymlibRehash[] = "Rebuild the in-memory tree of symbol libraries";
static fgw_error_t csch_act_SymlibRehash(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	csch_sheet_t *sheet = CSCH_ACT_SHEET;
	csch_lib_master_t *master = csch_lib_get_master("symbol", 1);

	csch_lib_clear_sheet_lib(sheet, master->uid);
	csch_lib_add_all(sheet, master, &conf_core.rc.library_search_paths, 1);
	rnd_event(&sheet->hidlib, CSCH_EVENT_LIBRARY_CHANGED, NULL);
	RND_ACT_IRES(0);
	return 0;
}

static const char csch_acts_SymlibReloadAllLocal[] = "SymlibReloadAllLocal()";
static const char csch_acth_SymlibReloadAllLocal[] = "Replace all local symbols from a newer version of the same symbol from the symbol library";
static fgw_error_t csch_act_SymlibReloadAllLocal(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	csch_sheet_t *sheet = CSCH_ACT_SHEET;
	csch_lib_master_t *master = csch_lib_get_master("symbol", 0);
	long cnt;

	cnt = csch_loclib_reload_all(sheet, sheet->local_libs.array[master->uid]);
	rnd_message(RND_MSG_INFO, "Reloaded %ld local lib symbol(s) from external libs\n", cnt);

	rnd_event(&sheet->hidlib, CSCH_EVENT_LIBRARY_CHANGED, NULL);
	RND_ACT_IRES(0);
	return 0;
}

static const char csch_acts_HlibraryRehash[] = "HlibraryRehash()";
static const char csch_acth_HlibraryRehash[] = "Rebuild the in-memory tree of hierarchic sheet libraries";
static fgw_error_t csch_act_HlibraryRehash(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	csch_sheet_t *sheet = CSCH_ACT_SHEET;
	csch_lib_master_t *master = csch_lib_get_master("hlibrary", 1);

	csch_lib_clear_sheet_lib(sheet, master->uid);
	csch_lib_add_all(sheet, master, &conf_core.rc.library_search_paths, 1);
	rnd_event(&sheet->hidlib, CSCH_EVENT_LIBRARY_CHANGED, NULL);
	RND_ACT_IRES(0);
	return 0;
}

static const char csch_acts_Revert[] = "Revert([sheet|project])";
static const char csch_acth_Revert[] = "Revert to on-disk version of file(s). Default target is sheet.";
fgw_error_t csch_act_Revert(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	const char *op = "sheet";
	csch_sheet_t *sheet = CSCH_ACT_SHEET;

	RND_ACT_MAY_CONVARG(1, FGW_STR, Revert, op = argv[1].val.str);

	if (rnd_strcasecmp(op, "sheet") == 0) {
		csch_revert_sheet(sheet, sch_rnd_sheet_new4revert);
	}
	else if (rnd_strcasecmp(op, "project") == 0) {
		rnd_message(RND_MSG_ERROR, "Revert(project,...) not yet implemented\n");
	}
	else
		RND_ACT_FAIL(Revert);

	RND_ACT_IRES(0);
	return 0;
}

static void prj_flush(csch_sheet_t *sheet, const char *listpath)
{
	const char *prjpath = NULL;

	rnd_conf_makedirty(RND_CFR_PROJECT);
	rnd_conf_update(listpath, -1);

	if (sheet->hidlib.project != NULL)
		prjpath = sheet->hidlib.project->fullpath;

	rnd_conf_save_file(&sheet->hidlib, prjpath, sheet->hidlib.fullpath, RND_CFR_PROJECT, NULL);
}

/* Does NOT increment num_root_sheets, caller needs to do that */
static void prj_append_sheet(csch_project_t *prj, csch_sheet_t *sheet, const char *listpath)
{
	lht_node_t *nd;

	if (prj->hdr.fullpath == NULL)
		sch_rnd_project_create_file_for_sheet_gui(sheet);

	if (prj->hdr.fullpath == NULL) /* cancel - no project file created */
		sheet->stype = CSCH_SHTY_UNLISTED;


	nd = rnd_conf_lht_get_at(RND_CFR_PROJECT, listpath, 1);
	if (nd != NULL) {
		char *val = NULL;

		if (prj->hdr.fullpath == NULL)
			val = rnd_strdup(rnd_basename(sheet->hidlib.fullpath));
		else if ((sheet->hidlib.fullpath != NULL) && (*sheet->hidlib.fullpath != '<'))
			val = rnd_relative_path_files(sheet->hidlib.fullpath, prj->hdr.fullpath);
		else
			rnd_message(RND_MSG_ERROR, "Failed to append sheet to project file's %s;\ntry to save the sheet first so it gets a file name!\n", listpath);

		if (val != NULL) {
			lht_node_t *n = lht_dom_node_alloc(LHT_TEXT, NULL);
			lht_dom_list_append(nd, n);
			n->data.text.value = val;
	
			prj_flush(sheet, listpath);
		}
	}
	else
		rnd_message(RND_MSG_ERROR, "Failed to append sheet to project file's %s\n", listpath);
}

static const char csch_acts_New[] = "New([scope, [root|aux|unlisted]])";
static const char csch_acth_New[] = "Create a new sheet. Scope is a project path or @ for current project.";
fgw_error_t csch_act_New(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	const char *scope = "@", *type = "root";
	char *fn, *path, *dn;
	csch_sheet_t *ns, *curr = (csch_sheet_t *)RND_ACT_DESIGN;
	csch_project_t *prj;

	RND_ACT_MAY_CONVARG(1, FGW_STR, New, scope = argv[1].val.str);
	RND_ACT_MAY_CONVARG(2, FGW_STR, New, type = argv[2].val.str);

	if ((strcmp(type, "root") != 0) && (strcmp(type, "aux") != 0) && (strcmp(type, "unlisted") != 0)) {
		rnd_message(RND_MSG_ERROR, "New(): invalid sheet type '%s'\n", type);
		return FGW_ERR_ARG_CONV;
	}

	if ((scope[0] != '@') || (scope[1] != '\0')) {
		rnd_message(RND_MSG_ERROR, "New(): only current project scope (@) is supported at the moment\n");
		return FGW_ERR_ARG_CONV;
	}

	dn = rnd_dirname(curr->hidlib.fullpath);
	if (dn == NULL) {
		if ((curr->hidlib.project != NULL) && (curr->hidlib.project->fullpath != NULL))
			dn = rnd_dirname(curr->hidlib.project->fullpath);
		if (dn == NULL) {
			rnd_message(RND_MSG_ERROR, "New(): project does not exist\n(Try saving current sheet first)\n");
			return FGW_ERR_ARG_CONV;
		}
	}


	RND_ACT_IRES(0);
	fn = rnd_hid_prompt_for(RND_ACT_DESIGN, "Please enter a file name of the new sheet.\nFile path is assumed to be the same as the project's path.\n(as a convention, it is recommended that the file name ends in .rs)", "new.rs", "File name of the new sheet");
	if (fn == NULL) /* cancel */
		return 0;

	if (strpbrk(fn, "/\\") != NULL) {
		rnd_message(RND_MSG_ERROR, "New(): sheet name can not contain path separator (use basename, not path)\n");
		return FGW_ERR_ARG_CONV;
	}

	path = rnd_concat(dn, "/", fn, NULL);
	if (rnd_file_readable(RND_ACT_DESIGN, path)) {
		int go_for_it;
		char *msg;
		
		msg = rnd_concat("File ", path, " already exists.\nSaving the new sheet would overwrite it.\nAre you sure you want the create a new sheet on this name?", NULL);
		go_for_it = rnd_hid_message_box(RND_ACT_DESIGN, "question", "New sheet: file already exists", msg, "yes", 1, "no/cancel", 0, NULL);
		free(msg);

		if (!go_for_it) {
			free(path);
			return 0;
		}
	}

	rnd_multi_switch_to(NULL); /* empty conf so we can overwrite */

	/* NOTE: this may be a contradiction to use curr->hidlib.project instead of
	         NULL: create a new unlisted sheet from menu, name it /tmp/foo.rs
	         and it will be put in the current project in memory, but not in
	         the project file (since it's unlisted). So this sheet is now using
	         the wrong project.lht that it wouldn't use when loaded */
	ns = sch_rnd_multi_new_empty_in_prj((csch_project_t *)curr->hidlib.project, path);
	free(path);

	ns->changed = 1; /* sheet is unsaved when created */

	prj = (csch_project_t *)ns->hidlib.project;

	switch(*type) {
		case 'r':
			ns->stype = CSCH_SHTY_ROOT;
			prj->num_root_sheets++;
			rnd_multi_switch_to_(&ns->hidlib); /* activate new, before project file write */
			prj_append_sheet(prj, ns, "prj/root_sheets");
			if (prj->hdr.fullpath == NULL) {
				/* undo the counting if the user didn't create the project file */
				prj->num_root_sheets--;
				ns->stype = CSCH_SHTY_UNLISTED;
			}
			rnd_event(&ns->hidlib, RND_EVENT_DESIGN_FN_CHANGED, NULL); /* get the final type displayed */
			break;
		case 'a':
			ns->stype = CSCH_SHTY_AUX;
			prj->num_aux_sheets++;
			rnd_multi_switch_to_(&ns->hidlib); /* activate new, before project file write */
			prj_append_sheet(prj, ns, "prj/aux_sheets");
			if (prj->hdr.fullpath == NULL) {
				/* undo the counting if the user didn't create the project file */
				prj->num_aux_sheets--;
				ns->stype = CSCH_SHTY_UNLISTED;
			}
			rnd_event(&ns->hidlib, RND_EVENT_DESIGN_FN_CHANGED, NULL); /* get the final type displayed */
			break;
		case 'u':
			ns->stype = CSCH_SHTY_UNLISTED;
			rnd_multi_switch_to_(&ns->hidlib); /* activate new */
			break;
	}

	return 0;
}

static const char csch_acts_ImportAttbl[] = "ImportAttbl([path, [fmt]])";
static const char csch_acth_ImportAttbl[] = "Import an attribute table file and make modifications to the concrete model or generate back annotation for the abstract model";
fgw_error_t csch_act_ImportAttbl(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	const char *fn = NULL, *fmt = NULL;
	csch_sheet_t *sheet = CSCH_ACT_SHEET;

	RND_ACT_MAY_CONVARG(1, FGW_STR, ImportAttbl, fn = argv[1].val.str);
	RND_ACT_MAY_CONVARG(2, FGW_STR, ImportAttbl, fmt = argv[2].val.str);

	if (fn == NULL) {
		static char *default_file = NULL;
		static const char *flt_table[] = {"*.csv", NULL};
		static const char *flt_any[] = {"*", "*.*", NULL};
		const rnd_hid_fsd_filter_t flt[] = {
			{"tables",                             NULL, flt_table},
			{"all",                                NULL, flt_any},
			{NULL, NULL,NULL}
		};

		fn = rnd_hid_fileselect(rnd_gui,
			"Import attribute table...", "Picks an attribute table to import and appy\n",
			default_file, ".csv", flt, "attbl", RND_HID_FSD_READ, NULL);

		free(default_file);
		default_file = NULL;

		if (fn == NULL) {
			RND_ACT_IRES(1);
			return 0; /* cancel */
		}
	}

	RND_ACT_IRES(csch_import_attbl(&sheet->hidlib, fn, fmt));

	return 0;
}

static const char csch_acts_ProjectNew[] = "ProjectNew([path])";
static const char csch_acth_ProjectNew[] = "Create a new project";
fgw_error_t csch_act_ProjectNew(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	const char *path = NULL;
	char *freeme = NULL;
	int ret = 1;
	csch_project_t *prj;

	RND_ACT_MAY_CONVARG(1, FGW_STR, ProjectNew, path = argv[1].val.str);

	if (path == NULL) {
		path = freeme = rnd_hid_fileselect(rnd_gui, "New project", "select the path/project.lht of the new project", "project.lht", NULL, NULL, "ProjectNew", 0, NULL);
		if (path == NULL)
			goto quit;
	}

	if (rnd_file_readable(RND_ACT_DESIGN, path)) {
		rnd_message(RND_MSG_ERROR, "Can't create project: project file already exists\n");
		goto quit;
	}

	ret = sch_rnd_project_create_file(RND_ACT_DESIGN, path, &prj);
	if ((ret == 0) && (prj != NULL)) {
		csch_sheet_t *ns;
		char *nam, *bn;

		rnd_multi_switch_to(NULL); /* empty conf so we can overwrite */
		ns = sch_rnd_multi_new_empty_in_prj(prj, NULL);
		ns->changed = 1; /* sheet is unsaved when created */

		nam = rnd_strdup(prj->hdr.fullpath);
		bn = nam + strlen(nam) - 11; /* nam ends in project.lht */
		strcpy(bn, "<none>");
		ns->hidlib.loadname = nam; /* sinde there's no full_path in hidlib, save_as will trigger on save */
		ns->stype = CSCH_SHTY_UNLISTED;
		rnd_multi_switch_to_(&ns->hidlib);
	}

	quit:;
	free(freeme);
	RND_ACT_IRES(ret);
	return 0;
}

static const char csch_acts_ProjectLoadPartial[] = "ProjectLoadPartial([@])";
static const char csch_acth_ProjectLoadPartial[] = "Load sheet's missing files (or in other words, load the project file of the sheet)";
fgw_error_t csch_act_ProjectLoadPartial(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	const char *scope = "@";
	csch_sheet_t *sheet = (csch_sheet_t *)RND_ACT_DESIGN;
	int ret;
	csch_project_t *prj;

	RND_ACT_CONVARG(1, FGW_STR, ProjectLoadPartial, scope = argv[1].val.str);

	RND_ACT_IRES(1);

	if ((scope[0] != '@') || (scope[1] != '\0')) {
		rnd_message(RND_MSG_ERROR, "ProjectLoadPartial(): only current project scope (@) is supported at the moment\n");
		return FGW_ERR_ARG_CONV;
	}

	prj = (csch_project_t *)sheet->hidlib.project;
	assert(prj != NULL);

	ret = rnd_actionva(&sheet->hidlib, "LoadFrom", "sheet", prj->hdr.fullpath, NULL);

	RND_ACT_IRES(ret);
	return 0;
}

static void prj_remove_sheet(csch_project_t *prj, const char *listpath, long pidx)
{
	lht_node_t *nd;

	if ((listpath == NULL) || (pidx < 0))
		return;

	nd = rnd_conf_lht_get_at(RND_CFR_PROJECT, listpath, 1);
	if (nd != NULL) {
		if (lht_tree_list_del_nth(nd, pidx) == LHTE_SUCCESS) {
			if (listpath[4] == 'r') /* removed from prj/root_sheets */
				prj->num_root_sheets--;
			if (listpath[4] == 'a') /* removed from prj/aux_sheets */
				prj->num_aux_sheets--;
		}
	}
}

static long prj_find_sheet_in_(const char *prjdir, const rnd_conflist_t *lst, const char *fpath)
{
	int idx;
	rnd_conf_listitem_t *item_li;
	const char *item_str;

	rnd_conf_loop_list_str(lst, item_li, item_str, idx) {
		char *sfp;
		const char *sfn = item_str;
		gds_t tmp = {0};

		if (!rnd_is_path_abs(sfn)) { /* relative sheet file name is always relative to the project */
			gds_append_str(&tmp, prjdir);
			gds_append(&tmp, '/');
			gds_append_str(&tmp, sfn);
			sfn = tmp.array;
		}

		sfp = rnd_lrealpath(sfn);
		gds_uninit(&tmp);

		if (sfp != NULL) {
			int res = strcmp(sfp, fpath);
			free(sfp);
			if (res == 0)
				return idx;
		}
	}
	return -1;
}

/* convert each item of lst into a realpath and check if it matches fpath;
   if it does, return its index on the list */
static long prj_find_sheet_in(csch_project_t *prj, const rnd_conflist_t *lst, const char *fpath)
{
	long res;
	char *bn;

	bn = rnd_dirname(prj->hdr.fullpath);
	res = prj_find_sheet_in_(bn, lst, fpath);
	free(bn);

	return res;
}



static const char csch_acts_ProjectSheetType[] = "ProjectSheetType(@, sheet_fullpath, root|aux|unlisted|unload)";
static const char csch_acth_ProjectSheetType[] = "Change the type of a sheet (addressed by its full path) in a project, making the necessary modifications in the project file";
fgw_error_t csch_act_ProjectSheetType(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	const char *scope, *fpath, *type, *listpath = NULL;
	int also_unload = 0;
	long n, pidx = -1;
	csch_sheet_t *sheet = NULL, *old = NULL, *curr = (csch_sheet_t *)RND_ACT_DESIGN;
	csch_project_t *prj;

	RND_ACT_CONVARG(1, FGW_STR, ProjectSheetType, scope = argv[1].val.str);
	RND_ACT_CONVARG(2, FGW_STR, ProjectSheetType, fpath = argv[2].val.str);
	RND_ACT_CONVARG(3, FGW_STR, ProjectSheetType, type = argv[3].val.str);

	RND_ACT_IRES(1);

	if ((scope[0] != '@') || (scope[1] != '\0')) {
		rnd_message(RND_MSG_ERROR, "ProjectSheetType(): only current project scope (@) is supported at the moment\n");
		return FGW_ERR_ARG_CONV;
	}

	prj = (csch_project_t *)curr->hidlib.project;
	assert(prj != NULL);

	/* find the sheet */
	for(n = 0; n < prj->hdr.designs.used; n++) {
		csch_sheet_t *sh = prj->hdr.designs.array[n];
		if (strcmp(sh->hidlib.fullpath, fpath) == 0) {
			sheet = sh;
			break;
		}
	}

	if (sheet == NULL) {
		rnd_message(RND_MSG_ERROR, "ProjectSheetType(): sheet '%s' not found\n", fpath);
		return 0;
	}

	if ((sheet->hidlib.fullpath == NULL) || (*sheet->hidlib.fullpath == '<')) {
		rnd_message(RND_MSG_ERROR, "sheet '%s' has no valid file name, try saving it first\n", fpath);
		return 0;
	}

	if (prj->hdr.fullpath == NULL)
		sch_rnd_project_create_file_for_sheet_gui(curr);

	if (prj->hdr.fullpath == NULL) /* cancel - no project file created */
		return 0;

	/* figure index-on-config-array of target sheet, if there's a list to look into */
	switch(sheet->stype) {
		case CSCH_SHTY_ROOT: listpath = "prj/root_sheets"; pidx = prj_find_sheet_in(prj, &conf_core.prj.root_sheets, fpath); break;
		case CSCH_SHTY_AUX:  listpath = "prj/aux_sheets"; pidx = prj_find_sheet_in(prj, &conf_core.prj.aux_sheets, fpath); break;
		default: /* no list to remove from */;
	}

	/* need to switch current sheet to get the project config */
	if (listpath != NULL)
		old = sch_rnd_multi_switch_to(sheet);

	if (strcmp(type, "root") == 0) {
		if (sheet->stype != CSCH_SHTY_ROOT) {
			prj_remove_sheet(prj, listpath, pidx);
			prj_append_sheet(prj, sheet, "prj/root_sheets");
			sheet->stype = CSCH_SHTY_ROOT;
			prj->num_root_sheets++;
		}
	}
	else if (strcmp(type, "aux") == 0) {
		if (sheet->stype != CSCH_SHTY_AUX) {
			prj_remove_sheet(prj, listpath, pidx);
			prj_append_sheet(prj, sheet, "prj/aux_sheets");
			sheet->stype = CSCH_SHTY_AUX;
			prj->num_aux_sheets++;
		}
	}
	else if (strcmp(type, "unlisted") == 0) {
		if (sheet->stype != CSCH_SHTY_UNLISTED) {
			prj_remove_sheet(prj, listpath, pidx);
			sheet->stype = CSCH_SHTY_UNLISTED;
			if (type[3] == 'o')
				also_unload = 1;
		}
	}
	else if (strcmp(type, "unload") == 0) {
		if (listpath != NULL) {
			prj_remove_sheet(prj, listpath, pidx);
			prj_flush(sheet, listpath);
		}

		sheet->stype = CSCH_SHTY_UNLISTED;
		also_unload = 1;
	}
	else {
		rnd_message(RND_MSG_ERROR, "ProjectSheetType(): invalid sheet type '%s'\n", type);
		goto error;
	}

	rnd_event(&sheet->hidlib, RND_EVENT_DESIGN_FN_CHANGED, NULL); /* get the final type displayed */

	if (old != NULL)
		sch_rnd_multi_switch_to(old);

	if (also_unload) {
		csch_sheet_t *next = (csch_sheet_t *)rnd_multi_neighbour_sheet(NULL);
		if (next != NULL) {
			sch_rnd_multi_unload(sheet);
			if (old == sheet)
				sch_rnd_multi_switch_to(next);
			rnd_event(&next->hidlib, CSCH_EVENT_SHEET_POSTUNLOAD, NULL);
		}
		else
			rnd_message(RND_MSG_ERROR, "ProjectSheetType(): can not unload the only sheet open\n");
	}

	RND_ACT_IRES(0);
	return 0;

	error:
	if (old != NULL)
		sch_rnd_multi_switch_to(old);
	return 0;
}

static const char csch_acts_SaveAll[] = "SaveAll([currprj])";
static const char csch_acth_SaveAll[] = "Save all unsaved sheets in current project (currprj)";
fgw_error_t csch_act_SaveAll(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	const char *scope = "currprj";
	csch_sheet_t *sheet = (csch_sheet_t *)RND_ACT_DESIGN;
	long n;
	csch_project_t *prj;

	RND_ACT_MAY_CONVARG(1, FGW_STR, SaveAll, scope = argv[1].val.str);

	RND_ACT_IRES(1);

	if (strcmp(scope, "currprj") != 0) {
		rnd_message(RND_MSG_ERROR, "ProjectSaveAll(): only current project scope (currprj) is supported at the moment\n");
		return FGW_ERR_ARG_CONV;
	}

	prj = (csch_project_t *)sheet->hidlib.project;
	assert(prj != NULL);

	RND_ACT_IRES(0);

	for(n = 0; n < prj->hdr.designs.used; n++) {
		csch_sheet_t *sheet = prj->hdr.designs.array[n];
		if (sheet->changed) {
			if (csch_save_sheet(sheet, sheet->hidlib.fullpath, "lihata") != 0) {
				rnd_message(RND_MSG_ERROR, "Can not save file '%s'\n", sheet->hidlib.fullpath);
				RND_ACT_IRES(-1);
			}
			else
				rnd_message(RND_MSG_INFO, "SaveAll: saved '%s'\n", sheet->hidlib.fullpath);
		}
	}

	return 0;
}

static rnd_action_t file_action_list[] = {
	{"Quit", csch_act_Quit, csch_acth_Quit, csch_acts_Quit},
	{"SymlibRehash", csch_act_SymlibRehash, csch_acth_SymlibRehash, csch_acts_SymlibRehash},
	{"SymlibReloadAllLocal", csch_act_SymlibReloadAllLocal, csch_acth_SymlibReloadAllLocal, csch_acts_SymlibReloadAllLocal},
	{"HlibraryRehash", csch_act_HlibraryRehash, csch_acth_HlibraryRehash, csch_acts_HlibraryRehash},
	{"Revert", csch_act_Revert, csch_acth_Revert, csch_acts_Revert},
	{"New", csch_act_New, csch_acth_New, csch_acts_New},
	{"Export", rnd_act_Export, rnd_acth_Export, rnd_acts_Export},
	{"ImportAttbl", csch_act_ImportAttbl, csch_acth_ImportAttbl, csch_acts_ImportAttbl},
	{"ProjectNew", csch_act_ProjectNew, csch_acth_ProjectNew, csch_acts_ProjectNew},
	{"ProjectLoadPartial", csch_act_ProjectLoadPartial, csch_acth_ProjectLoadPartial, csch_acts_ProjectLoadPartial},
	{"ProjectSheetType", csch_act_ProjectSheetType, csch_acth_ProjectSheetType, csch_acts_ProjectSheetType},
	{"SaveAll", csch_act_SaveAll, csch_acth_SaveAll, csch_acts_SaveAll},
};

void sch_rnd_file_act_init2(void)
{
	RND_REGISTER_ACTIONS(file_action_list, NULL);
}
