/* 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   : About.c                                */
/*                                                 */
/* Purpose: Compile an HTML 'About' page which     */
/*          describes available Plug-Ins.          */
/*                                                 */
/* Author : Piers Wombwell                         */
/*          This source adapted by A.D.Hodgkinson  */
/*                                                 */
/* History: 30-Jan-98: Adaptation complete (ADH)   */
/***************************************************/

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

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

#include "flex.h"

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

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

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

/* Local statics and definitions */

#define FLAGS_IMAGE (1u<<0)

static struct _abouts
{
  char           * dirname;

  unsigned int     count;
  unsigned int     flags;
  unsigned int     width;
  unsigned int     height;

  struct _abouts * next;

} * abouts = NULL;

/* Static function prototypes */

static void   about_free_abouts    (void);
static char * about_html_fname     (const char * dirname, unsigned int count, unsigned int width, unsigned int height);
static int    about_copy_info_file (char ** output, int output_size, const char * dirname, unsigned int count);

/*************************************************/
/* about_free_abouts()                           */
/*                                               */
/* Free all of the temporary structures used to  */
/* compile the About page.                       */
/*************************************************/

static void about_free_abouts(void)
{
  struct _abouts * next;

  if (abouts)
  {
    while (abouts)
    {
      next = abouts->next;

      free(abouts->dirname);
      free(abouts);

      abouts = next;
    }
  }
}

/*************************************************/
/* about_html_fname()                            */
/*                                               */
/* Creates a URL to point to an About item logo. */
/*                                               */
/* Parameters: Directory to look in;             */
/*                                               */
/*             Number of the file;               */
/*                                               */
/*             Item width;                       */
/*                                               */
/*             Item height (the last two form    */
/*             part of the filename).            */
/*                                               */
/* Returns:    Pointer to the URL in a *global*  */
/*             malloc block - if you need a      */
/*             local copy before calling the     */
/*             function again, you must take it  */
/*             there and then.                   */
/*************************************************/

static char * about_html_fname(const char * dirname, unsigned int count,
                               unsigned int width, unsigned int height)
{
  static char * fname = NULL;
  char        * s;
  int           len;

  if (fname) free(fname);
  fname = NULL;

  if (width == 0 && height == 0)
  {
    len = strlen(dirname) + sizeof("file:/.00");

    fname = malloc(len);
    if (!fname) return "";

    sprintf(fname, "file:/%s.%02d", dirname, count);
  }
  else
  {
    len = strlen(dirname) + sizeof("file:/.0000000000");

    fname = malloc(len);
    if (!fname) return "";

    sprintf(fname, "file:/%s.%02d%04d%04d", dirname, count, width, height);
  }

  if (fname && *fname)
  {
    for (s = fname + sizeof("file:/"); *s != '\0'; s++)
    {
      if (*s == '.')
      *s = '/';
    }
  }

  return fname;
}

/*************************************************/
/* about_copy_info_file                          */
/*                                               */
/* Copy the 'About' info file into the given     */
/* flex block.                                   */
/*                                               */
/* Paramters: Pointer to the flex block;         */
/*                                               */
/*            Size of the block so far;          */
/*                                               */
/*            Name of the directory to look in;  */
/*                                               */
/*            Number of the 'About' file to      */
/*            read (will cope with just 'About'  */
/*            rather than 'AboutXX').            */
/*                                               */
/* Returns:   Size of block after addition of    */
/*            the file.                          */
/*************************************************/

static int about_copy_info_file(char ** output, int output_size,
                                const char * dirname, unsigned int count)
{
  FILE * input;
  char   fname[Limits_OS_Pathname];
  int    length;
  int    old_state;

  /* Write 'AboutXX' as a leafname if it'll fit */

  if (strlen(dirname) + sizeof(".About00") <= sizeof(fname))
  {
    sprintf(fname, "%s.About%02d", dirname, count);
  }
  else return output_size;

  /* Try to open it */

  input = fopen(fname, "r");

  /* If this fails, write just 'About' (we know it'll fit */
  /* if 'AboutXX' did!)                                   */

  if (input == NULL && count == 0)
  {
    sprintf(fname, "%s.About", dirname);
    input = fopen(fname, "r");
  }

  /* If this fails, bail out */

  if (input == NULL) return output_size;

  fseek(input, 0L, SEEK_END);
  length = (int) ftell(input);
  rewind(input);

  /* Try to get the amount of memory the file requires */

  if (flex_extend((void **) output, output_size + length + 1) != 1) /* + 1 for the terminator */
  {
    fclose(input);
    return output_size;
  }

  /* Read the file, zero terminate it in the block, and close the file */

  old_state = flex_set_budge(0);

  fread(*output + output_size, length, 1, input);
  *(*output + output_size + length) = '\0';

  flex_set_budge(old_state);

  fclose(input);

  /* Return the original size plus the added data */

  return output_size + length;
}

/*************************************************/
/* about_build_page()                            */
/*                                               */
/* Builds an 'About' page enumerating Plug-Ins   */
/* to a given flex anchor.                       */
/*                                               */
/* There may already be data in the flex block,  */
/* or not. If the function bails out due to lack */
/* of memory or some other error, the flex block */
/* won't be freed and may have been added to.    */
/*                                               */
/* Parameters: The flex anchor (as a void **).   */
/*************************************************/

_kernel_oserror * about_build_page(void ** block)
{
  _kernel_swi_regs   regs; /* OS_ReadVarVal doesn't work with _swix */

  struct _abouts   * about;

  int                plugin_count = -1;
  int                max_width    = 0;
  int                output_size  = 0;
  int                len;
  int                start;

  if (!block) return NULL;

  regs.r[3] = 0; /* Count */

  do
  {
    plugin_count++;

    regs.r[0] = (int) "Plugin$About_*";

    regs.r[1] = NULL; /* No buffer                  */
    regs.r[2] = -1;   /* Verify whether it exists   */
    regs.r[4] = 0;    /* Returned unexpanded string */

    _kernel_swi(OS_ReadVarVal, &regs, &regs);

    if (regs.r[2] != 0)
    {
      /* Got directory name - let's enumerate About files... */

      char leafname[Limits_OS_Pathname];
      int  nobjs, index = 0;

      while (index != -1)
      {
        /* Read full info on the one 'About[XX]' file in */
        /* the directory.                                */

        if (
             _swix(OS_GBPB,
                   _INR(0,6) | _OUTR(3,4),

                   12,
                   getenv((const char*) regs.r[3]),
                   leafname,
                   1,
                   index,
                   sizeof(leafname),
                   "About*",

                   &nobjs, /* No. objects read so far */
                   &index) /* Where to continue       */
           )
           break;

        /* We may have nothing left to read (index == -1) */
        /* or have read no object this time               */

        if (index == -1) break;
        if (nobjs == 0)  continue;

        /* Build an 'about' structure for the entry */

        about = malloc(sizeof(struct _abouts));

        if (!about)
        {
          about_free_abouts();

          return make_no_cont_memory_error(12);
        }

        about->dirname = malloc(~regs.r[2] + 1);

        if (!about->dirname)
        {
          about_free_abouts();

          return make_no_cont_memory_error(12);
        }

        /* Read the expanded path into the buffer */

        _kernel_getenv((const char *) regs.r[3],
                       about->dirname,
                       (unsigned int) ~regs.r[2] + 1);

        /* We may have an 'About' file or an 'AboutXX' file */

        if (!strcmp(leafname + 24, "About")) about->count = 0;
        else                                 about->count = atoi(leafname + 24 + sizeof("About"));

        about->flags = 0;
        about->next  = abouts;
        abouts       = about;
      }
    }
  }
  while (regs.r[2] != 0);

  /* Now we've got all the 'About' files, we can look for the logos */

  for (about = abouts; about != NULL; about = about->next)
  {
    char leafname[Limits_OS_Pathname];
    int  nobjs, index = 0;

    while (index != -1)
    {
      sprintf(leafname, "%02d*", about->count);

      /* Similar to the above code, only the files are */
      /* identified as 'XXWWWWHHHH' (Ws = width, Hs =  */
      /* height).                                      */

      if (
           _swix(OS_GBPB,
                 _INR(0,6) | _OUTR(3,4),
                 12,

                 about->dirname,
                 leafname,
                 1,
                 index,
                 sizeof(leafname),
                 leafname,

                 &nobjs,
                 &index)
         )
         break;

      if (index == -1) break;
      if (nobjs == 0)  continue;

      /* Check if leafname is just two digits long, and therefore */
      /* has no width and height                                  */

      if (strlen(leafname + 24) == 2)
      {
        about->width = about->height = 0;
        about->flags = FLAGS_IMAGE;

        break;
      }

      /* Break up leafname into width & height */

      strncpy(leafname, leafname + 24 + 2, 4);
      leafname[5] = '\0';
      about->width = atoi(leafname);

      strncpy(leafname, leafname + 24 + 6, 4);
      leafname[5] = '\0';
      about->height = atoi(leafname);

      if (about->width > max_width)
      max_width = about->width;

      /* We now have the sprite widths. Mark About object as good */

      about->flags = FLAGS_IMAGE;
    }
  }

  /* Build the HTML - first the header */

  output_size = strlen(lookup_token("pabouthdr",1,0)) + 1;

  {
    int success;

    if (!*block) start   = 0,
                 success = flex_alloc (block, output_size);

    else         start   = flex_size  (block),
                 success = flex_extend(block, start + output_size);

    if (!success) goto panic;

    sprintf(((char *) *block) + start, tokens);

    output_size += start - 1; /* (Next item should overwrite the terminator) */
  }

  /* Next the browser's own entry. We need to substitute one a Messages */
  /* file entry into another Messages file entry, so some juggling      */
  /* about of buffers is needed - we'll store the string to substitute  */
  /* at block + output_size, compile the final string above this, then  */
  /* move the final string down into place.                             */

  len  = strlen(lookup_token("paboutbrw",1,0)) + 1;
  len += strlen(lookup_token("Version",  1,0)) * 2 + 1;

  if (!flex_extend(block, output_size + len)) goto panic;

  sprintf(((char *) *block) + output_size, tokens);

  len = strlen(tokens) + 1;
  sprintf(((char *) *block) + output_size + len,
          lookup_token("paboutbrw",1,0),
          ((char *) *block) + output_size);

  memmove(((char *) *block) + output_size,
          ((char *) *block) + output_size + len,
          strlen(((char *) *block) + output_size + len) + 1);

  output_size = start + strlen(((char *) *block) + start);

  /* Now the individual items */

  for (about = abouts; about != NULL; about = about->next)
  {
    if (!(about->flags & FLAGS_IMAGE))
    {
      len = strlen(lookup_token("paboutpl1",1,0)) + 1;

      if (!flex_extend(block, output_size + len)) goto panic;

      sprintf(((char *) *block) + output_size,
              tokens);

      output_size = start + strlen(((char *) *block) + start);
    }
    else if (about->width == 0 && about->height == 0)
    {
      len = strlen(about_html_fname(about->dirname, about->count, 0, 0)) +
            strlen(lookup_token("paboutpl2",1,0)) + 1;

      if (!flex_extend(block, output_size + len)) goto panic;

      sprintf(((char *) *block) + output_size,
              tokens,
              about_html_fname(about->dirname, about->count, 0, 0));

      output_size = start + strlen(((char *) *block) + start);
    }
    else
    {
      len = strlen(about_html_fname(about->dirname,
                                    about->count,
                                    about->width,
                                    about->height)) +
            4                                       + /* Width and height are 4 characters or less as numbers, */
            4                                       + /* due to the way the filenames encode them.             */
            strlen(lookup_token("paboutpl3",1,0)) + 1;

      if (!flex_extend(block, output_size + len)) goto panic;

      sprintf(((char *) *block) + output_size,
              tokens,
              about_html_fname(about->dirname,
                               about->count,
                               about->width,
                               about->height),
              about->width,
              about->height);

      output_size = start + strlen(((char *) *block) + start);
    }

    /* Copy in HTML About file */

    output_size = about_copy_info_file((char **) block,
                                       output_size,
                                       about->dirname,
                                       about->count);

    /* Write the item footer */

    len = strlen(lookup_token("paboutift",1,0)) + 1;

    if (!flex_extend(block, output_size + len)) goto panic;

    sprintf(((char *) *block) + output_size, tokens);

    output_size = start + strlen(((char *) *block) + start);
  }

  /* Write the page footer; then we're finished */

  len = strlen(lookup_token("paboutpft",1,0)) + 1;

  if (!flex_extend(block, output_size + len)) goto panic;

  sprintf(((char *) *block) + output_size, tokens);

  output_size = start + strlen(((char *) *block) + start);

  /* Ensure the block is clipped to exactly the right size (we're */
  /* building HTML, so it shouldn't have a zero terminator!)      */

  if (!flex_extend(block, output_size)) goto panic;

  /* Clean up and exit without error */

  about_free_abouts();

  return NULL;

panic:

  /* Clean up and exit with an error */

  about_free_abouts();

  /* This was *almost* certainly the error...! */

  return make_no_cont_memory_error(12);
}