/* Copyright 1998 Acorn Computers Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/***************************************************/
/* File   : ItemInfo.c                             */
/*                                                 */
/* Purpose: Functions relating to the Item         */
/*          Information window.                    */
/*                                                 */
/* Author : A.D.Hodgkinson                         */
/*                                                 */
/* History: 11-Mar-98: Created.                    */
/***************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "swis.h"
#include "kernel.h"

#include "HTMLLib.h" /* HTML library API, Which will include html2_ext.h, tags.h and struct.h */

#include "wimp.h"
#include "wimplib.h"
#include "event.h"

#include "toolbox.h"
#include "window.h"

#include "svcprint.h"
#include "Global.h"
#include "MiscDefs.h"
#include "Utils.h"

#include "Browser.h"
#include "Fetch.h" /* For ISLINK and ISOBJECT */
#include "Forms.h"
#include "Images.h"
#include "Menus.h"
#include "Object.h"
#include "Reformat.h"
#include "Windows.h"

#include "ItemInfo.h"

/* Local statics */

static char * src_url = NULL;
static char * dst_url = NULL;

/* Static function prototypes */

static void iteminfo_info_not_available      (ObjectId o, ComponentId c);
static void iteminfo_info_none               (ObjectId o, ComponentId c);

static void iteminfo_store_source            (ObjectId o, const char * source);
static void iteminfo_store_destination       (ObjectId o, const char * destination);

static void iteminfo_send_to_keyboard_buffer (const char * s);
static void iteminfo_fill_right              (char * buffer, int buffer_size, const char * s);

/*************************************************/
/* iteminfo_info_not_available()                 */
/*                                               */
/* Fill in a display field with a string to show */
/* the information is not available.             */
/*                                               */
/* Parameters: Object ID of the object the       */
/*             display field lies in;            */
/*                                               */
/*             Component ID of the display       */
/*             field.                            */
/*************************************************/

static void iteminfo_info_not_available(ObjectId o, ComponentId c)
{
  displayfield_set_value(0,
                         o,
                         c,
                         lookup_token("IINA:N/A",0,0));

  return;
}

/*************************************************/
/* iteminfo_info_none()                          */
/*                                               */
/* Fill in a display field with a string to show */
/* no information is available / relevant.       */
/*                                               */
/* Parameters: Object ID of the object the       */
/*             display field lies in;            */
/*                                               */
/*             Component ID of the display       */
/*             field.                            */
/*************************************************/

static void iteminfo_info_none(ObjectId o, ComponentId c)
{
  displayfield_set_value(0,
                         o,
                         c,
                         lookup_token("IINone:(None)",0,0));

  return;
}

/*************************************************/
/* iteminfo_store_source()                       */
/*                                               */
/* Remember a URL as a source URL for some item, */
/* so the 'Export item' button can be used if    */
/* present. Any URL already stored is discarded. */
/*                                               */
/* Parameters: Object ID of the Item Information */
/*             dialogue;                         */
/*                                               */
/*             Pointer to the URL to remember.   */
/*************************************************/

static void iteminfo_store_source(ObjectId o, const char * source)
{
  free(src_url);
  src_url = NULL;

  /* Remember the URL */

  if (source && *source)
  {
    src_url = malloc(strlen(source) + 1);

    if (src_url) strcpy(src_url, source);
  }

  /* If we have a stored URL, ungrey the 'Export item' button, */
  /* else grey it out.                                         */

  if (src_url) set_gadget_state(o, IIExportItem, 0);
  else         set_gadget_state(o, IIExportItem, 1);

  return;
}

/*************************************************/
/* iteminfo_store_destination                    */
/*                                               */
/* Remember a URL as a destination URL for some  */
/* item, so the 'Follow link' button can be used */
/* if present. Any URL already stored is         */
/* discarded.                                    */
/*                                               */
/* Parameters: Object ID of the Item Information */
/*             dialogue;                         */
/*                                               */
/*             Pointer to the URL to remember.   */
/*************************************************/

static void iteminfo_store_destination(ObjectId o, const char * destination)
{
  free(dst_url);
  dst_url = NULL;

  /* Remember the URL */

  if (destination && *destination)
  {
    dst_url = malloc(strlen(destination) + 1);

    if (dst_url) strcpy(dst_url, destination);
  }

  /* If we have a stored URL, ungrey the 'Follow Link' button, */
  /* else grey it out.                                         */

  if (dst_url) set_gadget_state(o, IIFollowLink, 0);
  else         set_gadget_state(o, IIFollowLink, 1);

  return;
}

/*************************************************/
/* iteminfo_send_to_keyboard_buffer()            */
/*                                               */
/* Little routine to send a stream of chars to   */
/* the keyboard buffer.                          */
/*                                               */
/* Parameters: Pointer to the string to send     */
/*             (terminator is not included).     */
/*************************************************/

static void iteminfo_send_to_keyboard_buffer(const char * s)
{
  if (!s) return;

  while (*s)
  {
    _swix(OS_Byte,
          _INR(0,2),

          138,   /* Insert char into buffer                  */
          0,     /* Magic number alert - 0 = keyboard buffer */
          *s++); /* Char to insert                           */
  }
}

/*************************************************/
/* iteminfo_fill_right()                         */
/*                                               */
/* Fills in the given buffer with either the     */
/* whole of the given string, or, if it doesn't  */
/* fit, the rightmost part of it.                */
/*                                               */
/* Parameters: Pointer to the buffer;            */
/*                                               */
/*             Size of the buffer;               */
/*                                               */
/*             Pointer to the string to fill in. */
/*************************************************/

static void iteminfo_fill_right(char * buffer, int buffer_size, const char * s)
{
  int len;

  if (!s || !buffer || buffer_size < 1) return;

  len = strlen(s);

  if (len < buffer_size) strcpy(buffer, s);
  else strcpy(buffer, s + len - buffer_size + 1);

  return;
}

/*************************************************/
/* iteminfo_to_be_shown()                        */
/*                                               */
/* Called before the Item Information window is  */
/* shown - fills in the contents, based on the   */
/* HStream and browser_data pointer returned by  */
/* calls to menus_document_opened_over and       */
/* menus_document_over_browser.                  */
/*                                               */
/* Parameters are as standard for a Toolbox      */
/* event handler.                                */
/*************************************************/

int iteminfo_to_be_shown(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  ChkError(iteminfo_fill_in_contents(menus_document_over_browser(),
                                     menus_document_opened_over(),
                                     idb->self_id));

  return 1;
}

/*************************************************/
/* iteminfo_follow_link()                        */
/*                                               */
/* Called when the 'Export item' button is       */
/* clicked on (or rather, the EIIFollowLink      */
/* event is raised).                             */
/*                                               */
/* Parameters are as standard for a Toolbox      */
/* event handler.                                */
/*************************************************/

int iteminfo_follow_link(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  if (dst_url && *dst_url)
  {
    int ctrl;

    _swix(OS_Byte,
          _INR(0,1) | _OUT(1),

          121,
          129,

          &ctrl);

    /* If Control is not held down, follow the link */

    if (!ctrl)
    {
      ChkError(windows_create_browser(dst_url,
                                      NULL,
                                      NULL,
                                      NULL,
                                      Windows_CreateBrowser_Normal));
    }

    /* Otherwise, insert '<a href="<url>"></a>' into the keyboard buffer... */

    else
    {
      char back[5];

      iteminfo_send_to_keyboard_buffer("<a href=\"");
      iteminfo_send_to_keyboard_buffer(dst_url);
      iteminfo_send_to_keyboard_buffer("\"></a>");

      /* Move cursor back before '</a>' */

      back[0] = back[1] = back[2] = back[3] = 140;
      back[4] = 0;

      iteminfo_send_to_keyboard_buffer(back);
    }

    free(dst_url);
    dst_url = NULL;

    return 1;
  }
  else return 0;
}

/*************************************************/
/* iteminfo_export_item()                        */
/*                                               */
/* Called when the 'Export item' button is       */
/* clicked on (or rather, the EIIExportItem      */
/* event is raised).                             */
/*                                               */
/* Parameters are as standard for a Toolbox      */
/* event handler.                                */
/*************************************************/

int iteminfo_export_item(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  if (src_url && *src_url)
  {
    int ctrl;

    _swix(OS_Byte,
          _INR(0,1) | _OUT(1),

          121,
          129,

          &ctrl);

    /* If Control is not held down, save the object */

    if (!ctrl)
    {
      ChkError(windows_create_browser(src_url,
                                      NULL,
                                      NULL,
                                      NULL,
                                      Windows_CreateBrowser_SaveToFile));
    }

    /* Otherwise, use the text in the display fields to construct */
    /* part of what could be an IMG or OBJECT tag, or similar     */

    else
    {
      char number[32];

      /* 'src="..."' */

      iteminfo_send_to_keyboard_buffer("src=\"");
      iteminfo_send_to_keyboard_buffer(src_url);
      iteminfo_send_to_keyboard_buffer("\"");

      /* ' width="..."' */

      if (!(displayfield_get_value(0,
                                   idb->self_id,
                                   IIActualSizeWidthDisplay,
                                   number,
                                   sizeof(number),
                                   NULL)))
      {
        number[sizeof(number) - 1] = 0;

        if (atoi(number))
        {
          iteminfo_send_to_keyboard_buffer(" width=\"");
          iteminfo_send_to_keyboard_buffer(number);
          iteminfo_send_to_keyboard_buffer("\"");
        }
      }

      /* ' height="..."' */

      if (!(displayfield_get_value(0,
                                   idb->self_id,
                                   IIActualSizeHeightDisplay,
                                   number,
                                   sizeof(number),
                                   NULL)))
      {
        number[sizeof(number) - 1] = 0;

        if (atoi(number))
        {
          iteminfo_send_to_keyboard_buffer(" height=\"");
          iteminfo_send_to_keyboard_buffer(number);
          iteminfo_send_to_keyboard_buffer("\"");
        }
      }
    }

    free(src_url);
    src_url = NULL;

    return 1;
  }
  else return 0;
}

/*************************************************/
/* iteminfo_fill_in_contents()                   */
/*                                               */
/* Fill in the Item Information window based on  */
/* the given token.                              */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             relevant to the item;             */
/*                                               */
/*             Pointer to the HStream to use as  */
/*             a source of information;          */
/*                                               */
/*             Object ID of the Item Information */
/*             dialogue to fill in.              */
/*************************************************/

_kernel_oserror * iteminfo_fill_in_contents(browser_data * b, HStream * t, ObjectId o)
{
  char url[Limits_ShortURL];

  /* Invalidate the source and destination URLs */

  iteminfo_store_source      (o, NULL);
  iteminfo_store_destination (o, NULL);

  if (!t)
  {
    /* No token - so fill in fields with 'none' or 'not available' */

    iteminfo_info_none(o, IIItemTypeDisplay);

    iteminfo_info_not_available(o, IILinkToDisplay);
    iteminfo_info_not_available(o, IIFetchedFromDisplay);

    iteminfo_info_not_available(o, IIActualSizeWidthDisplay);
    iteminfo_info_not_available(o, IIActualSizeHeightDisplay);
    iteminfo_info_not_available(o, IIScaledToWidthDisplay);
    iteminfo_info_not_available(o, IIScaledToHeightDisplay);
  }
  else
  {
    /* Deal with anchors at a generic level - other special */
    /* types can override this if they really want to.      */

    if (ISLINK(t) && *t->anchor)
    {
      iteminfo_fill_right(url, sizeof(url), t->anchor);

      displayfield_set_value(0,
                             o,
                             IILinkToDisplay,
                             url);

      iteminfo_store_destination(o, t->anchor);
    }
    else iteminfo_info_none(o, IILinkToDisplay);

    /* Now go through known token types */

    if (t->tagno == TAG_INPUT || t->tagno == TAG_TEXTAREA || t->tagno == TAG_SELECT)
    {
      const char * sub;
      int          post = 2;

      /* Forms item - for the 'link to', give the form submission URL, */
      /* if available.                                                 */

      sub = form_submission_information(b, t, &post);

      if (sub && *sub)
      {
        switch (post)
        {
          case 0:
          {
            iteminfo_fill_right(url, sizeof(url) - sizeof(" (GET)") + 1, sub);
            strcat(url, " (GET)");
          }
          break;

          case 1:
          {
            iteminfo_fill_right(url, sizeof(url) - sizeof(" (POST)") + 1, sub);
            strcat(url, " (POST)");
          }
          break;

          default:
          {
            iteminfo_fill_right(url, sizeof(url), sub);
          }
          break;
        }

        displayfield_set_value(0,
                               o,
                               IILinkToDisplay,
                               url);

        iteminfo_store_destination(o, sub);
      }
      else iteminfo_info_none(o, IILinkToDisplay);

      /* INPUT TYPE=IMAGE is a special case. */

      if (HtmlINPUTtype(t) == inputtype_IMAGE)
      {
        BBox size;
        char num[32];

        displayfield_set_value(0,
                               o,
                               IIItemTypeDisplay,
                               lookup_token("IIFrmImg:Form element (image)",0,0));

        /* Output the image source URL */

        if (HtmlINPUTsrc(t))
        {
          iteminfo_fill_right(url, sizeof(url), HtmlINPUTsrc(t));

          displayfield_set_value(0,
                                 o,
                                 IIFetchedFromDisplay,
                                 url);

          iteminfo_store_source(o, HtmlINPUTsrc(t));
        }
        else iteminfo_info_not_available(o, IIFetchedFromDisplay);

        /* Get the scaled size */

        reformat_get_image_size(b, t, &size);

        sprintf(num, "%d", size.xmax - size.xmin);
        displayfield_set_value(0, o, IIScaledToWidthDisplay, num);

        sprintf(num, "%d", size.ymax - size.ymin);
        displayfield_set_value(0, o, IIScaledToHeightDisplay, num);

        /* Get the actual size */

        image_get_token_actual_size(b, t, &size.xmin, &size.ymin);

        if (!size.xmin && !size.ymin)
        {
          iteminfo_info_not_available(o, IIActualSizeWidthDisplay);
          iteminfo_info_not_available(o, IIActualSizeHeightDisplay);
        }
        else
        {
          sprintf(num, "%d", size.xmin);
          displayfield_set_value(0, o, IIActualSizeWidthDisplay, num);

          sprintf(num, "%d", size.ymin);
          displayfield_set_value(0, o, IIActualSizeHeightDisplay, num);
        }
      }
      else
      {
        displayfield_set_value(0,
                               o,
                               IIItemTypeDisplay,
                               lookup_token("IIFrmGen:Form element (miscellaneous)",0,0));

        goto do_unknown;
      }
    }
    else if (t->style & IMG)
    {
      BBox size;
      char num[32];

      /* Output the image source URL */

      if (t->src)
      {
        iteminfo_fill_right(url, sizeof(url), t->src);

        displayfield_set_value(0,
                               o,
                               IIFetchedFromDisplay,
                               url);

        iteminfo_store_source(o, t->src);
      }
      else iteminfo_info_not_available(o, IIFetchedFromDisplay);

      /* Get the scaled size */

      reformat_get_image_size(b, t, &size);

      sprintf(num, "%d", size.xmax - size.xmin);
      displayfield_set_value(0, o, IIScaledToWidthDisplay, num);

      sprintf(num, "%d", size.ymax - size.ymin);
      displayfield_set_value(0, o, IIScaledToHeightDisplay, num);

      /* Get the actual size */

      image_get_token_actual_size(b, t, &size.xmin, &size.ymin);

      if (!size.xmin && !size.ymin)
      {
        iteminfo_info_not_available(o, IIActualSizeWidthDisplay);
        iteminfo_info_not_available(o, IIActualSizeHeightDisplay);

        displayfield_set_value(0,
                               o,
                               IIItemTypeDisplay,
                               lookup_token("IIImgNA:Image (size unknown)",0,0));
      }
      else
      {
        sprintf(num, "%d", size.xmin);
        displayfield_set_value(0, o, IIActualSizeWidthDisplay, num);

        sprintf(num, "%d", size.ymin);
        displayfield_set_value(0, o, IIActualSizeHeightDisplay, num);

        displayfield_set_value(0,
                               o,
                               IIItemTypeDisplay,
                               lookup_token("IIImage:Image (size known)",0,0));
      }
    }
    else if (ISOBJECT(t))
    {
      const char * data    = HtmlOBJECTdata(t);
      const char * current = browser_fetch_url(b);
      const char * newdata;

      /* For OBJECTs, need to jump a few hoops to get the source URL out */

      if (!current) current = browser_current_url(b);

      if (current)
      {
        int known = 0;

        newdata = HtmlRelativiseURL(current, data, t);

        if (newdata)
        {
          if (object_token_is_image(b, t))
          {
            BBox size;
            char num[32];

            displayfield_set_value(0,
                                   o,
                                   IIItemTypeDisplay,
                                   lookup_token("IIObjImg:Object (image)",0,0));

            /* Output the object's source URL */

            iteminfo_fill_right(url, sizeof(url), newdata);

            displayfield_set_value(0,
                                   o,
                                   IIFetchedFromDisplay,
                                   (char *) newdata);

            iteminfo_store_source(o, newdata);

            /* Will not need the relativised source URL from HtmlRelativiseURL */
            /* again, so free it. If we forgot to do this, since it was        */
            /* allocated in the context of the token it'd get freed sooner or  */
            /* later with the rest of the stream.                              */

            HtmlFree((void *) newdata);

            /* Get the scaled size */

            object_get_token_object_size(b, t, &size);

            sprintf(num, "%d", size.xmax - size.xmin);
            displayfield_set_value(0, o, IIScaledToWidthDisplay, num);

            sprintf(num, "%d", size.ymax - size.ymin);
            displayfield_set_value(0, o, IIScaledToHeightDisplay, num);

            /* Get the actual size */

            image_get_token_actual_size(b, t, &size.xmin, &size.ymin);

            if (!size.xmin && !size.ymin)
            {
              iteminfo_info_not_available(o, IIActualSizeWidthDisplay);
              iteminfo_info_not_available(o, IIActualSizeHeightDisplay);
            }
            else
            {
              sprintf(num, "%d", size.xmin);
              displayfield_set_value(0, o, IIActualSizeWidthDisplay, num);

              sprintf(num, "%d", size.ymin);
              displayfield_set_value(0, o, IIActualSizeHeightDisplay, num);
            }

            known = 1;

          /* Closure of 'if (object_is_image(...))' */
          }

        /* Closure of 'if (newdata)' */
        }

        /* If the object type was not dealt with (not known), use */
        /* a generic string instead                               */

        if (!known)
        {
          displayfield_set_value(0,
                                 o,
                                 IIItemTypeDisplay,
                                 lookup_token("IIObjGen:Object (miscellaneous)",0,0));

          goto do_unknown;
        }
      }
    }
    else if (t->tagno == 0 && (t->style & PCDATA))
    {
      /* Some text */

      if (ISLINK(t) && *t->anchor)
      {
        displayfield_set_value(0,
                               o,
                               IIItemTypeDisplay,
                               lookup_token("IITxtLnk:Piece of text (link)",0,0));
      }
      else
      {
        displayfield_set_value(0,
                               o,
                               IIItemTypeDisplay,
                               lookup_token("IITxtPln:Piece of text (not a link)",0,0));
      }

      goto do_unknown;
    }
    else
    {
      /* This isn't an item type we know about [yet] */

      displayfield_set_value(0,
                             o,
                             IIItemTypeDisplay,
                             lookup_token("IIUnknown:(Unknown)",0,0));

do_unknown: /* Jumped to for things which know the general */
            /* item type, but can't fill in the details.   */

      /* Information is not available */

      iteminfo_info_not_available(o, IIFetchedFromDisplay);

      iteminfo_info_not_available(o, IIActualSizeWidthDisplay);
      iteminfo_info_not_available(o, IIActualSizeHeightDisplay);
      iteminfo_info_not_available(o, IIScaledToWidthDisplay);
      iteminfo_info_not_available(o, IIScaledToHeightDisplay);
    }
  }

  return NULL;
}