/*
 * TilEm II
 *
 * Copyright (c) 2010-2011 Thibault Duponchelle 
 * Copyright (c) 2010 Benjamin Moody
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <gtk/gtk.h>
#include <glib/gstdio.h>
#include <ticalcs.h>
#include <tilem.h>

#include "gui.h"

/* Create a frame around the given widget, with a boldface label in
   the GNOME style */
GtkWidget* new_frame(const gchar* label, GtkWidget* contents)
{
	GtkWidget *frame, *align;
	char *str;

	str = g_strconcat("<b>", label, "</b>", NULL);
	frame = gtk_frame_new(str);
	g_free(str);

	g_object_set(gtk_frame_get_label_widget(GTK_FRAME(frame)),
	             "use-markup", TRUE, NULL);
	gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);

	align = gtk_alignment_new(0.5, 0.5, 1.0, 1.0);
	gtk_alignment_set_padding(GTK_ALIGNMENT(align), 6, 0, 12, 0);
	gtk_widget_show(align);
	gtk_container_add(GTK_CONTAINER(frame), align);
	gtk_container_add(GTK_CONTAINER(align), contents);
	gtk_widget_show(frame);

	return frame;
}

/* Get model name (abbreviation) for a TilEm model ID. */
const char * model_to_name(int model)
{
	const TilemHardware **models;
	int nmodels, i;

	tilem_get_supported_hardware(&models, &nmodels);
	for (i = 0; i < nmodels; i++)
		if (models[i]->model_id == model)
			return models[i]->name;

	return NULL;
}

/* Convert model name to a model ID. */
int name_to_model(const char *name)
{
	char *s;
	const TilemHardware **models;
	int nmodels, i, j;

	s = g_new(char, strlen(name) + 1);
	for (i = j = 0; name[i]; i++) {
		if (name[i] == '+')
			s[j++] = 'p';
		else if (name[i] != '-')
			s[j++] = g_ascii_tolower(name[i]);
	}
	s[j] = 0;

	tilem_get_supported_hardware(&models, &nmodels);
	for (i = 0; i < nmodels; i++) {
		if (!strcmp(s, models[i]->name)) {
			g_free(s);
			return models[i]->model_id;
		}
	}

	g_free(s);
	return 0;
}

/* Convert TilEm model ID to tifiles2 model ID. */
CalcModel model_to_calcmodel(int model)
{
	switch (model) {
	case TILEM_CALC_TI73:
		return CALC_TI73;

	case TILEM_CALC_TI82:
		return CALC_TI82;

	case TILEM_CALC_TI83:
	case TILEM_CALC_TI76:
		return CALC_TI83;

	case TILEM_CALC_TI83P:
	case TILEM_CALC_TI83P_SE:
		return CALC_TI83P;

	case TILEM_CALC_TI84P:
	case TILEM_CALC_TI84P_SE:
	case TILEM_CALC_TI84P_NSPIRE:
		return CALC_TI84P;

	case TILEM_CALC_TI85:
		return CALC_TI85;

	case TILEM_CALC_TI86:
		return CALC_TI86;

	default:
		return CALC_NONE;
	}
}

/* Convert tifiles2 model ID to TilEm model ID. */
int calcmodel_to_model(CalcModel model)
{
	switch (model) {
	case CALC_TI73:
		return TILEM_CALC_TI73;
	case CALC_TI82:
		return TILEM_CALC_TI82;
	case CALC_TI83:
		return TILEM_CALC_TI83;
	case CALC_TI83P:
		return TILEM_CALC_TI83P;
	case CALC_TI84P:
		return TILEM_CALC_TI84P;
	case CALC_TI85:
		return TILEM_CALC_TI85;
	case CALC_TI86:
		return TILEM_CALC_TI86;
	default:
		return 0;
	}
}

/* Get model ID for a given file. */
int file_to_model(const char *name)
{
	const char *p;
	TigContent *tig;
	int model;

	p = strrchr(name, '.');
	if (!p || strlen(p) < 4 || strchr(p, '/') || strchr(p, '\\'))
		return 0;
	p++;

	if (!g_ascii_strcasecmp(p, "prg"))
		return TILEM_CALC_TI81;

	if (!g_ascii_strncasecmp(p, "73", 2))
		return TILEM_CALC_TI73;
	if (!g_ascii_strncasecmp(p, "82", 2))
		return TILEM_CALC_TI82;
	if (!g_ascii_strncasecmp(p, "83", 2))
		return TILEM_CALC_TI83;
	if (!g_ascii_strncasecmp(p, "8x", 2))
		return TILEM_CALC_TI83P;
	if (!g_ascii_strncasecmp(p, "85", 2))
		return TILEM_CALC_TI85;
	if (!g_ascii_strncasecmp(p, "86", 2))
		return TILEM_CALC_TI86;

	if (!g_ascii_strcasecmp(p, "tig")
	    || !g_ascii_strcasecmp(p, "zip")) {
		/* read file and see what tifiles thinks the type is */
		tig = tifiles_content_create_tigroup(CALC_NONE, 0);
		tifiles_file_read_tigroup(name, tig);
		model = calcmodel_to_model(tig->model);
		tifiles_content_delete_tigroup(tig);
		return model;
	}

	return 0;
}

/* Get "base" model for file type support. */
int model_to_base_model(int calc_model)
{
	switch (calc_model) {
	case TILEM_CALC_TI83:
	case TILEM_CALC_TI76:
		return TILEM_CALC_TI83;

	case TILEM_CALC_TI83P:
	case TILEM_CALC_TI83P_SE:
	case TILEM_CALC_TI84P:
	case TILEM_CALC_TI84P_SE:
	case TILEM_CALC_TI84P_NSPIRE:
		return TILEM_CALC_TI83P;

	default:
		return calc_model;
	}
}

/* Check if calc is compatible with given file type. */
gboolean model_supports_file(int calc_model, int file_model)
{
	calc_model = model_to_base_model(calc_model);
	file_model = model_to_base_model(file_model);

	if (file_model == calc_model)
		return TRUE;

	if (file_model == TILEM_CALC_TI82
	    && (calc_model == TILEM_CALC_TI83
	        || calc_model == TILEM_CALC_TI83P))
		return TRUE;

	if (file_model == TILEM_CALC_TI83
	    && (calc_model == TILEM_CALC_TI83P))
		return TRUE;

	if (file_model == TILEM_CALC_TI85
	    && (calc_model == TILEM_CALC_TI86))
		return TRUE;

	return FALSE;
}

/* A popup which is used to let the user choose the model at startup */
char choose_rom_popup(GtkWidget *parent_window, const char *filename,
                      char default_model)
{
	const TilemHardware **models;
	GtkWidget *dlg, *vbox, *frame, *btn;
	GtkToggleButton **btns;
	char *ids, id = 0;
	int nmodels, noptions, i, j, defoption = 0, response;
	dword romsize;
	char *fn, *msg;

	tilem_get_supported_hardware(&models, &nmodels);

	/* determine ROM size for default model */
	for (i = 0; i < nmodels; i++)
		if (models[i]->model_id == default_model)
			break;

	g_return_val_if_fail(i < nmodels, 0);

	romsize = models[i]->romsize;

	/* all other models with same ROM size are candidates */
	noptions = 0;
	for (i = 0; i < nmodels; i++) {
		if (models[i]->model_id == default_model)
			defoption = noptions;
		if (models[i]->romsize == romsize)
			noptions++;
	}

	if (noptions < 2) /* no choice */
		return default_model;

	dlg = gtk_dialog_new_with_buttons("Select Calculator Type",
	                                  GTK_WINDOW(parent_window),
	                                  GTK_DIALOG_MODAL,
	                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
	                                  GTK_STOCK_OK, GTK_RESPONSE_OK,
	                                  NULL);
	gtk_dialog_set_alternative_button_order(GTK_DIALOG(dlg),
	                                        GTK_RESPONSE_OK,
	                                        GTK_RESPONSE_CANCEL,
	                                        -1);
	gtk_dialog_set_default_response(GTK_DIALOG(dlg),
	                                GTK_RESPONSE_OK);

	vbox = gtk_vbox_new(TRUE, 0);

	/* create radio buttons */

	btns = g_new(GtkToggleButton*, noptions);
	ids = g_new(char, noptions);
	btn = NULL;
	for (i = j = 0; i < nmodels; i++) {
		if (models[i]->romsize == romsize) {
			btn = gtk_radio_button_new_with_label_from_widget
				(GTK_RADIO_BUTTON(btn), models[i]->desc);
			btns[j] = GTK_TOGGLE_BUTTON(btn);
			ids[j] = models[i]->model_id;
			gtk_box_pack_start(GTK_BOX(vbox), btn, TRUE, TRUE, 3);
			j++;
		}
	}

	gtk_toggle_button_set_active(btns[defoption], TRUE);

	fn = g_filename_display_basename(filename);
	msg = g_strdup_printf("Calculator type for %s:", fn);
	frame = new_frame(msg, vbox);
	g_free(fn);
	g_free(msg);

	gtk_container_set_border_width(GTK_CONTAINER(frame), 6);
	gtk_widget_show_all(frame);

	vbox = gtk_dialog_get_content_area(GTK_DIALOG(dlg));
	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);

	response = gtk_dialog_run(GTK_DIALOG(dlg));

	if (response == GTK_RESPONSE_OK) {
		for (i = 0; i < noptions; i++) {
			if (gtk_toggle_button_get_active(btns[i])) {
				id = ids[i];
				break;
			}
		}
	}
	else {
		id = 0;
	}

	gtk_widget_destroy(dlg);
	g_free(btns);
	g_free(ids);

	return id;
}

/* Convert UTF-8 to filename encoding.  Use ASCII digits in place of
   subscripts if necessary.  If conversion fails utterly, fall back to
   the UTF-8 name, which is broken but better than nothing. */
char * utf8_to_filename(const char *utf8str)
{
	gchar *result, *ibuf, *obuf, *p;
	gsize icount, ocount;
	const gchar **charsets;
	GIConv ic;
	gunichar c;

	if (g_get_filename_charsets(&charsets))
		return g_strdup(utf8str);

	ic = g_iconv_open(charsets[0], "UTF-8");
	if (!ic) {
		g_warning("utf8_to_filename: unsupported charset %s",
		          charsets[0]);
		return g_strdup(utf8str);
	}

	ibuf = (gchar*) utf8str;
	icount = strlen(utf8str);
	ocount = icount * 2; /* be generous */
	result = obuf = g_new(gchar, ocount + 1);

	while (g_iconv(ic, &ibuf, &icount, &obuf, &ocount) == (gsize) -1) {
		if (errno != EILSEQ) {
			g_warning("utf8_to_filename: error in conversion");
			g_free(result);
			g_iconv_close(ic);
			return g_strdup(utf8str);
		}

		c = g_utf8_get_char(ibuf);
		if (c >= 0x2080 && c <= 0x2089)
			*obuf = c - 0x2080 + '0';
		else
			*obuf = '_';
		obuf++;
		ocount--;

		p = g_utf8_next_char(ibuf);
		icount -= p - ibuf;
		ibuf = p;
	}

	*obuf = 0;
	g_iconv_close(ic);
	return result;
}

/* Convert UTF-8 to a subset of UTF-8 that is compatible with the
   locale */
char * utf8_to_restricted_utf8(const char *utf8str)
{
	char *p, *q;
	p = utf8_to_filename(utf8str);
	q = g_filename_to_utf8(p, -1, NULL, NULL, NULL);
	g_free(p);
	if (q)
		return q;
	else
		return g_strdup(utf8str);
}

/* Generate default filename (UTF-8) for a variable */
char * get_default_filename(const TilemVarEntry *tve)
{
	GString *str = g_string_new("");

	if (tve->slot_str) {
		g_string_append(str, tve->slot_str);
		if (tve->name_str && tve->name_str[0]) {
			g_string_append_c(str, '-');
			g_string_append(str, tve->name_str);
		}
	}
	else if (tve->name_str && tve->name_str[0]) {
		g_string_append(str, tve->name_str);
	}
	else {
		g_string_append(str, "untitled");
	}
	g_string_append_c(str, '.');
	g_string_append(str, tve->file_ext);
	return g_string_free(str, FALSE);
}
