/*

IsMyHwOK: A tool for checking how well-supported hardware is using Free drivers.
Copyright (C) 2010  Thomas Glyn Dennis

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/>.

*/

#include <gtk/gtk.h>
#include <stdio.h>
#include <stdlib.h>
#include "hardware.h"
#include "osstuff.h"

/* Save the results to a file. */

void saveFile (const CsvTable *hw_list, gboolean html)
{

  GtkWidget *dialog = NULL;
  char      *output = NULL;

  /* Check whether any data actually exists. */

  if (!hw_list)
  {

    dialog = gtk_message_dialog_new (NULL,
                                      GTK_DIALOG_MODAL,
                                      GTK_MESSAGE_ERROR,
                                      GTK_BUTTONS_CLOSE,
                                      "No data to export.");

    if (dialog)
    {

      gtk_dialog_run (GTK_DIALOG (dialog));

      gtk_widget_destroy (dialog);

    } else { fprintf (stderr, "No data to export.\n"); }

    return;

  }

  /* Create file chooser dialog. */

  dialog = gtk_file_chooser_dialog_new ("Export",
                NULL, /* Parent window. */
                GTK_FILE_CHOOSER_ACTION_SAVE,
                GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
                NULL);

  if (!dialog)
  {

    fprintf (stderr, "Couldn't create dialog.\n");

    return;

  }

  /* Initialise. */

  if (html)
  {

    gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), "out.html");

  }
  else
  {

    gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), "out.csv");

  }

  gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
                                                  TRUE);

  /* Save the file. */

  if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
  {

    output = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));

    if (html) { csvSaveHTML (hw_list, output          ); }
    else      { csvSaveCSV  (hw_list, output, ',', '"'); }

    g_free (output);

  }

  /* Destroy the file dialog. */

  gtk_widget_destroy (dialog);

}

/* Save the results to a HTML file. */

void saveHTML (GtkWidget *w, gpointer data)
{

  if (!w) { return; }

  saveFile ((CsvTable*)data, TRUE);

}

/* Save the results as a CSV file. */

void saveCSV (GtkWidget *w, gpointer data)
{

  if (!w) { return; }

  saveFile ((CsvTable*)data, FALSE);

}

/* Submit a rating to the FSF. */

void fsf (GtkWidget *w, gpointer data)
{

  GtkWidget *dialog = NULL;

  if (!w) { return; }

  /* Check whether any data actually exists. */

  if (!data)
  {

    dialog = gtk_message_dialog_new (NULL,
                                      GTK_DIALOG_MODAL,
                                      GTK_MESSAGE_ERROR,
                                      GTK_BUTTONS_CLOSE,
                                      "No data to export.");

    if (dialog)
    {

      gtk_dialog_run (GTK_DIALOG (dialog));

      gtk_widget_destroy (dialog);

    } else { fprintf (stderr, "No data to export.\n"); }

    return;

  }

  /* Create dialog. */

  dialog = gtk_about_dialog_new ();

  if (!dialog)
  {

    fprintf (stderr, "Couldn't create dialog.\n");

    return;

  }

  /* Set up the dialog. */

  gtk_about_dialog_set_program_name (GTK_ABOUT_DIALOG (dialog),
    "FSF Hardware Pages");

  gtk_about_dialog_set_website (GTK_ABOUT_DIALOG (dialog),
    "http://www.fsf.org/resources/hw/");

  gtk_about_dialog_set_comments (GTK_ABOUT_DIALOG (dialog),
    "You can manually submit ratings via email: hardware@fsf.org");

  /* Run the dialog. */

  gtk_dialog_run (GTK_DIALOG (dialog));

  gtk_widget_destroy (dialog);

}

/* Submit a rating to h-node. */

void hnode (GtkWidget *w, gpointer data)
{

  GtkWidget *dialog = NULL;

  if (!w) { return; }

  /* Check whether any data actually exists. */

  if (!data)
  {

    dialog = gtk_message_dialog_new (NULL,
                                      GTK_DIALOG_MODAL,
                                      GTK_MESSAGE_ERROR,
                                      GTK_BUTTONS_CLOSE,
                                      "No data to export.");

    if (dialog)
    {

      gtk_dialog_run (GTK_DIALOG (dialog));

      gtk_widget_destroy (dialog);

    } else { fprintf (stderr, "No data to export.\n"); }

    return;

  }

  /* Create dialog. */

  dialog = gtk_about_dialog_new ();

  if (!dialog)
  {

    fprintf (stderr, "Couldn't create dialog.\n");

    return;

  }

  /* Set up the dialog. */

  gtk_about_dialog_set_program_name (GTK_ABOUT_DIALOG (dialog),
    "h-node");

  gtk_about_dialog_set_website (GTK_ABOUT_DIALOG (dialog),
    "http://www.h-node.com");

  gtk_about_dialog_set_comments (GTK_ABOUT_DIALOG (dialog),
    "Automatic h-node submission is not yet implemented.");

  /* Run the dialog. */

  gtk_dialog_run (GTK_DIALOG (dialog));

  gtk_widget_destroy (dialog);

}

/* About button function. */

void about (GtkWidget *w, gpointer data)
{

  const gchar *author[] = { "Thomas Dennis (go.busto@gmail.com)", NULL };
  const gchar *artist[] = { "No art is used by the program yet.", NULL };
  const gchar *dcmntr[] = { "Thomas Dennis (go.busto@gmail.com)", NULL };

  GtkWidget *dialog = NULL;

  if (!w || !data) { /* Quiet compiler warnings. */ }

  /* Create the dialog. */

  dialog = gtk_about_dialog_new ();

  if (!dialog)
  {

    fprintf (stderr, "Couldn't create About dialog.\n");

    return;

  }

  /* Set up the dialog. */

  gtk_about_dialog_set_program_name (GTK_ABOUT_DIALOG (dialog),
    "Is My Hardware OK?");

  gtk_about_dialog_set_copyright (GTK_ABOUT_DIALOG (dialog),
    "Copyright © 2010 Thomas Dennis");

  gtk_about_dialog_set_version (GTK_ABOUT_DIALOG (dialog), "v10.09.25");

  gtk_about_dialog_set_website (GTK_ABOUT_DIALOG (dialog),
    "http://www.gnu.org/licenses/gpl-3.0.html");

  gtk_about_dialog_set_authors     (GTK_ABOUT_DIALOG (dialog), author);
  gtk_about_dialog_set_artists     (GTK_ABOUT_DIALOG (dialog), artist);
  gtk_about_dialog_set_documenters (GTK_ABOUT_DIALOG (dialog), dcmntr);

  gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG (dialog),
    "en_GB: Thomas Dennis (go.busto@gmail.com)");

  gtk_about_dialog_set_comments (GTK_ABOUT_DIALOG (dialog),
    "Hardware Compatability Tool.");

  /* Run the dialog. */

  gtk_dialog_run (GTK_DIALOG (dialog));

  gtk_widget_destroy (dialog);

}

/* Create the program GUI. */

int guiBuild (const CsvTable *os, const CsvTable *hw)
{

  char *rgb_str[] = { "Grey", "Red",  "Yellow",  "Green" };

  enum
  {

    RGBA_COL,
    PCID_COL,
    TYPE_COL,
    NAME_COL,
    MAKE_COL,
    INFO_COL,
    N_COLS

  };

  GtkTreeStore *store = NULL;
  GtkTreeIter  iter;

  GtkCellRenderer   *renderer = NULL;
  GtkTreeViewColumn *column   = NULL;

  GtkWidget *scroll = NULL;

  GtkWidget *hwnd = NULL;
  GtkWidget *vbox = NULL;
  GtkWidget *hbox = NULL;
  GtkWidget *mbar = NULL;
  GtkWidget *tree = NULL;
  GtkWidget *gtkw = NULL;

  GtkMenuItem *menu = NULL;

  long rgba_index, i;

  /* Create main window. */

  hwnd = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_container_set_border_width (GTK_CONTAINER (hwnd), 0);
  gtk_window_set_title (GTK_WINDOW (hwnd), "Hardware Information");
  gtk_window_set_position (GTK_WINDOW (hwnd), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size (GTK_WINDOW (hwnd), 512, 384);
  gtk_widget_realize (hwnd);
  g_signal_connect (hwnd, "destroy", gtk_main_quit, NULL);

  /* Create VBOX. */

  vbox = gtk_vbox_new (FALSE, 0);
  gtk_container_add (GTK_CONTAINER (hwnd), vbox);

  /* Create menu bar. */

  mbar = gtk_menu_bar_new ();
  gtk_box_pack_start (GTK_BOX (vbox), mbar, FALSE, FALSE, 0);

  /* Create a device list for GTK to use. */

  store = gtk_tree_store_new (N_COLS,
                              G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
                              G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);

  if (hw)
  {

    for (i = 1; i < hw->height; i++)
    {

      /* Determine colour. */

      rgba_index = 0;

      if (hw->cell[0][i])
      {

        if      (hw->cell[0][i][0] == '1') { rgba_index = 1; }
        else if (hw->cell[0][i][0] == '2') { rgba_index = 2; }
        else if (hw->cell[0][i][0] == '3') { rgba_index = 3; }

      }

      /* Add entry. */

      gtk_tree_store_append (store, &iter, NULL);

      gtk_tree_store_set (store, &iter,
                          RGBA_COL, rgb_str[rgba_index],
                          PCID_COL, hw->cell[1][i],
                          TYPE_COL, hw->cell[2][i],
                          NAME_COL, hw->cell[3][i],
                          MAKE_COL, hw->cell[4][i],
                          INFO_COL, hw->cell[5][i],
                          -1);

    }

  }

  /* Create scrollable region (for the device list widget). */

  scroll = gtk_scrolled_window_new (NULL, NULL);

  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll),
                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

  gtk_box_pack_start (GTK_BOX (vbox), scroll, TRUE, TRUE, 0);

  /* Create device list widget. */

  tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));

  /* Create the Status column. */

  renderer = gtk_cell_renderer_text_new ();

  column = gtk_tree_view_column_new_with_attributes ("?", renderer,
                                        "cell-background", RGBA_COL, NULL);

  gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);

  /* Create the PCI/USB ID column. */

  renderer = gtk_cell_renderer_text_new ();

  column = gtk_tree_view_column_new_with_attributes ("PCI/USB ID", renderer,
                                                   "text", PCID_COL, NULL);

  gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);

  /* Create the Type column. */

  renderer = gtk_cell_renderer_text_new ();

  column = gtk_tree_view_column_new_with_attributes ("Type", renderer,
                                                   "text", TYPE_COL, NULL);

  gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);

  /* Create the Name column. */

  renderer = gtk_cell_renderer_text_new ();

  column = gtk_tree_view_column_new_with_attributes ("Name", renderer,
                                                   "text", NAME_COL, NULL);

  gtk_tree_view_column_set_resizable (column, TRUE);

  gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);

  /* Create the Make column. */

  renderer = gtk_cell_renderer_text_new ();

  column = gtk_tree_view_column_new_with_attributes ("Make", renderer,
                                                   "text", MAKE_COL, NULL);

  gtk_tree_view_column_set_resizable (column, TRUE);

  gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);

  /* Create the Info column. */

  renderer = gtk_cell_renderer_text_new ();

  column = gtk_tree_view_column_new_with_attributes ("Comments", renderer,
                                                   "text", INFO_COL, NULL);

  gtk_tree_view_column_set_resizable (column, TRUE);

  gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);

  /* Put the device list into the scrollable area. */

  gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scroll), tree);

  /* Set up the populated tree view. */

  gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW (tree), TRUE);
  gtk_tree_view_set_enable_search     (GTK_TREE_VIEW (tree), TRUE);
  gtk_tree_view_set_rules_hint        (GTK_TREE_VIEW (tree), TRUE);

  /* Create Export menu. */

  menu = GTK_MENU_ITEM (gtk_menu_item_new_with_mnemonic ("E_xport"));
  gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), gtk_menu_new ());
  gtk_container_add (GTK_CONTAINER (mbar), GTK_WIDGET (menu));

  gtkw = gtk_menu_item_new_with_mnemonic ("Export H_TML");
  g_signal_connect (gtkw, "activate", GTK_SIGNAL_FUNC (saveHTML), (gpointer)hw);
  gtk_container_add (GTK_CONTAINER (gtk_menu_item_get_submenu (menu)), gtkw);

  gtkw = gtk_menu_item_new_with_mnemonic ("Export _CSV");
  g_signal_connect (gtkw, "activate", GTK_SIGNAL_FUNC (saveCSV), (gpointer)hw);
  gtk_container_add (GTK_CONTAINER (gtk_menu_item_get_submenu (menu)), gtkw);

  /* Create Submit menu. */

  menu = GTK_MENU_ITEM (gtk_menu_item_new_with_mnemonic ("_Submit"));
  gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), gtk_menu_new ());
  gtk_container_add (GTK_CONTAINER (mbar), GTK_WIDGET (menu));

  gtkw = gtk_menu_item_new_with_mnemonic ("Submit to h-_node");
  g_signal_connect (gtkw, "activate", GTK_SIGNAL_FUNC (hnode), (gpointer)tree);
  gtk_container_add (GTK_CONTAINER (gtk_menu_item_get_submenu (menu)), gtkw);

  gtkw = gtk_menu_item_new_with_mnemonic ("Submit to _FSF");
  g_signal_connect (gtkw, "activate", GTK_SIGNAL_FUNC (fsf), (gpointer)tree);
  gtk_container_add (GTK_CONTAINER (gtk_menu_item_get_submenu (menu)), gtkw);

  /* Create Help menu. */

  menu = GTK_MENU_ITEM (gtk_menu_item_new_with_mnemonic ("_Help"));
  gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), gtk_menu_new ());
  gtk_container_add (GTK_CONTAINER (mbar), GTK_WIDGET (menu));

  gtkw = gtk_image_menu_item_new_from_stock (GTK_STOCK_ABOUT, NULL);
  g_signal_connect (gtkw, "activate", GTK_SIGNAL_FUNC (about), NULL);
  gtk_container_add (GTK_CONTAINER (gtk_menu_item_get_submenu (menu)), gtkw);

  /* Create HBOX. */

  hbox = gtk_hbox_new (FALSE, 4);
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 2);

  /* Create FSF website link button. */

  gtkw = gtk_link_button_new_with_label ("http://www.h-node.com/",
                                          "h-node Database");

  gtk_box_pack_start (GTK_BOX (hbox), gtkw, TRUE, TRUE, 0);

  gtk_box_pack_start (GTK_BOX (hbox), gtk_vseparator_new (), FALSE, FALSE, 0);

  /* Create kernel label. */

  gtkw = gtk_label_new (NULL);
  gtk_label_set_markup (GTK_LABEL(gtkw), "<b>Kernel: </b>");
  gtk_box_pack_start (GTK_BOX (hbox), gtkw, FALSE, TRUE, 0);

  if (os) { gtkw = gtk_label_new (os->cell[0][0]); }
  else    { gtkw = gtk_label_new ("Unknown");      }

  gtk_box_pack_start (GTK_BOX (hbox), gtkw, FALSE, TRUE, 0);

  gtk_box_pack_start (GTK_BOX (hbox), gtk_vseparator_new (), FALSE, FALSE, 0);

  /* Create version label. */

  gtkw = gtk_label_new (NULL);
  gtk_label_set_markup (GTK_LABEL(gtkw), "<b>Version: </b>");
  gtk_box_pack_start (GTK_BOX (hbox), gtkw, FALSE, TRUE, 0);

  if (os) { gtkw = gtk_label_new (os->cell[0][2]); }
  else    { gtkw = gtk_label_new ("Unknown");      }

  gtk_box_pack_start (GTK_BOX (hbox), gtkw, FALSE, TRUE, 0);

  gtk_box_pack_start (GTK_BOX (hbox), gtk_vseparator_new (), FALSE, FALSE, 0);

  /* Create machine label. */

  gtkw = gtk_label_new (NULL);
  gtk_label_set_markup (GTK_LABEL(gtkw), "<b>Machine: </b>");
  gtk_box_pack_start (GTK_BOX (hbox), gtkw, FALSE, TRUE, 0);

  if (os) { gtkw = gtk_label_new (os->cell[0][1]); }
  else    { gtkw = gtk_label_new ("Unknown");      }

  gtk_box_pack_start (GTK_BOX (hbox), gtkw, FALSE, TRUE, 0);

  gtk_box_pack_start (GTK_BOX (hbox), gtk_vseparator_new (), FALSE, FALSE, 0);

  /* Create FSF website link button. */

  gtkw = gtk_link_button_new_with_label ("http://www.fsf.org/resources/hw/",
                                          "FSF Hardware Page");

  gtk_box_pack_start (GTK_BOX (hbox), gtkw, TRUE, TRUE, 0);

  /* Display the GUI. */

  gtk_widget_show_all (hwnd);

  return 0;

}

/* Program entry point. */

int main (int argc, char *argv[])
{

  CsvTable *hw_info = NULL;
  CsvTable *os_info = NULL;

  GtkWidget *dialog = NULL;

  int result = 0;

  /* Initialise GTK. */

  g_log_set_handler ("Gtk", G_LOG_LEVEL_WARNING, (GLogFunc) gtk_false, NULL);
  gtk_init (&argc, &argv);
  g_log_set_handler ("Gtk", G_LOG_LEVEL_WARNING, g_log_default_handler, NULL);

  /* Get OS info. */

  os_info = osinfo ();

  if (!os_info) { fprintf (stderr, "Couldn't get OS info.\n"); }

  /* Create hardware table. */

  hw_info = getHardware ();

  if (!os_info) { fprintf (stderr, "Couldn't get hardware info.\n"); }

  /* Create GUI. */

  result = guiBuild (os_info, hw_info);

  /* Ensure that the GUI was successfully created. */

  if (result != 0)
  {

    dialog = gtk_message_dialog_new (NULL,
                                      GTK_DIALOG_MODAL,
                                      GTK_MESSAGE_ERROR,
                                      GTK_BUTTONS_CLOSE,
                                      "Couldn't create GUI.");

    if (dialog)
    {

      gtk_dialog_run (GTK_DIALOG (dialog));

      gtk_widget_destroy (dialog);

    } else { fprintf (stderr, "Couldn't create GUI.\n"); }

    return 1;

  }

  /* Main loop. */

  gtk_main ();

  /* Free data. */

  csvFree (hw_info);
  csvFree (os_info);

  /* Exit (success). */

  return 0;

}
