/*
 * FIG : Facility for Interactive Generation of figures
 * Copyright (c) 1985-1988 by Supoj Sutanthavibul
 * Parts Copyright (c) 1989-2015 by Brian V. Smith
 * Parts Copyright (c) 1991 by Paul King
 * Parts Copyright (c) 2016-2024 by Thomas Loimer
 *
 * Any party obtaining a copy of these files is granted, free of charge, a
 * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
 * nonexclusive right and license to deal in this software and documentation
 * files (the "Software"), including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense and/or sell copies of
 * the Software, and to permit persons who receive copies from any such
 * party to do so, with the only requirement being that the above copyright
 * and this permission notice remain intact.
 *
 */

#include "f_load.h"

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Intrinsic.h>     /* includes X11/Xlib.h, which includes X11/X.h */
#include <X11/StringDefs.h>	/* XtNlabel, XtNstring */

#include "resources.h"
#include "mode.h"
#include "object.h"

#include "e_compound.h"
#include "f_read.h"
#include "f_util.h"
#include "u_bound.h"
#include "u_create.h"
#include "u_draw.h"
#include "u_list.h"
#include "u_redraw.h"
#include "u_undo.h"
#include "w_cmdpanel.h"
#include "w_cursor.h"
#include "w_export.h"
#include "w_grid.h"
#include "w_layers.h"
#include "w_msgpanel.h"
#include "w_print.h"
#include "w_rulers.h"
#include "w_setup.h"
#include "w_util.h"

/* LOCAL declarations */

static void	read_fail_message(char *file, int err);
static void	update_settings (fig_settings *settings);


/* load Fig file.

   Call with:
	file		= name of Fig file
	xoff, yoff	= offset from 0 (Fig units)
*/
int
load_file(char *file, int xoff, int yoff)
{
    int		    s;
    F_compound	    c;
    fig_settings    settings;

    put_msg("Loading file %s...",file);
    c.parent = NULL;
    c.GABPtr = NULL;
    c.arcs = NULL;
    c.compounds = NULL;
    c.ellipses = NULL;
    c.lines = NULL;
    c.splines = NULL;
    c.texts = NULL;
    c.comments = NULL;
    c.next = NULL;
    set_temp_cursor(wait_cursor);

    /* initialize the active_layers array */
    reset_layers();
    /* and depth counters */
    reset_depths();
    /* object counters for depths */
    clearallcounts();

    s = read_figc(file, &c, DONT_MERGE, REMAP_IMAGES, xoff, yoff, &settings);
    defer_update_layers = 1;	/* so update_layers() won't update for each object */
    add_compound_depth(&c);	/* count objects at each depth */
    defer_update_layers = 0;
    update_layers();

    if (s == 0) {		/* Successful read */
	clean_up();
	(void) strcpy(save_filename, cur_filename);
	update_cur_filename(file);
	/* in case the user is inside any compounds */
	close_all_compounds();
	saved_objects = objects;
	objects = c;

	/* update the settings in appres.xxx from the settings struct returned from read_fig */
	update_settings(&settings);
	/* reset the grid menus in print/export panels */
	reset_grid_menus(appres.INCHES);

	/* and draw the figure on the canvas*/
	redisplay_canvas();

	put_msg("Current figure \"%s\" (%d objects)", file, num_object);
	set_action(F_LOAD);
	reset_cursor();
	/* reset modified flag in case any change in orientation set it */
	reset_modifiedflag();
	/* update the recent list */
	update_recent_list(file);
	return 0;
    } else if (s == ENOENT || s == EMPTY_FILE) {
	char fname[PATH_MAX];

	clean_up();
	saved_objects = objects;
	objects = c;
	redisplay_canvas();
	put_msg("Current figure \"%s\" (new file)", file);
	(void) strcpy(save_filename, cur_filename);
	/* update current file name with null */
	update_cur_filename(file);
	/* update title bar with "(new file)" */
	strcpy(fname,file);
	strcat(fname," (new file)");
	update_wm_title(fname);
	set_action(F_LOAD);
	reset_cursor();
	reset_modifiedflag();
	return 0;
    }

    read_fail_message(file, s);
    reset_modifiedflag();
    reset_cursor();
    return 1;
}

static void
update_settings(fig_settings *settings)
{
	DeclareArgs(2);
	char    buf[30];

	/* set landscape flag oppositely and change_orient() will toggle it */
	if (settings->landscape != (int) appres.landscape) {
	    /* change_orient toggles */
	    appres.landscape = ! ((Boolean) settings->landscape);
	    /* now change the orientation of the canvas */
	    change_orient();
	    /* and flush to let things settle out */
	    app_flush();
	}
	/* set the printer and export justification labels */
	appres.flushleft = settings->flushleft;
	FirstArg(XtNlabel, just_items[(int)settings->flushleft]);
	if (export_popup)
	    SetValues(export_just_panel);
	if (print_popup)
	    SetValues(print_just_panel);

	/* set the rulers/grid accordingly */
	if (settings->units != (int) appres.INCHES || cur_gridunit != settings->grid_unit) {
	    /* reset the units string for the length messages */
	    strcpy(cur_fig_units, settings->units ? "in" : "cm");
	    /* and grid unit (1/16th, 1/10th, etc) */
	    cur_gridunit = settings->grid_unit;

	    appres.INCHES = (Boolean) settings->units;
	    /* PIC_FACTOR is the number of Fig units per printer points (1/72 inch) */
	    /* it changes with Metric and Imperial */
	    PIC_FACTOR  = (appres.INCHES ? (float)PIX_PER_INCH : 2.54*PIX_PER_CM)/72.0;
	    /* make sure we aren't previewing a figure in the file panel */
	    if (canvas_win == main_canvas) {
		reset_rulers();
		init_grid();
		setup_grid();
		/* change the label in the units widget */
		FirstArg(XtNlabel, appres.INCHES ? "in" : "cm");
		SetValues(unitbox_sw);
	    }
	}

	/* set the unit indicator in the upper-right corner */
	set_unit_indicator(False);

	/* paper size */
	if ((appres.papersize = settings->papersize) < 0) {
	    file_msg("Illegal paper size in file, using default");
	    appres.papersize = (appres.INCHES? PAPER_LETTER: PAPER_A4);
	}
	/* and the print and export paper size menus */
	FirstArg(XtNlabel, paper_sizes[appres.papersize].fname);
	if (export_popup)
	    SetValues(export_papersize_panel);
	if (print_popup)
	    SetValues(print_papersize_panel);

	appres.magnification = settings->magnification;
	/* set the magnification in the export and print panels */
	sprintf(buf,"%.2f",appres.magnification);
	FirstArg(XtNstring, buf);
	if (export_popup)
	    SetValues(export_mag_text);
	if (print_popup) {
	    SetValues(print_mag_text);
	    print_update_figure_size();
	}

	/* multi-page setting */
	appres.multiple = settings->multiple;
	FirstArg(XtNlabel, multiple_pages[(int)appres.multiple]);
	if (export_popup)
	    SetValues(export_multiple_panel);
	if (print_popup)
	    SetValues(print_multiple_panel);

	/* GIF transparent color */
	appres.transparent = settings->transparent;
	/* make colorname from number */
	set_color_name(appres.transparent, buf);
	FirstArg(XtNlabel, buf);
	if (export_popup)
	    SetValues(export_transp_panel);
}

void merge_file(char *file, int xoff, int yoff)
{
    F_compound	   *c, *c2;
    int		    s;
    fig_settings    settings;

    if ((c = create_compound()) == NULL)
	return;
    c->arcs = NULL;
    c->compounds = NULL;
    c->ellipses = NULL;
    c->lines = NULL;
    c->splines = NULL;
    c->texts = NULL;
    c->comments = NULL;
    c->next = NULL;

    set_temp_cursor(wait_cursor);

    /* clear picture object read flag */
    pic_obj_read = False;

    /* read merged file into compound */
    s = read_figc(file, c, MERGE, DONT_REMAP_IMAGES, xoff, yoff, &settings);

    if (s == 0) {			/* Successful read */
	/* only if there are objects other than user colors */
	if (c) {
	    /* if there are no objects other than one compound, don't encapsulate
		   it in another compound */
	    if (c->arcs == 0 && c->ellipses == 0 && c->lines == 0 &&
		c->splines == 0 && c->texts == 0 &&
		(c->compounds != 0 && c->compounds->next == 0)) {
		    /* save ptr to embedded compound */
		    c2 = c->compounds;
		    /* free the toplevel */
		    free((char *) c);
	            c = c2;
	    }
	    compound_bound(c, &c->nwcorner.x, &c->nwcorner.y,
		   &c->secorner.x, &c->secorner.y);
	    clean_up();
	    /* add the depths of the objects besides adding the objects to the main list */
	    list_add_compound(&objects.compounds, c);
	    set_latestcompound(c);
	}
	/* must remap all EPS/GIF/XPMs now if any new pic objects were read */
	if (pic_obj_read)
	    remap_imagecolors();
	redraw_images(&objects);
	if (c)
	    redisplay_zoomed_region(c->nwcorner.x, c->nwcorner.y,
				c->secorner.x, c->secorner.y);
	put_msg("%d object(s) read from \"%s\"", num_object, file);
	set_action_object(F_ADD, O_COMPOUND);
	reset_cursor();
	set_modifiedflag();
    }
    read_fail_message(file, s);
    reset_cursor();
}

/* update the recent list */

void
update_recent_list(char *file)
{
    int    i;
    char   *name, path[PATH_MAX], num[3];

    /* prepend path if not already in name */
    if (file[0] != '/') {
	/* prepend path if not already there */
	get_directory(path);
	strcat(path, "/");
	strcat(path, file);
	file = path;
    }
    /* if this name is already in the list, delete it and move list up */
    for (i=0; i<num_recent_files; i++) {
	if (strcmp(file,&recent_files[i].name[2])==0)	 /* point past number in menu entry */
	    break;
    }
    /* already there? */
    if (i < num_recent_files) {
	/* yes, move others up */
	free(recent_files[i].name);
	for ( ; i < num_recent_files-1; i++)
	    recent_files[i] = recent_files[i+1];
	num_recent_files--;
    }

    /* first, push older entries down one slot */
    for (i=num_recent_files; i>0; i--) {
	if (i >= max_recent_files) {
	    /* pushing superfluous off the end, free their name */
	    free(recent_files[i-1].name);
	    num_recent_files = max_recent_files;
	    continue;
	}
	/* shift down */
	recent_files[i] = recent_files[i-1];
	/* renumber entry */
	sprintf(num,"%1d",i+1);
	strncpy(recent_files[i].name,num,1);
    }

    /* put new entry in first slot */
    /* prepend with file number (1) */
    name = new_string(strlen(file)+3);
    sprintf(name,"1 %s",file);
    recent_files[0].name = name;
    if (num_recent_files < max_recent_files)
	num_recent_files++;

    /* now recreate the File menu with the new filenames */
    rebuild_file_menu(None);
    /* now update the user's .xfigrc file with the file list */
    update_recent_files();
}

static void
read_fail_message(char *file, int err)
{
    if (err == 0)		/* Successful read */
	return;
#ifdef ENAMETOOLONG
    else if (err == ENAMETOOLONG)
	file_msg("File name \"%s\" is too long", file);
#endif /* ENAMETOOLONG */
    else if (err == ENOENT)
	file_msg("File \"%s\" does not exist.", file);
    else if (err == ENOTDIR)
	file_msg("A name in the path \"%s\" is not a directory.", file);
    else if (err == EACCES)
	file_msg("Read access to file \"%s\" is blocked.", file);
    else if (err == EISDIR)
	file_msg("File \"%s\" is a directory.", file);
    else if (err == NO_VERSION)
	file_msg("File \"%s\" has no version number in header.", file);
    else if (err == EMPTY_FILE)
	file_msg("File \"%s\" is empty.", file);
    else if (err == BAD_FORMAT)
	/* Format error; relevant error message is already delivered */
	;
    else
	file_msg("File \"%s\" is not accessible; %s.", file, strerror(err));
}
