/* 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   : Encoding.c                             */
/*                                                 */
/* Purpose: Routines to handle the encoding menus. */
/*                                                 */
/* Author : K.J.Bracey                             */
/*                                                 */
/* History: 05-Sep-97: Created.                    */
/***************************************************/

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

#include "swis.h"

#include "wimp.h"
#include "toolbox.h"
#include "menu.h"
#include "event.h"

#include "Unicode/iso10646.h"

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

#include "Choices.h"
#include "Encoding.h"

/* Statics */

static ObjectId menu_id         = NULL_ObjectId;
static int      ticked_encoding = 0xffffffff;
static int      entries_faded;

static int encoding_get_encoding_item_r(ObjectId o, int encoding, ObjectId *encobjid, ComponentId *enccompid);

/*************************************************/
/* encoding_fade_unusable_entries()              */
/*                                               */
/* Scan the Encoding menu, fading out encodings  */
/* that aren't supported.                        */
/*                                               */
/* Parameters: The object ID of the Encoding     */
/*             menu.                             */
/*************************************************/

static int encoding_fade_unusable_entries(ObjectId o)
{
  /* How do we do this without an enumerate call? */

  return 0;
}

/*************************************************/
/* encoding_tick_entry()                         */
/*                                               */
/* Select or deselect the entry corresponding    */
/* to a specified encoding. Parent menu items    */
/* are also selected/deselected.                 */
/*                                               */
/* Parameters: The object ID of the Encoding     */
/*             menu;                             */
/*                                               */
/*             The number of the encoding;       */
/*                                               */
/*             0 to untick, 1 to tick.           */
/*                                               */
/* Returns:    1 if entry found, 0 if not.       */
/*                                               */
/* Assumes:    Parent menu items have            */
/*             consecutive component IDs         */
/*             starting at 0x11000 in each menu; */
/*                                               */
/*             The component ID of an encoding   */
/*             item is the number of the         */
/*             encoding it represents;           */
/*                                               */
/*             A given encoding is only          */
/*             represented once in the menu      */
/*             tree.                             */
/*************************************************/

static int encoding_tick_entry(ObjectId o, int enc, int state)
{
  ComponentId c;
  _kernel_oserror *e;

  e = menu_set_tick(0, o, enc, state);

  if (!e) return 1;

  /* The encoding isn't in this menu, so scan the submenus. */

  for (c = 0x11000; ; c++)
  {
    ObjectId sub;

    e = menu_get_sub_menu_show(0, o, c, &sub);

    /* If an error - component not found - end of submenus */

    if (e) return 0;

    if (sub == NULL_ObjectId) continue;

    if (encoding_tick_entry(sub, enc, state))
    {
      ChkError(menu_set_tick(0, o, c, state));
      return 1;
    }
  }
}

/*************************************************/
/* encoding_prepare_menu()                       */
/*                                               */
/* Ensure that the state of the Encoding menu    */
/* is correct for a supplied encoding_priority   */
/* and encoding type.                            */
/*                                               */
/* Parameters: Object ID of the Encoding menu;   */
/*                                               */
/*             The encoding priority             */
/*                                               */
/*             The encoding number               */
/*                                               */
/* Assumes:    The "From document" item is       */
/*             component 0x11FFF in the top-     */
/*             level menu;                       */
/*                                               */
/*             The Encoding menu is a shared     */
/*             object.                           */
/*************************************************/

void encoding_prepare_menu(ObjectId o, encoding_priority encoding_priority, int encoding)
{
  menu_set_tick(0, o, 0x11FFF, encoding_priority != priority_user);

  if (ticked_encoding != encoding)
  {
    if (ticked_encoding) encoding_tick_entry(o, ticked_encoding, 0);

    encoding_tick_entry(o, encoding, 1);
    ticked_encoding = encoding;
  }
}

/*************************************************/
/* encoding_select()                             */
/*                                               */
/* Event handler for the selection of an         */
/* encoding (EEncoding_Select).                  */
/*                                               */
/* Parameters are as for a standard Toolbox      */
/* event handler.                                */
/*                                               */
/* Assumes: The top-level Encoding menu is an    */
/*          ancestor object;                     */
/*                                               */
/*          The component ID is the number of    */
/*          the encoding to select.              */
/*************************************************/

int encoding_select(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  browser_data * b;
  _kernel_oserror *e;

  ChkError(toolbox_get_client_handle(0, idb->ancestor_id, (void *) &b));

  if (!is_known_browser(b))
  {
    ObjectId ancestor;

    e = toolbox_get_ancestor(0, menu_id, &ancestor, NULL);

    if (!e && ancestor == choices_windowid && new_choices)
    {
      new_choices->encoding = idb->self_component;
      choices_set_encoding_field();
      encoding_prepare_menu(menu_id, priority_default, new_choices->encoding);
      return 1;
    }
    else
    {
      return 0;
    }
  }
  else
  {
    b->encoding          = idb->self_component;
    b->encoding_priority = priority_user;

    encoding_prepare_menu(menu_id, b->encoding_priority, b->encoding);
  }

  return 1;
}

/*************************************************/
/* encoding_from_document_select()               */
/*                                               */
/* Handle the selection of the "From document"   */
/* menu entry.                                   */
/*                                               */
/* Parameters are as for a standard Toolbox      */
/* event handler.                                */
/*************************************************/

int encoding_from_document_select(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  browser_data * b;

  ChkError(toolbox_get_client_handle(0, idb->ancestor_id, (void *) &b));

  if (!is_known_browser(b)) return 0;

  if (b->encoding_priority == priority_user)
  {
    b->encoding_priority = priority_default;
  }
  else
  {
    b->encoding_priority = priority_user;
  }

  encoding_prepare_menu(menu_id, b->encoding_priority, b->encoding);

  return 1;
}

/*************************************************/
/* encoding_show_menu()                          */
/*                                               */
/* Set up the Encoding menu before it is shown.  */
/*                                               */
/* Parameters are as for a standard Toolbox      */
/* event handler.                                */
/*                                               */
/* Assumes:    The "From document" item is       */
/*             component 0x11FFF in the top-     */
/*             level menu;                       */
/*************************************************/

int encoding_show_menu(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  browser_data * b;

  ChkError(toolbox_get_client_handle(0, idb->ancestor_id, (void *) &b));

  if (!is_known_browser(b))
  {
    if (idb->ancestor_id == choices_windowid && new_choices)
    {
      /* Fade "From document" item as it is not relevant to choices */

      menu_set_fade(0, idb->self_id, 0x11FFF, 1);
      encoding_prepare_menu(menu_id, priority_default, new_choices->encoding);

      toolbox_set_client_handle(0, idb->self_id, NULL);

      return 1;
    }
    else
    {
      return 0;
    }
  }
  else
  {
    /* Unfade "From document" item as it is relevant to browser windows */

    menu_set_fade(0, idb->self_id, 0x11FFF, 0);

    if (!entries_faded)
    {
      encoding_fade_unusable_entries(idb->self_id);
      entries_faded = 1;
    }

    toolbox_set_client_handle(0, idb->self_id, b);

    encoding_prepare_menu(menu_id, b->encoding_priority, b->encoding);
  }

  return 1;
}

/*************************************************/
/* encoding_changed_by_meta()                    */
/*                                               */
/* Called by HTMLLib when a META tag that        */
/* changes encoding is found. Update the         */
/* browser_data struct and the menu accordingly. */
/*                                               */
/* This routine is registered with HTMLLib in    */
/* html_get_next_token() when the parse starts.  */
/*                                               */
/* Parameters: The number of the new encoding;   */
/*                                               */
/*             Pointer to the relevant           */
/*             browser_data struct.              */
/*************************************************/

void encoding_changed_by_meta(int encoding, void * handle)
{
  browser_data * b = (browser_data *) handle;

  b->encoding          = encoding;
  b->encoding_priority = priority_meta;

  encoding_update_menus(b);
}

/*************************************************/
/* encoding_update_menus()                       */
/*                                               */
/* Update the Encoding menu if it is currently   */
/* open for the specified browser.               */
/*                                               */
/* Parameters: Pointer to the browser_data       */
/*             whose encoding has changed.       */
/*************************************************/

void encoding_update_menus(browser_data * b)
{
  if (menu_id != NULL_ObjectId)
  {
    browser_data * b2;

    if (toolbox_get_client_handle(0, menu_id, (void *) &b2)) return;

    if (b2 == b) encoding_prepare_menu(menu_id, b->encoding_priority, b->encoding);
  }
}

/*************************************************/
/* encoding_get_encoding_item()                  */
/*                                               */
/* Finds the menu item containing the name       */
/* of the specified encoding.                    */
/*                                               */
/* Parameters: the required encoding             */
/*                                               */
/*             Pointer to the objectid to return */
/*             information in                    */
/*                                               */
/*             Pointer to the componentid to     */
/*             return the information in.        */
/*                                               */
/* Returns:    1 if the encoding was found       */
/*             0 if it was not                   */
/*                                               */
/* Assumes:    Parent menu items have            */
/*             consecutive component IDs         */
/*             starting at 0x11000 in each menu; */
/*                                               */
/*             The component ID of an encoding   */
/*             item is the number of the         */
/*             encoding it represents;           */
/*************************************************/

int encoding_get_encoding_item(int encoding, ObjectId * encobjid, ComponentId * enccompid)
{
  return encoding_get_encoding_item_r(menu_id, encoding, encobjid, enccompid);
}

/*************************************************/
/* encoding_get_encoding_item_r()                */
/*                                               */
/* Recursive backend to                          */
/* encoding_get_encoding_item                    */
/*************************************************/

static int encoding_get_encoding_item_r(ObjectId o, int encoding, ObjectId * encobjid, ComponentId * enccompid)
{
  _kernel_oserror *e;
  ComponentId c;
  int state;

  /* We don't really want to know if this item is ticked, we just want to see if the item exists */

  e = menu_get_tick(0, o, encoding, &state);

  if (!e)
  {
    *encobjid  = o;
    *enccompid = encoding;
    return 1;
  }

  for (c = 0x11000; ; c++)
  {
    ObjectId sub;

    e = menu_get_sub_menu_show(0, o, c, &sub);

    /* If an error - component not found - end of submenus */

    if (e) return 0;

    if (sub == NULL_ObjectId) continue;

    if (encoding_get_encoding_item_r(sub, encoding, encobjid, enccompid)) return 1;
  }
}

/*************************************************/
/* encoding_init()                               */
/*                                               */
/* Initialises the encoding system - basically,  */
/* creates the menu now (rather than letting it  */
/* autocreate later - it's a shared object) so   */
/* that the Object ID is known.                  */
/*************************************************/

_kernel_oserror *encoding_init(void)
{
  /* Encoding menu may not be there, so this should be able to */
  /* fail silently.                                            */

  toolbox_create_object(0, "Encoding", &menu_id);

  return NULL;
}

/*************************************************/
/* encoding_get_menuid(void)                     */
/*                                               */
/* Returns the ObjectId of the main encoding     */
/* menu.                                         */
/*************************************************/

ObjectId encoding_get_menuid(void)
{
  return menu_id;
}