/* Lasem - SVG and Mathml library
 *
 * Copyright © 2010 Emmanuel Pacaud
 * Copyright © 2023 Matthew Jakeman
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General
 * Public License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1335, USA.
 *
 * Author:
 * 	Matthew Jakeman <mattjakemandev@gmail.com>
 */

#include <gtk/gtk.h>

#include <lsm.h>
#include <lsmdom.h>
#include <cairo.h>

#define VIEWER_TYPE_WINDOW (viewer_window_get_type())
G_DECLARE_FINAL_TYPE (ViewerWindow, viewer_window, VIEWER, WINDOW, GtkApplicationWindow)

struct _ViewerWindow
{
    GtkApplicationWindow parent_instance;

    GtkWidget *lasem_view;

    LsmDomDocument *document;
    LsmDomView *view;
    GError *error;
};

G_DEFINE_FINAL_TYPE (ViewerWindow, viewer_window, GTK_TYPE_APPLICATION_WINDOW)

static GtkWidget *
viewer_window_new (GtkApplication *app)
{
    return GTK_WIDGET (g_object_new (VIEWER_TYPE_WINDOW,
                                     "application", app,
                                     NULL));
}

static void
viewer_window_finalize (GObject *object)
{
    ViewerWindow *self = (ViewerWindow *)object;

    g_clear_pointer (&self->view, g_object_unref);
    g_clear_pointer (&self->document, g_object_unref);

    G_OBJECT_CLASS (viewer_window_parent_class)->finalize (object);
}

static void
viewer_window_class_init (ViewerWindowClass *klass)
{
    GObjectClass *object_class = G_OBJECT_CLASS (klass);

    object_class->finalize = viewer_window_finalize;
}

static void
display_error (ViewerWindow *window, GError *error, const char *error_text)
{
    char *message;

    message = (error != NULL)
        ? error->message
        : "Undetermined Error";

    GtkAlertDialog *dlg = gtk_alert_dialog_new ("%s: %s\n",
						error_text,
						message);

    gtk_window_set_modal (GTK_WINDOW (dlg), TRUE);
    g_signal_connect (dlg, "response", G_CALLBACK (gtk_window_destroy), NULL);

    gtk_window_present (GTK_WINDOW (dlg));
}

static void
viewer_window_load_from_file (ViewerWindow *window, const char *path)
{
    GError *error = NULL;

    gchar *contents;
    gsize length;

    if (!g_file_get_contents (path, &contents, &length, &error))
    {
        display_error (window, error, "Could not read file");

        if (error != NULL)
            g_error_free (error);

        return;
    }

    if (window->view)
    {
        g_clear_pointer (&window->view, g_object_unref);
        g_clear_pointer (&window->document, g_object_unref);
        g_clear_pointer (&window->error, g_object_unref);
    }

    window->document = lsm_dom_document_new_from_memory (contents, length, &error);

    if (!LSM_IS_DOM_DOCUMENT (window->document))
    {
        display_error (window, error, "Could not read MathML/SVG");

        if (error != NULL)
            g_error_free (error);

        return;
    }

    window->view = lsm_dom_document_create_view (window->document);
    gtk_widget_queue_draw (window->lasem_view);
}

static void
lasem_view_draw (GtkDrawingArea *drawing_area,
                 cairo_t        *cr,
                 int             width,
                 int             height,
                 ViewerWindow     *window)
{
    if (window->view == NULL)
        return;

    int margin = 25;

    guint content_width = 0;
    guint content_height = 0;
    guint baseline = 0;
    lsm_dom_view_get_size_pixels (window->view, &content_width, &content_height, &baseline);

    float multiplier = ((float)width - (2 * margin)) / content_width;
    lsm_dom_view_set_resolution (window->view, multiplier * LSM_DOM_VIEW_DEFAULT_RESOLUTION);

    lsm_dom_view_render (window->view, cr, margin, margin);
}

static void
viewer_on_file_open (GtkFileDialog *dialog,
                     GAsyncResult  *result,
                     ViewerWindow  *self)
{
    GFile *file;
    GError *error = NULL;

    file = gtk_file_dialog_open_finish (dialog, result, &error);

    if (G_IS_FILE (file))
    {
        char *path;

        path = g_file_get_path (file);
        viewer_window_load_from_file (self, path);
        g_free (path);
    }
    else
    {
        display_error (self, error, "File Chooser error");

        if (error != NULL)
            g_error_free (error);
    }
}

static void
viewer_show_file_chooser (ViewerWindow *self)
{
    GtkFileDialog *dialog;
    GtkFileFilter *filter;

    filter = gtk_file_filter_new ();
    gtk_file_filter_add_mime_type (filter, "text/mathml");
    gtk_file_filter_add_mime_type (filter, "image/svg+xml");
    gtk_file_filter_set_name (filter, "MathML/SVG Files");

    dialog = gtk_file_dialog_new ();
    gtk_file_dialog_set_modal (dialog, TRUE);
    gtk_file_dialog_set_default_filter (dialog, filter);

    gtk_file_dialog_open (dialog, GTK_WINDOW (self),
                          NULL, (GAsyncReadyCallback) viewer_on_file_open,
                          self);
}

static void
viewer_window_init (ViewerWindow *self)
{
    gtk_window_set_title (GTK_WINDOW (self), "Lasem Viewer");
    gtk_window_set_default_size (GTK_WINDOW (self), 800, 600);
    gtk_window_set_resizable (GTK_WINDOW (self), FALSE);

    GtkWidget *header_bar = gtk_header_bar_new ();
    gtk_window_set_titlebar (GTK_WINDOW (self), header_bar);

    GtkWidget *open_btn = gtk_button_new_with_label ("Open");
    gtk_widget_add_css_class (open_btn, "suggested-action");
    g_signal_connect_swapped (open_btn, "clicked", G_CALLBACK (viewer_show_file_chooser), self);
    gtk_header_bar_pack_start (GTK_HEADER_BAR (header_bar), open_btn);

    GtkWidget *lasem_view = gtk_drawing_area_new ();
    gtk_window_set_child (GTK_WINDOW (self), lasem_view);
    gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (lasem_view),
                                    (GtkDrawingAreaDrawFunc) lasem_view_draw,
                                    self, NULL);
    self->lasem_view = lasem_view;
}

static void
activate (GtkApplication* app,
          gpointer        user_data)
{
    GtkWidget *window = viewer_window_new(app);
    gtk_widget_set_visible (window, TRUE);
}

int
main (int    argc,
      char **argv)
{
    GtkApplication *app;
    int status;

    app = gtk_application_new ("org.lasem.Viewer", G_APPLICATION_DEFAULT_FLAGS);
    g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
    status = g_application_run (G_APPLICATION (app), argc, argv);
    g_object_unref (app);

    return status;
}
