/*
 * AT-SPI - Assistive Technology Service Provider Interface
 * (Gnome Accessibility Project; https://wiki.gnome.org/Accessibility)
 *
 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include <libxml/parser.h>
#include <libxml/tree.h>

#include "my-atk.h"

#define ACCESSIBLE_NODE ((const xmlChar *) "accessible")
#define ACC_ACTION_NODE ((const xmlChar *) "accessible_action")
#define ACC_COMPONENT_NODE ((const xmlChar *) "accessible_component")
#define ACC_DOCUMENT_NODE ((const xmlChar *) "accessible_document")
#define ACC_HYPERLINK_NODE ((const xmlChar *) "accessible_hyperlink")
#define ACC_HYPERTEXT_NODE ((const xmlChar *) "accessible_hypertext")
#define ACC_IMAGE_NODE ((const xmlChar *) "accessible_image")
#define ACC_TABLE_NODE ((const xmlChar *) "accessible_table")
#define ACC_TABLE_CELL_NODE ((const xmlChar *) "accessible_table_cell")
#define ACC_EDIT_TEXT_NODE ((const xmlChar *) "accessible_editable_text")
#define ACC_TEXT_NODE ((const xmlChar *) "accessible_text")
#define ACC_SELECTION_NODE ((const xmlChar *) "accessible_selection")
#define ACC_VALUE_NODE ((const xmlChar *) "accessible_value")
#define ACTION_NODE ((const xmlChar *) "action")
#define DOCUMENT_NODE ((const xmlChar *) "document")
#define INTERFACE_NODE  ((const xmlChar *) "interface")
#define RELATION_NODE ((const xmlChar *) "relation")
#define STATE_NODE ((const xmlChar *) "state")
#define COMPONENT_NODE ((const xmlChar *) "component")
#define HYPERLINK_NODE ((const xmlChar *) "hyperlink")
#define HYPERTEXT_NODE ((const xmlChar *) "hypertext")
#define IMAGE_NODE ((const xmlChar *) "image")
#define TABLE_NODE ((const xmlChar *) "table")
#define TABLE_CELL_NODE ((const xmlChar *) "table_cell")
#define TEXT_NODE ((const xmlChar *) "text_node")
#define VALUE_NODE ((const xmlChar *) "value_node")
#define SELECT_NODE ((const xmlChar *) "select_node")

#define NAME_ATTR ((const xmlChar *) "name")
#define DESC_ATTR ((const xmlChar *) "description")
#define ROLE_ATTR ((const xmlChar *) "role")
#define MIN_ATTR ((const xmlChar *) "min")
#define MAX_ATTR ((const xmlChar *) "max")
#define CURRENT_ATTR ((const xmlChar *) "current")
#define STEP_ATTR ((const xmlChar *) "step")
#define COL_HEADER_ATTR ((const xmlChar *) "col_header")
#define ROW_HEADER_ATTR ((const xmlChar *) "row_header")
#define COL_DESC_ATTR ((const xmlChar *) "col_desc")
#define ROW_DESC_ATTR ((const xmlChar *) "row_desc")
#define SELECTED_ATTR ((const xmlChar *) "selected")
#define SELECTED_COL_ATTR ((const xmlChar *) "selected_col")
#define RELATION_TYPE_ATTR ((const xmlChar *) "relation_type")
#define RELATION_TARGET_NAME_ATTR ((const xmlChar *) "target_name")
#define STATE_TYPE_ATTR ((const xmlChar *) "state_enum")
#define ACTION_NAME_ATTR ((const xmlChar *) "action_name")
#define ACTION_DES_ATTR ((const xmlChar *) "action_description")
#define ACTION_KEY_BIND_ATTR ((const xmlChar *) "key_binding")
#define COMP_X_ATTR ((const xmlChar *) "x")
#define COMP_Y_ATTR ((const xmlChar *) "y")
#define COMP_WIDTH_ATTR ((const xmlChar *) "width")
#define COMP_HEIGHT_ATTR ((const xmlChar *) "height")
#define COMP_LAYER_ATTR ((const xmlChar *) "layer")
#define COMP_ZORDER_ATTR ((const xmlChar *) "zorder")
#define COMP_ALPHA_ATTR ((const xmlChar *) "alpha")
#define IMAGE_DES_ATTR ((const xmlChar *) "image_description")
#define IMAGE_LOCALE_ATTR ((const xmlChar *) "image_locale")
#define TEXT_TEXT_ATTR ((const xmlChar *) "text")
#define TEXT_BOLD_ATTR ((const xmlChar *) "bold_text")
#define TEXT_UNDERLINE_ATTR ((const xmlChar *) "underline_text")
#define TEXT_DUMMY_ATTR ((const xmlChar *) "dummy_text")
#define START_ATTR ((const xmlChar *) "start")
#define END_ATTR ((const xmlChar *) "end")
#define LINK_ATTR ((const xmlChar *) "link")
#define CELL_X_ATTR ((const xmlChar *) "cell_x")
#define CELL_Y_ATTR ((const xmlChar *) "cell_y")
#define ROW_SPAN_ATTR ((const xmlChar *) "row_span")
#define COLUMN_SPAN_ATTR ((const xmlChar *) "column_span")
#define SELECT_ATTR ((const xmlChar *) "selected")
#define PAGE_ATTR ((const xmlChar *) "page_no")
#define PAGE_NUM_ATTR ((const xmlChar *) "page_number")

MyAtkObject *relation_target = NULL;

static double atof_get_prop (xmlNode *node, const xmlChar *attr)
{
  double ret;
  xmlChar *str = xmlGetProp (node, attr);
  if (!str)
    return 0;
  ret = atof ((const char *)str);
  xmlFree(str);

  return ret;
}

static int atoi_get_prop (xmlNode *node, const xmlChar *attr)
{
  int ret;
  xmlChar *str = xmlGetProp (node, attr);
  if (!str)
    return 0;
  ret = atoi ((const char *)str);
  xmlFree(str);

  return ret;
}

static AtkAttribute *
get_atk_attribute (xmlNode *node, const xmlChar *attr)
{
  xmlChar *str;
  AtkAttribute *tmp = g_malloc (sizeof (AtkAttribute));

  if (!tmp)
    return NULL;

  str = xmlGetProp (node, attr);
  tmp->name = g_strdup ((const char *)attr);
  tmp->value = g_strdup ((const char *)str);

  free (str);
  return tmp;
}

static gpointer
create_atk_object_from_element (xmlNode *element)
{
  xmlNode *child_node;
  xmlNode *child_node2;

  gpointer obj;
  gpointer child_obj = NULL;
  AtkRelationSet *relation_set = NULL;
  AtkObject *array[1];
  AtkRelation *relation;
  AtkStateSet *state_set = NULL;
  AtkStateType state_type;

  xmlChar *name;
  xmlChar *description;
  xmlChar *state_enum;
  xmlChar *role;
  gint relation_type;
  xmlChar *relation_target_name;
  xmlChar *action_name;
  xmlChar *action_des;
  xmlChar *action_key_bind;
  xmlChar *image_des;
  xmlChar *image_locale;
  xmlChar *text;
  gint x_size, y_size;
  gint width, height;
  gint x_extent, y_extent, w_extent, h_extent;
  name = xmlGetProp (element, NAME_ATTR);
  description = xmlGetProp (element, DESC_ATTR);
  role = xmlGetProp (element, ROLE_ATTR);
  GType type = MY_TYPE_ATK_OBJECT;
  gint layer;
  gint zorder;
  gdouble alpha;

  if (!xmlStrcmp (element->name, ACCESSIBLE_NODE))
    type = MY_TYPE_ATK_OBJECT;

  if (!xmlStrcmp (element->name, ACC_ACTION_NODE))
    type = MY_TYPE_ATK_ACTION;

  if (!xmlStrcmp (element->name, ACC_COMPONENT_NODE))
    type = MY_TYPE_ATK_COMPONENT;

  if (!xmlStrcmp (element->name, ACC_DOCUMENT_NODE))
    type = MY_TYPE_ATK_DOCUMENT;

  if (!xmlStrcmp (element->name, ACC_EDIT_TEXT_NODE))
    type = MY_TYPE_ATK_EDITABLE_TEXT;

  if (!xmlStrcmp (element->name, ACC_HYPERLINK_NODE))
    type = MY_TYPE_ATK_HYPERTEXT;

  if (!xmlStrcmp (element->name, ACC_HYPERTEXT_NODE))
    type = MY_TYPE_ATK_HYPERTEXT;

  if (!xmlStrcmp (element->name, ACC_IMAGE_NODE))
    type = MY_TYPE_ATK_IMAGE;

  if (!xmlStrcmp (element->name, ACC_SELECTION_NODE))
    type = MY_TYPE_ATK_SELECTION;

  if (!xmlStrcmp (element->name, ACC_TEXT_NODE))
    type = MY_TYPE_ATK_TEXT;

  if (!xmlStrcmp (element->name, ACC_TABLE_NODE))
    type = MY_TYPE_ATK_TABLE;

  if (!xmlStrcmp (element->name, ACC_TABLE_CELL_NODE))
    type = MY_TYPE_ATK_TABLE_CELL;

  if (!xmlStrcmp (element->name, ACC_VALUE_NODE))
    type = MY_TYPE_ATK_VALUE;


  obj = g_object_new (type,
                      "accessible-name", name,
                      "accessible-description", description,
                      "accessible-role", atk_role_for_name ((const gchar *)role),
                      NULL);
  child_node = element->xmlChildrenNode;
  while (child_node != NULL) {
    if (!xmlStrcmp (child_node->name, ACCESSIBLE_NODE) ||
        !xmlStrcmp (child_node->name, ACC_ACTION_NODE) ||
        !xmlStrcmp (child_node->name, ACC_COMPONENT_NODE) ||
        !xmlStrcmp (child_node->name, ACC_DOCUMENT_NODE) ||
        !xmlStrcmp (child_node->name, ACC_EDIT_TEXT_NODE) ||
        !xmlStrcmp (child_node->name, ACC_HYPERLINK_NODE) ||
        !xmlStrcmp (child_node->name, ACC_HYPERTEXT_NODE) ||
        !xmlStrcmp (child_node->name, ACC_IMAGE_NODE) ||
        !xmlStrcmp (child_node->name, ACC_SELECTION_NODE) ||
        !xmlStrcmp (child_node->name, ACC_TABLE_NODE) ||
        !xmlStrcmp (child_node->name, ACC_TABLE_CELL_NODE) ||
        !xmlStrcmp (child_node->name, ACC_TEXT_NODE) ||
        !xmlStrcmp (child_node->name, ACC_VALUE_NODE)) {
      child_obj = create_atk_object_from_element (child_node);
      my_atk_object_add_child (obj, child_obj);
    }
    child_node2 = child_node->xmlChildrenNode;
    while (child_node2 != NULL) {
      if (!xmlStrcmp (child_node2->name, RELATION_NODE)) {
        relation_type = atoi_get_prop (child_node2, RELATION_TYPE_ATTR);
        relation_target_name = xmlGetProp (child_node2, RELATION_TARGET_NAME_ATTR);
        relation_set = atk_object_ref_relation_set (ATK_OBJECT (child_obj));
        array[0] = ATK_OBJECT (obj);
        relation = atk_relation_new (array, 1, relation_type);
        atk_relation_new (array, 1, relation_type);
        atk_relation_set_add (relation_set, relation);
        g_object_unref (relation);
        g_object_unref (relation_set);
        xmlFree (relation_target_name);
      }
      if (!xmlStrcmp (child_node2->name, STATE_NODE)) {
        state_set = atk_object_ref_state_set (ATK_OBJECT (child_obj));
        state_enum = xmlGetProp (child_node2, STATE_TYPE_ATTR);
        state_type = atk_state_type_for_name ((const gchar *)state_enum);
        atk_state_set_add_state (state_set, state_type);
        g_object_unref (state_set);
        xmlFree (state_enum);
      }
      if (!xmlStrcmp (child_node2->name, ACTION_NODE)) {
        action_name = xmlGetProp (child_node2, ACTION_NAME_ATTR);
        action_des = xmlGetProp (child_node2, ACTION_DES_ATTR);
        action_key_bind = xmlGetProp (child_node2, ACTION_KEY_BIND_ATTR);
        my_atk_action_add_action (child_obj, (const gchar *)action_name,
                                  (const gchar *)action_des,
                                  (const gchar *)action_key_bind);
      }
      if (!xmlStrcmp (child_node2->name, COMPONENT_NODE)) {
        x_extent = atoi_get_prop (child_node2, COMP_X_ATTR);
        y_extent = atoi_get_prop (child_node2, COMP_Y_ATTR);
        w_extent = atoi_get_prop (child_node2, COMP_WIDTH_ATTR);
        h_extent = atoi_get_prop (child_node2, COMP_HEIGHT_ATTR);
        layer = atoi_get_prop (child_node2, COMP_LAYER_ATTR);
        zorder = atoi_get_prop (child_node2, COMP_ZORDER_ATTR);
        alpha = atof_get_prop (child_node2, COMP_ALPHA_ATTR);
        atk_component_set_extents (ATK_COMPONENT (child_obj),
                                   x_extent,
                                   y_extent,
                                   w_extent,
                                   h_extent,
                                   ATK_XY_SCREEN);
        my_atk_component_set_layer (ATK_COMPONENT (child_obj), layer);
        my_atk_component_set_mdi_zorder (ATK_COMPONENT (child_obj), zorder);
        my_atk_component_set_alpha (ATK_COMPONENT (child_obj), alpha);
      }
      if (!xmlStrcmp (child_node2->name, DOCUMENT_NODE)) {
        my_atk_set_document (ATK_DOCUMENT(child_obj),
                             atoi_get_prop (child_node2, PAGE_ATTR),
                             atoi_get_prop (child_node2, PAGE_NUM_ATTR));
      }
      if (!xmlStrcmp (child_node2->name, HYPERLINK_NODE)) {
        xmlChar *text = xmlGetProp (child_node2, TEXT_TEXT_ATTR);

        my_atk_set_hypertext (ATK_HYPERTEXT (child_obj), (const gchar *)text);
        xmlFree (text);
      }
      if (!xmlStrcmp (child_node2->name, HYPERTEXT_NODE)) {
        xmlChar *text = xmlGetProp (child_node2, TEXT_TEXT_ATTR);

        my_atk_set_hypertext (ATK_HYPERTEXT (child_obj), (const gchar *)text);
        xmlFree (text);
      }
      if (!xmlStrcmp (child_node2->name, IMAGE_NODE)) {
        image_des = xmlGetProp (child_node2, IMAGE_DES_ATTR);
        x_size = atoi_get_prop (child_node2, COMP_X_ATTR);
        y_size = atoi_get_prop (child_node2, COMP_Y_ATTR);
        width = atoi_get_prop (child_node2, COMP_WIDTH_ATTR);
        height = atoi_get_prop (child_node2, COMP_HEIGHT_ATTR);
        image_locale = xmlGetProp (child_node2, IMAGE_LOCALE_ATTR);

        my_atk_set_image (ATK_IMAGE (child_obj),
                          (const gchar *)image_des,
                          x_size,
                          y_size,
                          width,
                          height,
                          (const gchar *)image_locale);
      }
      if (!xmlStrcmp (child_node2->name, TEXT_NODE)) {
        text = xmlGetProp (child_node2, TEXT_TEXT_ATTR);
        AtkAttributeSet *attrSet = NULL;
        AtkAttribute *a1 = get_atk_attribute (child_node2, TEXT_BOLD_ATTR);
        AtkAttribute *a2 = get_atk_attribute (child_node2, TEXT_UNDERLINE_ATTR);
        AtkAttribute *a3 = get_atk_attribute (child_node2, TEXT_DUMMY_ATTR);
        attrSet = g_slist_append(NULL, a1);
        attrSet = g_slist_append(attrSet, a2);
        attrSet = g_slist_append(attrSet, a3);
        my_atk_set_text (ATK_TEXT (child_obj),
                         (const gchar *)text,
                         atoi_get_prop (child_node2, COMP_X_ATTR),
                         atoi_get_prop (child_node2, COMP_Y_ATTR),
                         atoi_get_prop (child_node2, COMP_WIDTH_ATTR),
                         atoi_get_prop (child_node2, COMP_HEIGHT_ATTR),
                         attrSet);
      }
      if (!xmlStrcmp (child_node2->name, TABLE_CELL_NODE)) {
        my_atk_set_table_cell (ATK_TABLE_CELL (child_obj),
                               atoi_get_prop (child_node2, CELL_X_ATTR),
                               atoi_get_prop (child_node2, CELL_Y_ATTR),
                               atoi_get_prop (child_node2, ROW_SPAN_ATTR),
                               atoi_get_prop (child_node2, COLUMN_SPAN_ATTR));
      }
      if (!xmlStrcmp (child_node2->name, VALUE_NODE)) {
        my_atk_set_value (ATK_VALUE(child_obj),
                          atof_get_prop (child_node2, MIN_ATTR),
                          atof_get_prop (child_node2, CURRENT_ATTR),
                          atof_get_prop (child_node2, MAX_ATTR),
                          atof_get_prop (child_node2, STEP_ATTR));
      }
      child_node2 = child_node2->next;
    }
    child_node = child_node->next;
  }
  return obj;
}

/*
 * Reads the XML from filename and uses it
 * to create a tree of MyAtkObjects.
 *
 * returns: The root object of the tree.
 */
MyAtkObject *
atk_object_xml_parse (gchar *filename)
{
  xmlDoc *doc;
  xmlNode *root_element;
  MyAtkObject *new_atk_object = NULL;

  doc = xmlReadFile (filename, NULL, 0);
  g_assert (doc != NULL);

  root_element = xmlDocGetRootElement (doc);

  if (!root_element)
    return NULL;

  new_atk_object = create_atk_object_from_element (root_element);

  xmlFreeDoc (doc);
  return new_atk_object;
}
