/* GIMP - The GNU Image Manipulation Program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * metadata-editor.c
 * Copyright (C) 2016, 2017 Ben Touchette
 *
 * 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 <https://www.gnu.org/licenses/>.
 */

#include "config.h"

#include <stdlib.h>
#include <ctype.h>

#include <gegl.h>
#include <gtk/gtk.h>
#include <gexiv2/gexiv2.h>

#include <glib.h>
#include <glib/gstdio.h>

#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>

#include "libgimp/stdplugins-intl.h"

#include "metadata-misc.h"
#include "metadata-xml.h"
#include "metadata-tags.h"

extern gboolean gimpmetadata;
extern gboolean force_write;

gboolean xmptag;
gboolean iptctag;
gboolean tagvalue;
gboolean taglistvalue;
gboolean tagname;
gboolean tagmode;
gboolean listelement;
gboolean element;
gchar *str_tag_value;
gchar *str_tag_name;
gchar *str_tag_mode;
gchar *str_element;
gchar *list_tag_data[256][256];
gint row_count = 0;
gint item_count = 0;


void
xml_parser_start_element (GMarkupParseContext  *context,
                          const gchar          *element_name,
                          const gchar         **attribute_names,
                          const gchar         **attribute_values,
                          gpointer              user_data,
                          GError              **error)
{
  if (strcmp (element_name, "gimp-metadata") == 0)
    {
      gimpmetadata = TRUE;
    }
  else if (strcmp (element_name, "iptc-tag") == 0)
    {
      item_count = 0;
      row_count = 0;
      iptctag = TRUE;
    }
  else if (strcmp (element_name, "xmp-tag") == 0)
    {
      item_count = 0;
      row_count = 0;
      xmptag = TRUE;
    }
  else if (strcmp (element_name, "tag-value") == 0)
    {
      tagvalue = TRUE;
    }
  else if (strcmp (element_name, "tag-list-value") == 0)
    {
      taglistvalue = TRUE;
    }
  else if (strcmp (element_name, "tag-name") == 0)
    {
      tagname = TRUE;
    }
  else if (strcmp (element_name, "tag-mode") == 0)
    {
      tagmode = TRUE;
    }
  else if (strcmp (element_name, "list-element") == 0)
    {
      listelement = TRUE;
      row_count += 1;
    }
  else if (strcmp (element_name, "element") == 0)
    {
      element = TRUE;
      item_count += 1;
    }
}

void
xml_parser_data (GMarkupParseContext *context,
                 const gchar         *text,
                 gsize                text_len,
                 gpointer             user_data,
                 GError             **error)
{
  if (tagvalue)
    {
      if (str_tag_value)
        g_free(str_tag_value);

      if (text)
        str_tag_value = g_strdup(text);
      else
        str_tag_value = g_strconcat("", NULL);
    }
  else if (tagname)
    {
      if (str_tag_name)
        g_free(str_tag_name);

      if (text)
        str_tag_name = g_strdup(text);
      else
        str_tag_name = g_strconcat("", NULL);
    }
  else if (tagmode)
    {
      if (str_tag_mode)
        g_free(str_tag_mode);

      if (text)
        str_tag_mode = g_strdup(text);
      else
        str_tag_mode = g_strconcat("", NULL);
    }
  else if (element)
    {
      if (str_element)
        g_free(str_element);

      if (text)
        str_element = g_strdup(text);
      else
        str_element = g_strconcat("", NULL);
    }
}

void
set_tag_ui (metadata_editor *args,
            gint             index,
            gchar           *name,
            gchar           *value,
            gchar*           mode)
{
  GtkWidget *widget;
  gchar *value_utf;

  widget = GTK_WIDGET (gtk_builder_get_object (args->builder, str_tag_name));

  if (!strcmp ("single", mode))
    {
      GtkEntry *entry_widget;

      value_utf = g_locale_to_utf8 (str_tag_value, -1, NULL, NULL, NULL);
      entry_widget = GTK_ENTRY (widget);
      gtk_entry_set_text (entry_widget, value_utf);
    }
  else if (!strcmp ("multi", mode))
    {
      GtkTextView   *text_view;
      GtkTextBuffer *buffer;

      value_utf = g_locale_to_utf8 (str_tag_value, -1, NULL, NULL, NULL);
      text_view = GTK_TEXT_VIEW (widget);
      buffer = gtk_text_view_get_buffer (text_view);
      gtk_text_buffer_set_text (buffer, value_utf, -1);
    }
  else if (!strcmp ("combo", mode))
    {
      gint32 value;

      value_utf = g_locale_to_utf8 (str_tag_value, -1, NULL, NULL, NULL);
      value = atoi(value_utf);
      gtk_combo_box_set_active (GTK_COMBO_BOX(widget), value);
    }
  else if (!strcmp ("list", mode))
    {
      GtkTreeModel  *treemodel;
      GtkListStore  *liststore;
      GtkTreeIter    iter;
      gint           number_of_rows;
      gint           row;
      gint           item;

      liststore = GTK_LIST_STORE(gtk_tree_view_get_model((GtkTreeView *)widget));
      treemodel = GTK_TREE_MODEL (liststore);
      number_of_rows =
        gtk_tree_model_iter_n_children(GTK_TREE_MODEL(liststore), NULL);

      /* Clear all current values */
      for (row = number_of_rows; row > -1; row--)
        {
          if (gtk_tree_model_iter_nth_child(treemodel, &iter, NULL, row))
            {
              gtk_list_store_remove(liststore, &iter);
            }
        }
      /* Add new values values */
      if (!strcmp ("Xmp.plus.Licensor", name))
        {
          for (row = 1; row < row_count+1; row++)
            {
              gtk_list_store_append (liststore, &iter);
              gtk_list_store_set (liststore, &iter,
                                  COL_LICENSOR_NAME, list_tag_data[row][1],
                                  COL_LICENSOR_ID, list_tag_data[row][2],
                                  COL_LICENSOR_PHONE1, list_tag_data[row][3],
                                  COL_LICENSOR_PHONE_TYPE1, list_tag_data[row][4],
                                  COL_LICENSOR_PHONE2, list_tag_data[row][5],
                                  COL_LICENSOR_PHONE_TYPE2, list_tag_data[row][6],
                                  COL_LICENSOR_EMAIL, list_tag_data[row][7],
                                  COL_LICENSOR_WEB, list_tag_data[row][8],
                                  -1);
              for (item = 1; item < G_N_ELEMENTS (licensor) + 1; item++)
                {
                  if (list_tag_data[row][item])
                    {
                      if (list_tag_data[row][item])
                        g_free(list_tag_data[row][item]);
                    }
                }
            }

          if (row_count < 2)
            {
              for (row = 0; row < 2 - row_count; row++)
                {
                  gtk_list_store_append (liststore, &iter);
                  gtk_list_store_set (liststore, &iter,
                                      COL_LICENSOR_NAME, NULL,
                                      COL_LICENSOR_ID, NULL,
                                      COL_LICENSOR_PHONE1, NULL,
                                      COL_LICENSOR_PHONE_TYPE1, NULL,
                                      COL_LICENSOR_PHONE2, NULL,
                                      COL_LICENSOR_PHONE_TYPE2, NULL,
                                      COL_LICENSOR_EMAIL, NULL,
                                      COL_LICENSOR_WEB, NULL,
                                      -1);
                }
            }
        }
      else if (!strcmp ("Xmp.plus.ImageCreator", name))
        {
          for (row = 1; row < row_count+1; row++)
            {
              gtk_list_store_append (liststore, &iter);
              gtk_list_store_set (liststore, &iter,
                                  COL_IMG_CR8_NAME, list_tag_data[row][1],
                                  COL_IMG_CR8_ID, list_tag_data[row][2],
                                  -1);
              for (item = 1; item < G_N_ELEMENTS (imagecreator) + 1; item++)
                {
                  if (list_tag_data[row][item])
                    {
                      if (list_tag_data[row][item])
                        g_free(list_tag_data[row][item]);
                    }
                }
            }

          if (row_count < 2)
            {
              for (row = 0; row < 2 - row_count; row++)
                {
                  gtk_list_store_append (liststore, &iter);
                  gtk_list_store_set (liststore, &iter,
                                      COL_IMG_CR8_NAME, NULL,
                                      COL_IMG_CR8_ID, NULL,
                                      -1);
                }
            }
        }
      else if (!strcmp ("Xmp.iptcExt.ArtworkOrObject", name))
        {
          for (row = 1; row < row_count+1; row++)
            {
              gtk_list_store_append (liststore, &iter);
              gtk_list_store_set (liststore, &iter,
                                  COL_AOO_TITLE, list_tag_data[row][1],
                                  COL_AOO_DATE_CREAT, list_tag_data[row][2],
                                  COL_AOO_CREATOR, list_tag_data[row][3],
                                  COL_AOO_SOURCE, list_tag_data[row][4],
                                  COL_AOO_SRC_INV_ID, list_tag_data[row][5],
                                  COL_AOO_CR_NOT, list_tag_data[row][6],
                                  -1);
              for (item = 1; item < G_N_ELEMENTS (artworkorobject) + 1; item++)
                {
                  if (list_tag_data[row][item])
                    {
                      if (list_tag_data[row][item])
                        g_free(list_tag_data[row][item]);
                    }
                }
            }

          if (row_count < 2)
            {
              for (row = 0; row < 2 - row_count; row++)
                {
                  gtk_list_store_append (liststore, &iter);
                  gtk_list_store_set (liststore, &iter,
                                      COL_AOO_TITLE, NULL,
                                      COL_AOO_DATE_CREAT, NULL,
                                      COL_AOO_CREATOR, NULL,
                                      COL_AOO_SOURCE, NULL,
                                      COL_AOO_SRC_INV_ID, NULL,
                                      COL_AOO_CR_NOT, NULL,
                                      -1);
                }
            }
        }
      else if (!strcmp ("Xmp.iptcExt.RegistryId", name))
        {
          for (row = 1; row < row_count+1; row++)
            {
              gtk_list_store_append (liststore, &iter);
              gtk_list_store_set (liststore, &iter,
                                  COL_REGSITRY_ORG_ID, list_tag_data[row][1],
                                  COL_REGSITRY_ITEM_ID, list_tag_data[row][2],
                                  -1);
              for (item = 1; item < G_N_ELEMENTS (registryid) + 1; item++)
                {
                  if (list_tag_data[row][item])
                    {
                      if (list_tag_data[row][item])
                        g_free(list_tag_data[row][item]);
                    }
                }
            }

          if (row_count < 2)
            {
              for (row = 0; row < 2 - row_count; row++)
                {
                  gtk_list_store_append (liststore, &iter);
                  gtk_list_store_set (liststore, &iter,
                                      COL_REGSITRY_ORG_ID, NULL,
                                      COL_REGSITRY_ITEM_ID, NULL,
                                      -1);
                }
            }
        }
      else if (!strcmp ("Xmp.plus.CopyrightOwner", name))
        {
          if (row_count > 0)
            {
              for (row = 1; row < row_count+1; row++)
                {
                  gtk_list_store_append (liststore, &iter);
                  gtk_list_store_set (liststore, &iter,
                                      COL_CR_OWNER_NAME, list_tag_data[row][1],
                                      COL_CR_OWNER_ID, list_tag_data[row][2],
                                      -1);
                  for (item = 1; item < G_N_ELEMENTS (copyrightowner) + 1; item++)
                    {
                      if (list_tag_data[row][item])
                        {
                          if (list_tag_data[row][item])
                            g_free(list_tag_data[row][item]);
                        }
                    }
                }
            }

          if (row_count < 2)
            {
              for (row = 0; row < 2 - row_count; row++)
                {
                  gtk_list_store_append (liststore, &iter);
                  gtk_list_store_set (liststore, &iter,
                                      COL_CR_OWNER_NAME, NULL,
                                      COL_CR_OWNER_ID, NULL,
                                      -1);
                }
            }
        }
      else if (!strcmp ("Xmp.iptcExt.LocationShown", name))
        {
          for (row = 1; row < row_count+1; row++)
            {
              gtk_list_store_append (liststore, &iter);
              gtk_list_store_set (liststore, &iter,
                                  COL_LOC_SHO_SUB_LOC, list_tag_data[row][1],
                                  COL_LOC_SHO_CITY, list_tag_data[row][2],
                                  COL_LOC_SHO_STATE_PROV, list_tag_data[row][3],
                                  COL_LOC_SHO_CNTRY, list_tag_data[row][4],
                                  COL_LOC_SHO_CNTRY_ISO, list_tag_data[row][5],
                                  COL_LOC_SHO_CNTRY_WRLD_REG, list_tag_data[row][6],
                                  -1);
              for (item = 1; item < G_N_ELEMENTS (locationshown) + 1; item++)
                {
                  if (list_tag_data[row][item])
                    {
                      if (list_tag_data[row][item])
                        g_free(list_tag_data[row][item]);
                    }
                }
            }

          if (row_count < 2)
            {
              for (row = 0; row < 2 - row_count; row++)
                {
                  gtk_list_store_append (liststore, &iter);
                  gtk_list_store_set (liststore, &iter,
                                      COL_LOC_SHO_SUB_LOC, NULL,
                                      COL_LOC_SHO_CITY, NULL,
                                      COL_LOC_SHO_STATE_PROV, NULL,
                                      COL_LOC_SHO_CNTRY, NULL,
                                      COL_LOC_SHO_CNTRY_ISO, NULL,
                                      COL_LOC_SHO_CNTRY_WRLD_REG, NULL,
                                      -1);
                }
            }
        }
      else if (!strcmp ("Xmp.iptcExt.OrganisationInImageName", name))
        {
          for (row = 1; row < row_count+1; row++)
            {
              gtk_list_store_append (liststore, &iter);
              gtk_list_store_set (liststore, &iter,
                                  COL_ORG_IMG_NAME, list_tag_data[row][1],
                                  -1);
              if (list_tag_data[row][1])
                {
                   if (list_tag_data[row][1])
                     g_free(list_tag_data[row][1]);
                }
            }

          if (row_count < 2)
            {
              for (row = 0; row < 2 - row_count; row++)
                {
                  gtk_list_store_append (liststore, &iter);
                  gtk_list_store_set (liststore, &iter,
                                      COL_ORG_IMG_NAME, NULL,
                                      -1);
                }
            }
        }
      else if (!strcmp ("Xmp.iptcExt.OrganisationInImageCode", name))
        {
          for (row = 1; row < row_count+1; row++)
            {
              gtk_list_store_append (liststore, &iter);
              gtk_list_store_set (liststore, &iter,
                                  COL_ORG_IMG_CODE, list_tag_data[row][1],
                                  -1);
              if (list_tag_data[row][1])
                {
                   if (list_tag_data[row][1])
                     g_free(list_tag_data[row][1]);
                }
            }

          if (row_count < 2)
            {
              for (row = 0; row < 2 - row_count; row++)
                {
                  gtk_list_store_append (liststore, &iter);
                  gtk_list_store_set (liststore, &iter,
                                      COL_ORG_IMG_CODE, NULL,
                                      -1);
                }
            }
        }
      else if (!strcmp ("Xmp.plus.PropertyReleaseID", name))
        {
          for (row = 1; row < row_count+1; row++)
            {
              gtk_list_store_append (liststore, &iter);
              gtk_list_store_set (liststore, &iter,
                                  COL_PROP_REL_ID, list_tag_data[row][1],
                                  -1);
              if (list_tag_data[row][1])
                {
                   if (list_tag_data[row][1])
                     g_free(list_tag_data[row][1]);
                }
            }

          if (row_count < 2)
            {
              for (row = 0; row < 2 - row_count; row++)
                {
                  gtk_list_store_append (liststore, &iter);
                  gtk_list_store_set (liststore, &iter,
                                      COL_PROP_REL_ID, NULL,
                                      -1);
                }
            }
        }
      else if (!strcmp ("Xmp.plus.ModelReleaseID", name))
        {
          for (row = 1; row < row_count+1; row++)
            {
              gtk_list_store_append (liststore, &iter);
              gtk_list_store_set (liststore, &iter,
                                  COL_MOD_REL_ID, list_tag_data[row][1],
                                  -1);
              if (list_tag_data[row][1])
                {
                   if (list_tag_data[row][1])
                     g_free(list_tag_data[row][1]);
                }
            }

          if (row_count < 2)
            {
              for (row = 0; row < 2 - row_count; row++)
                {
                  gtk_list_store_append (liststore, &iter);
                  gtk_list_store_set (liststore, &iter,
                                      COL_MOD_REL_ID, NULL,
                                      -1);
                }
            }
        }
    }
}

const gchar *
get_tag_ui_text (metadata_editor *args,
                 gchar           *name,
                 gchar           *mode)
{
  GObject *object;

  object = gtk_builder_get_object (args->builder, name);

  if (! strcmp ("single", mode))
    {
      GtkEntry *entry = GTK_ENTRY (object);
      return gtk_entry_get_text (entry);
    }
  else if (!strcmp ("multi", mode))
    {
      GtkTextView   *text_view = GTK_TEXT_VIEW (object);
      GtkTextBuffer *buffer;
      GtkTextIter    start;
      GtkTextIter    end;

      buffer = gtk_text_view_get_buffer (text_view);
      gtk_text_buffer_get_start_iter (buffer, &start);
      gtk_text_buffer_get_end_iter (buffer, &end);

      return gtk_text_buffer_get_text (buffer, &start, &end, TRUE);
    }

  return NULL;
}

gchar *
get_tag_ui_list (metadata_editor *args, gchar *name, gchar *mode)
{
  GObject       *object;
  GtkWidget     *widget;
  GtkTreeModel  *treemodel;
  GtkListStore  *liststore;
  GtkTreeIter    iter;
  gchar         *xmldata;
  gint           number_of_rows;
  gint           row;
  gint           has_data;
  gchar         *tagdata[256][256];

  has_data = FALSE;
  xmldata = (gchar*)g_malloc(262144);
  object = gtk_builder_get_object (args->builder, name);
  widget = GTK_WIDGET(object);

  liststore = GTK_LIST_STORE(gtk_tree_view_get_model((GtkTreeView *)widget));
  treemodel = GTK_TREE_MODEL (liststore);
  number_of_rows =
    gtk_tree_model_iter_n_children(GTK_TREE_MODEL(liststore), NULL);

  for (row = 0; row < number_of_rows; row++)
    {
      if (gtk_tree_model_iter_nth_child(treemodel, &iter, NULL, row))
        {
          if (!strcmp ("Xmp.plus.Licensor", name))
            {
              gtk_tree_model_get (treemodel, &iter,
                                  COL_LICENSOR_NAME, &tagdata[row][0],
                                  COL_LICENSOR_ID, &tagdata[row][1],
                                  COL_LICENSOR_PHONE1, &tagdata[row][2],
                                  COL_LICENSOR_PHONE_TYPE1, &tagdata[row][3],
                                  COL_LICENSOR_PHONE2, &tagdata[row][4],
                                  COL_LICENSOR_PHONE_TYPE2, &tagdata[row][5],
                                  COL_LICENSOR_EMAIL, &tagdata[row][6],
                                  COL_LICENSOR_WEB, &tagdata[row][7],
                                  -1);

              if ((tagdata[row][0] != NULL && strlen(tagdata[row][0]) > 0) ||
                  (tagdata[row][1] != NULL && strlen(tagdata[row][1]) > 0) ||
                  (tagdata[row][2] != NULL && strlen(tagdata[row][2]) > 0) ||
                  (tagdata[row][3] != NULL && strlen(tagdata[row][3]) > 0) ||
                  (tagdata[row][4] != NULL && strlen(tagdata[row][4]) > 0) ||
                  (tagdata[row][5] != NULL && strlen(tagdata[row][5]) > 0) ||
                  (tagdata[row][6] != NULL && strlen(tagdata[row][6]) > 0) ||
                  (tagdata[row][7] != NULL && strlen(tagdata[row][7]) > 0))
                {
                  gint  types;

                  has_data = TRUE;

                  xmldata = g_strconcat (xmldata, "\t\t\t<list-element>\n", NULL);
                  for (types = 0; types < 8; types++)
                    {
                      xmldata = g_strconcat (xmldata, "\t\t\t\t<element>", NULL);
                      xmldata = g_strconcat (xmldata, tagdata[row][types], NULL);
                      xmldata = g_strconcat (xmldata, "</element>\n", NULL);
                    }
                  xmldata = g_strconcat (xmldata, "\t\t\t</list-element>\n", NULL);
                }
            }
          else if (!strcmp ("Xmp.plus.CopyrightOwner", name))
            {
              gtk_tree_model_get (treemodel, &iter,
                                  COL_CR_OWNER_NAME, &tagdata[row][0],
                                  COL_CR_OWNER_ID, &tagdata[row][1],
                                  -1);

              if ((tagdata[row][0] != NULL && strlen(tagdata[row][0]) > 0) ||
                  (tagdata[row][1] != NULL && strlen(tagdata[row][1]) > 0))
                {
                  has_data = TRUE;

                  xmldata = g_strconcat (xmldata, "\t\t\t<list-element>\n", NULL);
                  xmldata = g_strconcat (xmldata, "\t\t\t\t<element>", NULL);
                  xmldata = g_strconcat (xmldata, tagdata[row][0], NULL);
                  xmldata = g_strconcat (xmldata, "</element>\n", NULL);
                  xmldata = g_strconcat (xmldata, "\t\t\t\t<element>", NULL);
                  xmldata = g_strconcat (xmldata, tagdata[row][1], NULL);
                  xmldata = g_strconcat (xmldata, "</element>\n", NULL);
                  xmldata = g_strconcat (xmldata, "\t\t\t</list-element>\n", NULL);
                }
            }
          else if (!strcmp ("Xmp.plus.ImageCreator", name))
            {
              gtk_tree_model_get (treemodel, &iter,
                                  COL_IMG_CR8_NAME, &tagdata[row][0],
                                  COL_IMG_CR8_ID, &tagdata[row][1],
                                  -1);

              if ((tagdata[row][0] != NULL && strlen(tagdata[row][0]) > 0) ||
                  (tagdata[row][1] != NULL && strlen(tagdata[row][1]) > 0))
                {
                  gint  types;

                  has_data = TRUE;

                  xmldata = g_strconcat (xmldata, "\t\t\t<list-element>\n", NULL);
                  for (types = 0; types < 2; types++)
                    {
                      xmldata = g_strconcat (xmldata, "\t\t\t\t<element>", NULL);
                      xmldata = g_strconcat (xmldata, tagdata[row][types], NULL);
                      xmldata = g_strconcat (xmldata, "</element>\n", NULL);
                    }
                  xmldata = g_strconcat (xmldata, "\t\t\t</list-element>\n", NULL);
                }
            }
          else if (!strcmp ("Xmp.iptcExt.ArtworkOrObject", name))
            {
              gtk_tree_model_get (treemodel, &iter,
                                  COL_AOO_TITLE, &tagdata[row][0],
                                  COL_AOO_DATE_CREAT, &tagdata[row][1],
                                  COL_AOO_CREATOR, &tagdata[row][2],
                                  COL_AOO_SOURCE, &tagdata[row][3],
                                  COL_AOO_SRC_INV_ID, &tagdata[row][4],
                                  COL_AOO_CR_NOT, &tagdata[row][5],
                                  -1);

              if ((tagdata[row][0] != NULL && strlen(tagdata[row][0]) > 0) ||
                  (tagdata[row][1] != NULL && strlen(tagdata[row][1]) > 0) ||
                  (tagdata[row][2] != NULL && strlen(tagdata[row][2]) > 0) ||
                  (tagdata[row][3] != NULL && strlen(tagdata[row][3]) > 0) ||
                  (tagdata[row][4] != NULL && strlen(tagdata[row][4]) > 0) ||
                  (tagdata[row][5] != NULL && strlen(tagdata[row][5]) > 0))
                {
                  gint  types;

                  has_data = TRUE;

                  xmldata = g_strconcat (xmldata, "\t\t\t<list-element>\n", NULL);
                  for (types = 0; types < 6; types++)
                    {
                      xmldata = g_strconcat (xmldata, "\t\t\t\t<element>", NULL);
                      xmldata = g_strconcat (xmldata, tagdata[row][types], NULL);
                      xmldata = g_strconcat (xmldata, "</element>\n", NULL);
                    }
                  xmldata = g_strconcat (xmldata, "\t\t\t</list-element>\n", NULL);
                }
            }
          else if (!strcmp ("Xmp.iptcExt.RegistryId", name))
            {
              gtk_tree_model_get (treemodel, &iter,
                                  COL_REGSITRY_ORG_ID, &tagdata[row][0],
                                  COL_REGSITRY_ITEM_ID, &tagdata[row][1],
                                  -1);

              if ((tagdata[row][0] != NULL && strlen(tagdata[row][0]) > 0) ||
                  (tagdata[row][1] != NULL && strlen(tagdata[row][1]) > 0))
                {
                  gint  types;

                  has_data = TRUE;

                  xmldata = g_strconcat (xmldata, "\t\t\t<list-element>\n", NULL);
                  for (types = 0; types < 2; types++)
                    {
                      xmldata = g_strconcat (xmldata, "\t\t\t\t<element>", NULL);
                      xmldata = g_strconcat (xmldata, tagdata[row][types], NULL);
                      xmldata = g_strconcat (xmldata, "</element>\n", NULL);
                    }
                  xmldata = g_strconcat (xmldata, "\t\t\t</list-element>\n", NULL);
                }
            }
          else if (!strcmp ("Xmp.iptcExt.LocationShown", name))
            {
              gtk_tree_model_get (treemodel, &iter,
                                  COL_LOC_SHO_SUB_LOC, &tagdata[row][0],
                                  COL_LOC_SHO_CITY, &tagdata[row][1],
                                  COL_LOC_SHO_STATE_PROV, &tagdata[row][2],
                                  COL_LOC_SHO_CNTRY, &tagdata[row][3],
                                  COL_LOC_SHO_CNTRY_ISO, &tagdata[row][4],
                                  COL_LOC_SHO_CNTRY_WRLD_REG, &tagdata[row][5],
                                  -1);

              if ((tagdata[row][0] != NULL && strlen(tagdata[row][0]) > 0) ||
                  (tagdata[row][1] != NULL && strlen(tagdata[row][1]) > 0) ||
                  (tagdata[row][2] != NULL && strlen(tagdata[row][2]) > 0) ||
                  (tagdata[row][3] != NULL && strlen(tagdata[row][3]) > 0) ||
                  (tagdata[row][4] != NULL && strlen(tagdata[row][4]) > 0) ||
                  (tagdata[row][5] != NULL && strlen(tagdata[row][5]) > 0))
                {
                  gint  types;

                  has_data = TRUE;

                  xmldata = g_strconcat (xmldata, "\t\t\t<list-element>\n", NULL);
                  for (types = 0; types < 6; types++)
                    {
                      xmldata = g_strconcat (xmldata, "\t\t\t\t<element>", NULL);
                      xmldata = g_strconcat (xmldata, tagdata[row][types], NULL);
                      xmldata = g_strconcat (xmldata, "</element>\n", NULL);
                    }
                  xmldata = g_strconcat (xmldata, "\t\t\t</list-element>\n", NULL);
                }
            }
          else if (!strcmp ("Xmp.iptcExt.OrganisationInImageName", name))
            {
              gtk_tree_model_get (treemodel, &iter,
                                  COL_ORG_IMG_NAME, &tagdata[row][0],
                                  -1);

              if ((tagdata[row][0] != NULL && strlen(tagdata[row][0]) > 0))
                {
                  has_data = TRUE;

                  xmldata = g_strconcat (xmldata, "\t\t\t<list-element>\n", NULL);
                  xmldata = g_strconcat (xmldata, "\t\t\t\t<element>", NULL);
                  xmldata = g_strconcat (xmldata, tagdata[row][0], NULL);
                  xmldata = g_strconcat (xmldata, "</element>\n", NULL);
                  xmldata = g_strconcat (xmldata, "\t\t\t</list-element>\n", NULL);
                }
            }
          else if (!strcmp ("Xmp.iptcExt.OrganisationInImageCode", name))
            {
              gtk_tree_model_get (treemodel, &iter,
                                  COL_ORG_IMG_CODE, &tagdata[row][0],
                                  -1);

              if ((tagdata[row][0] != NULL && strlen(tagdata[row][0]) > 0))
                {
                  has_data = TRUE;

                  xmldata = g_strconcat (xmldata, "\t\t\t<list-element>\n", NULL);
                  xmldata = g_strconcat (xmldata, "\t\t\t\t<element>", NULL);
                  xmldata = g_strconcat (xmldata, tagdata[row][0], NULL);
                  xmldata = g_strconcat (xmldata, "</element>\n", NULL);
                  xmldata = g_strconcat (xmldata, "\t\t\t</list-element>\n", NULL);
                }
            }
          else if (!strcmp ("Xmp.plus.PropertyReleaseID", name))
            {
              gtk_tree_model_get (treemodel, &iter,
                                  COL_PROP_REL_ID, &tagdata[row][0],
                                  -1);

              if ((tagdata[row][0] != NULL && strlen(tagdata[row][0]) > 0))
                {
                  has_data = TRUE;

                  xmldata = g_strconcat (xmldata, "\t\t\t<list-element>\n", NULL);
                  xmldata = g_strconcat (xmldata, "\t\t\t\t<element>", NULL);
                  xmldata = g_strconcat (xmldata, tagdata[row][0], NULL);
                  xmldata = g_strconcat (xmldata, "</element>\n", NULL);
                  xmldata = g_strconcat (xmldata, "\t\t\t</list-element>\n", NULL);
                }
            }
          else if (!strcmp ("Xmp.plus.ModelReleaseID", name))
            {
              gtk_tree_model_get (treemodel, &iter,
                                  COL_MOD_REL_ID, &tagdata[row][0],
                                  -1);

              if ((tagdata[row][0] != NULL && strlen(tagdata[row][0]) > 0))
                {
                  has_data = TRUE;

                  xmldata = g_strconcat (xmldata, "\t\t\t<list-element>\n", NULL);
                  xmldata = g_strconcat (xmldata, "\t\t\t\t<element>", NULL);
                  xmldata = g_strconcat (xmldata, tagdata[row][0], NULL);
                  xmldata = g_strconcat (xmldata, "</element>\n", NULL);
                  xmldata = g_strconcat (xmldata, "\t\t\t</list-element>\n", NULL);
                }
            }
        }
    }

  if (has_data == TRUE)
    {
      return xmldata;
    }

  g_free(xmldata);

  return NULL;
}

gint
get_tag_ui_combo (metadata_editor *args, gchar *name, gchar *mode)
{
  GObject *object;
  GtkComboBoxText *combo;
  gint value;

  object = gtk_builder_get_object (args->builder, name);

  combo = GTK_COMBO_BOX_TEXT (object);
  value = gtk_combo_box_get_active (GTK_COMBO_BOX(combo));

  return value;
}

void
xml_parser_end_element (GMarkupParseContext  *context,
                        const gchar          *element_name,
                        gpointer              user_data,
                        GError              **error)
{
  metadata_editor *args;
  int i;

  args = user_data;

  if (strcmp (element_name, "gimp-metadata") == 0)
    {
      gimpmetadata = FALSE;
    }
  else if (strcmp (element_name, "iptc-tag") == 0)
    {
      iptctag = FALSE;
#ifdef _ENABLE_IPTC_TAG_
      if (str_tag_name && str_tag_value)
        {
          /* make sure to only allow supported tags */
          for (i = 0; i < G_N_ELEMENTS (equivalent_metadata_tags); i++)
            {
              if (strcmp(equivalent_metadata_tags[i].tag, str_tag_name) == 0)
                {
#ifdef _SET_IPTC_TAG_
                  set_tag_ui (args, i, str_tag_name, str_tag_value,
                              equivalent_metadata_tags[i].mode);
#endif
                  if (force_write == TRUE)
                    gexiv2_metadata_set_tag_string (args->metadata,
                                                    str_tag_name,
                                                    str_tag_value);
                  break;
                }
            }
        }
#endif
    }
  else if (strcmp (element_name, "xmp-tag") == 0)
    {
      xmptag = FALSE;
      if (strcmp (str_tag_mode, "list") != 0)
        {
          if (str_tag_name && str_tag_value)
            {
              /* make sure to only allow supported tags */
              for (i = 0; i < G_N_ELEMENTS (default_metadata_tags); i++)
                {
                  if (strcmp(default_metadata_tags[i].tag, str_tag_name) == 0)
                    {
                      set_tag_ui (args, i, str_tag_name, str_tag_value,
                                  default_metadata_tags[i].mode);
#ifdef _ENABLE_FORCE_WRITE_
                      if (force_write == TRUE)
                        gexiv2_metadata_set_tag_string (args->metadata,
                                                        str_tag_name,
                                                        str_tag_value);
#endif
                      break;
                    }
                }
            }

        }
      else if (strcmp (str_tag_mode, "list") == 0)
        {
          if (row_count > 0)
            {
              /* make sure to only allow supported tags */
              for (i = 0; i < G_N_ELEMENTS (default_metadata_tags); i++)
                {
                  if (strcmp(default_metadata_tags[i].tag, str_tag_name) == 0)
                    {
                      set_tag_ui (args, i, str_tag_name, str_tag_value,
                                       default_metadata_tags[i].mode);
#ifdef _ENABLE_FORCE_WRITE_
                      if (force_write == TRUE)
                        gexiv2_metadata_set_tag_string (args->metadata,
                                                        str_tag_name,
                                                        str_tag_value);
#endif
                      break;
                    }
                }
            }
          row_count = 0;
          item_count = 0;
        }
    }
  else if (strcmp (element_name, "tag-value") == 0)
    {
      tagvalue = FALSE;
    }
  else if (strcmp (element_name, "tag-list-value") == 0)
    {
      taglistvalue = FALSE;
    }
  else if (strcmp (element_name, "tag-name") == 0)
    {
      tagname = FALSE;
    }
  else if (strcmp (element_name, "tag-mode") == 0)
    {
      tagmode = FALSE;
    }
  else if (strcmp (element_name, "list-element") == 0)
    {
      listelement = FALSE;
      item_count = 0;
    }
  else if (strcmp (element_name, "element") == 0)
    {
      element = FALSE;
      list_tag_data[row_count][item_count] = g_strdup(str_element);
    }
}

gboolean
xml_parser_parse_file (GimpXmlParser  *parser,
                       const gchar    *filename,
                       GError        **error)
{
  GIOChannel *io;
  gboolean    success;

  g_return_val_if_fail (parser != NULL, FALSE);
  g_return_val_if_fail (filename != NULL, FALSE);
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

  io = g_io_channel_new_file (filename, "r", error);
  if (!io)
    return FALSE;

  success = xml_parser_parse_io_channel (parser, io, error);

  g_io_channel_unref (io);

  return success;
}

GimpXmlParser *
xml_parser_new (const GMarkupParser *markup_parser,
                gpointer             user_data)
{
  GimpXmlParser *parser;

  g_return_val_if_fail (markup_parser != NULL, NULL);

  parser = g_slice_new (GimpXmlParser);

  parser->context = g_markup_parse_context_new (markup_parser,
                    0, user_data, NULL);

  return parser;
}

void
xml_parser_free (GimpXmlParser *parser)
{
  g_return_if_fail (parser != NULL);

  g_markup_parse_context_free (parser->context);
  g_slice_free (GimpXmlParser, parser);
}

gboolean
parse_encoding (const gchar  *text,
                gint          text_len,
                gchar       **encoding)
{
  const gchar *start;
  const gchar *end;
  gint         i;

  g_return_val_if_fail (text, FALSE);

  if (text_len < 20)
    return FALSE;

  start = g_strstr_len (text, text_len, "<?xml");
  if (!start)
    return FALSE;

  end = g_strstr_len (start, text_len - (start - text), "?>");
  if (!end)
    return FALSE;

  *encoding = NULL;

  text_len = end - start;
  if (text_len < 12)
    return TRUE;

  start = g_strstr_len (start + 1, text_len - 1, "encoding");
  if (!start)
    return TRUE;

  start += 8;

  while (start < end && *start == ' ')
    start++;

  if (*start != '=')
    return TRUE;

  start++;

  while (start < end && *start == ' ')
    start++;

  if (*start != '\"' && *start != '\'')
    return TRUE;

  text_len = end - start;
  if (text_len < 1)
    return TRUE;

  for (i = 1; i < text_len; i++)
    if (start[i] == start[0])
      break;

  if (i == text_len || i < 3)
    return TRUE;

  *encoding = g_strndup (start + 1, i - 1);

  return TRUE;
}

gboolean
xml_parser_parse_io_channel (GimpXmlParser  *parser,
                             GIOChannel     *io,
                             GError        **error)
{
  GIOStatus    status;
  gchar        buffer[4096];
  gsize        len = 0;
  gsize        bytes;
  const gchar *io_encoding;
  gchar       *encoding = NULL;

  g_return_val_if_fail (parser != NULL, FALSE);
  g_return_val_if_fail (io != NULL, FALSE);
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

  io_encoding = g_io_channel_get_encoding (io);
  if (g_strcmp0 (io_encoding, "UTF-8"))
    {
      g_warning ("xml_parser_parse_io_channel():\n"
                 "The encoding has already been set on this GIOChannel!");
      return FALSE;
    }

  /* try to determine the encoding */

  g_io_channel_set_encoding (io, NULL, NULL);

  while (len < sizeof (buffer))
    {
      status = g_io_channel_read_chars (io, buffer + len, 1, &bytes, error);
      len += bytes;

      if (status == G_IO_STATUS_ERROR)
        return FALSE;
      if (status == G_IO_STATUS_EOF)
        break;

      if (parse_encoding (buffer, len, &encoding))
        break;
    }

  if (encoding)
    {
      if (! g_io_channel_set_encoding (io, encoding, error))
        return FALSE;

      if (encoding)
        g_free (encoding);
    }
  else
    {
      g_io_channel_set_encoding (io, "UTF-8", NULL);
    }

  while (TRUE)
    {
      if (!g_markup_parse_context_parse (parser->context, buffer, len, error))
        return FALSE;

      status = g_io_channel_read_chars (io,
                                        buffer, sizeof (buffer), &len, error);

      switch (status)
        {
        case G_IO_STATUS_ERROR:
          return FALSE;
        case G_IO_STATUS_EOF:
          return g_markup_parse_context_end_parse (parser->context, error);
        case G_IO_STATUS_NORMAL:
        case G_IO_STATUS_AGAIN:
          break;
        }
    }
}

