/* Copyright 1997 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   : Object.c                               */
/*                                                 */
/* Purpose: Handling OBJECT, APPLET and EMBED.     */
/*                                                 */
/* Author : A.D.Hodgkinson                         */
/*                                                 */
/* History: 05-Oct-97: Created.                    */
/***************************************************/

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

#include "swis.h"
#include "flex.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 "svcprint.h"
#include "Global.h"
#include "FromROSLib.h"
#include "Utils.h"

#include "Browser.h"
#include "Fetch.h" /* (Which itself includes URLstat.h) */
#include "FetchHTML.h"
#include "Images.h"
#include "Memory.h"
#include "MiscDefs.h"
#include "PlugIn.h"
#include "Redraw.h"
#include "Reformat.h"
#include "TokenUtils.h"
#include "URLveneer.h"

#include "Object.h"

/* Static function prototypes */

static int               object_get_token_object (browser_data * b, HStream * t);
static _kernel_oserror * object_get_object_size  (browser_data * b, int object, BBox * size);

/*************************************************/
/* object_new_object()                           */
/*                                               */
/* Adds a structure for a new Object to a        */
/* browser's array of Objects.                   */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             to add to;                        */
/*                                               */
/*             Pointer to a token representing   */
/*             the Object (OBJECT, EMBED or      */
/*             APPLET tag).                      */
/*************************************************/

_kernel_oserror * object_new_object(browser_data * b, HStream * t)
{
  _kernel_oserror * e;

  /* If this isn't an Object token, do nothing (well, */
  /* complain about it in TRACE builds).              */

  if (!t || t->tagno != TAG_OBJECT)
  {
    #ifdef TRACE

      erb.errnum = Utils_Error_Custom_Normal;

      sprintf(erb.errmess,
              "Token %08x passed to object_new_object does not represent an OBJECT, APPLET or EMBED tag",
              (int) t);

      return &erb;

    #endif

    return NULL;
  }

  /* Allocate memory for the item */

  RetError(memory_set_chunk_size(b,
                                 NULL,
                                 CK_OBJB,
                                 (b->nobjects + 1) * sizeof(object_info)));

  /* Fill in the new item */

  b->odata[b->nobjects].browser_instance_handle = 0;
  b->odata[b->nobjects].plugin_instance_handle  = 0;
  b->odata[b->nobjects].plugin_task_handle      = 0;
  b->odata[b->nobjects].token                   = t;

  /* Width and height initially come from the token, but if this */
  /* ends up leading to a Plug-In being called, that Plug-In     */
  /* could ask for the size to change; hence the 'width' and     */
  /* 'height' fields.                                            */

  b->odata[b->nobjects].units                   = UNITS_PIXELS;
  b->odata[b->nobjects].width                   = t->rows;
  b->odata[b->nobjects].height                  = t->cols;

  /* Finally, increment the objects counter */

  b->nobjects++;

  return NULL;
}

/*************************************************/
/* object_discard()                              */
/*                                               */
/* Discards all Objects held by a given browser. */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             relevant to the Objects.          */
/*************************************************/

_kernel_oserror * object_discard(browser_data * b)
{
  b->nobjects = 0;

  return memory_set_chunk_size(b, NULL, CK_OBJB, 0);
}

/*************************************************/
/* object_get_token_object()                     */
/*                                               */
/* Return the number of the Object represented   */
/* in the given browser by the given token.      */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             relevant to the Objects;          */
/*                                               */
/*             Pointer to a token representing   */
/*             the Object (OBJECT, EMBED or      */
/*             APPLET tag).                      */
/*                                               */
/* Returns:    Number of the Object from 0 to    */
/*             number of Objects minus 1, or -1  */
/*             if the Object cannot be found.    */
/*************************************************/

static int object_get_token_object(browser_data * b, HStream * t)
{
  int found = -1;
  int i;

  /* If this isn't an Object token, do nothing (well, */
  /* complain about it in TRACE builds).              */

  if (!t || t->tagno != TAG_OBJECT)
  {
    #ifdef TRACE

      erb.errnum = Utils_Error_Custom_Normal;

      sprintf(erb.errmess,
              "Token %08x passed to object_new_object does not represent an OBJECT, APPLET or EMBED tag",
              (int) t);

      show_error_ret(&erb);

    #endif

    return -1;
  }

  /* Otherwise, try to find the item */

  for (i = 0; i < b->nobjects; i++)
  {
    if (b->odata[i].token == t)
    {
      found = i;
      break;
    }
  }

  return found;
}

/*************************************************/
/* object_get_object_size()                      */
/*                                               */
/* Returns the size of a given Object.           */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             to add to;                        */
/*                                               */
/*             Number of the Object, from 0 to   */
/*             number of Objects minus 1;        */
/*                                               */
/*             Pointer to a BBox, in which the   */
/*             size of the Object will be        */
/*             written.                          */
/*************************************************/

static _kernel_oserror * object_get_object_size(browser_data * b, int object, BBox * size)
{
  /* Can't do anything without a bounding box! */

  if (!size)
  {
    #ifdef TRACE

      erb.errnum = Utils_Error_Custom_Normal;
      strcpy(erb.errmess, "Null bounding box pointer passed to object_get_object_size");
      return &erb;

    #endif

    return NULL;
  }

  /* Fill in zeros to start with */

  size->xmin = size->ymin = 0;
  size->xmax = size->ymax = 0;

  /* Is this a valid object number? */

  if (object < 0 || object >= b->nobjects)
  {
    #ifdef TRACE

      erb.errnum = Utils_Error_Custom_Normal;

      sprintf(erb.errmess,
              "Invalid object number %d for passed to object_get_object_size (browser %08x, nobjects = %d)",
              object,
              (int) b,
              b->nobjects);

      return &erb;

    #endif

    return NULL;
  }

  /* Find its size */

  if (b->odata[object].units == UNITS_PIXELS)
  {
    size->xmax = b->odata[object].width  * 2; /* A 'web pixel' -> 2 OS units under RISC OS */
    size->ymax = b->odata[object].height * 2;
  }

  return NULL;
}

/*************************************************/
/* object_get_token_object_size()                */
/*                                               */
/* Returns the size of a given Object.           */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             to add to;                        */
/*                                               */
/*             Pointer to a token representing   */
/*             the Object (OBJECT, EMBED or      */
/*             APPLET tag);                      */
/*                                               */
/*             Pointer to a BBox, in which the   */
/*             size of the Object will be        */
/*             written.                          */
/*************************************************/

_kernel_oserror * object_get_token_object_size(browser_data * b, HStream * t, BBox * size)
{
  return object_get_object_size(b, object_get_token_object(b, t), size);
}

/*************************************************/
/* object_redraw()                               */
/*                                               */
/* Does a high level redraw of an Object, using  */
/* an outline to show where the Object should be */
/* if it isn't plotted by some other method.     */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             relevant to the Object;           */
/*                                               */
/*             Pointer to a RedrawWindowBlock    */
/*             struct which holds information    */
/*             about the current redraw session; */
/*                                               */
/*             Address of the token representing */
/*             the OBJECT, APPLET or EMBED tag;  */
/*                                               */
/*             The X offset in window coords (so */
/*             OS units) of the left hand edge   */
/*             of the Object;                    */
/*                                               */
/*             The Y offset in window coords (so */
/*             OS units) of the bottom edge of   */
/*             the Object.                       */
/*************************************************/

_kernel_oserror * object_redraw(browser_data * b, WimpRedrawWindowBlock * r, HStream * token, int x, int y)
{
  int object  = object_get_token_object(b, token);
  int plotted = 0;

  // Here would go some code to check if this is, say, an image,
  // and if so call the image redraw code instead. This would set
  // the 'plotted' flag as appropriate - may need to extend image
  // library API to say if it was about to draw a placeholder
  // itself, so that plotted stays unset (assuming it isn't possible
  // to easily generalise the image placeholder plotting).

  if (!plotted)
  {
    /* Plot a placeholder */

    BBox box;

    object_get_object_size(b, object, &box);

    box.xmin += x;
    box.ymin += y;
    box.xmax += x - box.xmin;
    box.ymax += y - box.ymin;

    box.xmin &= ~(wimpt_dx() - 1);
    box.ymin &= ~(wimpt_dy() - 1);
Printf("%d, %d\n",box.xmax,box.ymax);
    if (box.xmax > 8 && box.ymax > 8)
    {
      /* xmin, ymin hold the bottom left hand corner coordinates, whilst */
      /* xmax, ymax hold the width and height. The adjustments are to    */
      /* account for the way the bbc_rectanglefill function works; e.g., */
      /* to get a width of 4 OS units, ask for 3 (as it adds this to the */
      /* x coordinate and treats it as an inclusive x coordinate max).   */
      /* There are corrections to plot 2 OS units inside of the real     */
      /* bounding box (looks better when images touch each other) and to */
      /* get the darker sides of the 'slabbed in' box overlapping the    */
      /* lighter sides by the right amount.                              */

      redraw_set_colour(Redraw_Colour_AlmostWhite);
      bbc_rectanglefill(box.xmin + 2,
                        box.ymin + 2,
                        box.xmax - 5,
                        3);
      bbc_rectanglefill(box.xmax + box.xmin - 6,
                        box.ymin + 2,
                        3,
                        box.ymax - 5);

      redraw_set_colour(Redraw_Colour_MidGrey);
      bbc_rectanglefill(box.xmin + 2,
                        box.ymax + box.ymin - 6,
                        box.xmax - 7,
                        3);
      bbc_rectanglefill(box.xmin + 2,
                        box.ymin + 4,
                        3,
                        box.ymax - 7);
    }
    else
    {
      if (box.xmax < 2) box.xmax = 2;
      if (box.ymax < 2) box.ymax = 2;

      redraw_set_colour(0);
      bbc_rectangle(box.xmin,box.ymin,box.xmax - 1,box.ymax - 1);
    }
  }

  return NULL;
}