/*
 * Copyright (c) 1997-2006  Motoyuki Kasahara
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include "build-pre.h"
#include "eb.h"
#include "error.h"
#include "appendix.h"
#include "build-post.h"

/*
 * Unexported functions.
 */
static EB_Error_Code eb_narrow_character_text_jis(EB_Appendix *appendix,
  int character_number, char *text);
static EB_Error_Code eb_narrow_character_text_latin(EB_Appendix *appendix,
  int character_number, char *text);

/*
 * Hash macro for cache data.
 */
#define EB_HASH_ALT_CACHE(c)    ((c) & 0x0f)


/*
 * Examine whether the current subbook in `book' has a narrow font
 * alternation or not.
 */
int
eb_have_narrow_alt(EB_Appendix *appendix)
{
  eb_lock(&appendix->lock);
  LOG(("in: eb_have_narrow_alt(appendix=%d)", (int)appendix->code));

  /*
   * Current subbook must have been set.
   */
  if (appendix->subbook_current == NULL)
    goto failed;

  if (appendix->subbook_current->narrow_page == 0)
    goto failed;

  LOG(("out: eb_have_narrow_alt() = %d", 1));
  eb_unlock(&appendix->lock);

  return 1;

  /*
   * An error occurs...
   */
  failed:
  LOG(("out: eb_have_narrow_alt() = %d", 0));
  eb_unlock(&appendix->lock);
  return 0;
}


/*
 * Look up the character number of the start of the narrow font alternation
 * of the current subbook in `book'.
 */
EB_Error_Code
eb_narrow_alt_start(EB_Appendix *appendix, int *start)
{
  EB_Error_Code error_code;

  eb_lock(&appendix->lock);
  LOG(("in: eb_narrow_alt_start(appendix=%d)", (int)appendix->code));

  /*
   * Current subbook must have been set.
   */
  if (appendix->subbook_current == NULL) {
    error_code = EB_ERR_NO_CUR_APPSUB;
    goto failed;
  }

  if (appendix->subbook_current->narrow_page == 0) {
    error_code = EB_ERR_NO_ALT;
    goto failed;
  }

  *start = appendix->subbook_current->narrow_start;

  LOG(("out: eb_narrow_alt_start(start=%d) = %s", *start,
    eb_error_string(EB_SUCCESS)));
  eb_unlock(&appendix->lock);

  return EB_SUCCESS;

  /*
   * An error occurs...
   */
  failed:
  *start = -1;
  LOG(("out: eb_narrow_alt_start() = %s", eb_error_string(error_code)));
  eb_unlock(&appendix->lock);
  return error_code;
}


/*
 * Return the character number of the end of the narrow font alternation
 * of the current subbook in `book'.
 */
EB_Error_Code
eb_narrow_alt_end(EB_Appendix *appendix, int *end)
{
  EB_Error_Code error_code;

  eb_lock(&appendix->lock);
  LOG(("in: eb_narrow_alt_end(appendix=%d)", (int)appendix->code));

  /*
   * Current subbook must have been set.
   */
  if (appendix->subbook_current == NULL) {
    error_code = EB_ERR_NO_CUR_APPSUB;
    goto failed;
  }

  if (appendix->subbook_current->narrow_page == 0) {
    error_code = EB_ERR_NO_ALT;
    goto failed;
  }

  *end = appendix->subbook_current->narrow_end;

  LOG(("out: eb_narrow_alt_end(end=%d) = %s", *end,
    eb_error_string(EB_SUCCESS)));
  eb_unlock(&appendix->lock);

  return EB_SUCCESS;

  /*
   * An error occurs...
   */
  failed:
  *end = -1;
  LOG(("out: eb_narrow_alt_end() = %s", eb_error_string(error_code)));
  eb_unlock(&appendix->lock);
  return error_code;
}


/*
 * Get the alternation text of the character number `character_number'.
 */
EB_Error_Code
eb_narrow_alt_character_text(EB_Appendix *appendix, int character_number,
  char *text)
{
  EB_Error_Code error_code;

  eb_lock(&appendix->lock);
  LOG(("in: eb_narrow_alt_character_text(appendix=%d, character_number=%d)",
    (int)appendix->code, character_number));

  /*
   * Current subbook must have been set.
   */
  if (appendix->subbook_current == NULL) {
    error_code = EB_ERR_NO_CUR_APPSUB;
    goto failed;
  }

  /*
   * The narrow font must exist in the current subbook.
   */
  if (appendix->subbook_current->narrow_page == 0) {
    error_code = EB_ERR_NO_ALT;
    goto failed;
  }

  if (appendix->subbook_current->character_code == EB_CHARCODE_ISO8859_1) {
    error_code = eb_narrow_character_text_latin(appendix,
      character_number, text);
  } else {
    error_code = eb_narrow_character_text_jis(appendix, character_number,
      text);
  }
  if (error_code != EB_SUCCESS)
    goto failed;

  LOG(("out: eb_narrow_alt_character_text(text=%s) = %s",
    eb_quoted_string(text), eb_error_string(EB_SUCCESS)));
  eb_unlock(&appendix->lock);

  return EB_SUCCESS;

  /*
   * An error occurs...
   */
  failed:
  *text = '\0';
  LOG(("out: eb_narrow_alt_character_text() = %s",
    eb_error_string(error_code)));
  eb_unlock(&appendix->lock);
  return error_code;
}


/*
 * Get the alternation text of the character number `character_number'.
 */
static EB_Error_Code
eb_narrow_character_text_jis(EB_Appendix *appendix, int character_number,
  char *text)
{
  EB_Error_Code error_code;
  int start;
  int end;
  off_t location;
  EB_Alternation_Cache *cachep;

  LOG(("in: eb_narrow_alt_character_text_jis(appendix=%d, \
character_number=%d)",
    (int)appendix->code, character_number));

  start = appendix->subbook_current->narrow_start;
  end = appendix->subbook_current->narrow_end;

  /*
   * Check for `character_number'.  Is it in a font?
   * This test works correctly even when the font doesn't exist in
   * the current subbook because `start' and `end' have set to -1
   * in the case.
   */
  if (character_number < start
    || end < character_number
    || (character_number & 0xff) < 0x21
    || 0x7e < (character_number & 0xff)) {
    error_code = EB_ERR_NO_SUCH_CHAR_TEXT;
    goto failed;
  }

  /*
   * Calculate the location of alternation data.
   */
  location
    = (appendix->subbook_current->narrow_page - 1) * EB_SIZE_PAGE
    + (((character_number >> 8) - (start >> 8)) * 0x5e
      + (character_number & 0xff) - (start & 0xff))
    * (EB_MAX_ALTERNATION_TEXT_LENGTH + 1);

  /*
   * Check for the cache data.
   */
  cachep = appendix->narrow_cache + EB_HASH_ALT_CACHE(character_number);
  if (cachep->character_number == character_number) {
    memcpy(text, cachep->text, EB_MAX_ALTERNATION_TEXT_LENGTH + 1);
    goto succeeded;
  }

  /*
   * Read the alternation data.
   */
  if (zio_lseek(&appendix->subbook_current->zio, location, SEEK_SET) < 0) {
    error_code = EB_ERR_FAIL_SEEK_APP;
    goto failed;
  }
  cachep->character_number = -1;
  if (zio_read(&appendix->subbook_current->zio, cachep->text,
    EB_MAX_ALTERNATION_TEXT_LENGTH + 1)
    != EB_MAX_ALTERNATION_TEXT_LENGTH + 1) {
    error_code = EB_ERR_FAIL_READ_APP;
    goto failed;
  }

  /*
   * Update cache data.
   */
  memcpy(text, cachep->text, EB_MAX_ALTERNATION_TEXT_LENGTH + 1);
  cachep->text[EB_MAX_ALTERNATION_TEXT_LENGTH] = '\0';
  cachep->character_number = character_number;

  succeeded:
  LOG(("out: eb_narrow_alt_character_text_jis(text=%s) = %s",
    eb_quoted_string(text), eb_error_string(EB_SUCCESS)));
  return EB_SUCCESS;

  /*
   * An error occurs...
   */
  failed:
  *text = '\0';
  LOG(("out: eb_narrow_alt_character_text_jis() = %s",
    eb_error_string(error_code)));
  return error_code;
}


/*
 * Get the alternation text of the character number `character_number'.
 */
static EB_Error_Code
eb_narrow_character_text_latin(EB_Appendix *appendix, int character_number,
  char *text)
{
  EB_Error_Code error_code;
  int start;
  int end;
  off_t location;
  EB_Alternation_Cache *cache_p;

  LOG(("in: eb_narrow_alt_character_text_latin(appendix=%d, \
character_number=%d)",
    (int)appendix->code, character_number));

  start = appendix->subbook_current->narrow_start;
  end = appendix->subbook_current->narrow_end;

  /*
   * Check for `character_number'.  Is it in a font?
   * This test works correctly even when the font doesn't exist in
   * the current subbook because `start' and `end' have set to -1
   * in the case.
   */
  if (character_number < start
    || end < character_number
    || (character_number & 0xff) < 0x01
    || 0xfe < (character_number & 0xff)) {
    error_code = EB_ERR_NO_SUCH_CHAR_TEXT;
    goto failed;
  }

  /*
   * Calculate the location of alternation data.
   */
  location
    = (appendix->subbook_current->narrow_page - 1) * EB_SIZE_PAGE
    + (((character_number >> 8) - (start >> 8)) * 0xfe
      + (character_number & 0xff) - (start & 0xff))
    * (EB_MAX_ALTERNATION_TEXT_LENGTH + 1);

  /*
   * Check for the cache data.
   */
  cache_p = appendix->narrow_cache + EB_HASH_ALT_CACHE(character_number);
  if (cache_p->character_number == character_number) {
    memcpy(text, cache_p->text, EB_MAX_ALTERNATION_TEXT_LENGTH + 1);
    goto succeeded;
  }

  /*
   * Read the alternation data.
   */
  if (zio_lseek(&appendix->subbook_current->zio, location, SEEK_SET) < 0) {
    error_code = EB_ERR_FAIL_SEEK_APP;
    goto failed;
  }
  cache_p->character_number = -1;
  if (zio_read(&appendix->subbook_current->zio, cache_p->text,
    EB_MAX_ALTERNATION_TEXT_LENGTH + 1)
    != EB_MAX_ALTERNATION_TEXT_LENGTH + 1) {
    error_code = EB_ERR_FAIL_READ_APP;
    goto failed;
  }

  /*
   * Update cache data.
   */
  memcpy(text, cache_p->text, EB_MAX_ALTERNATION_TEXT_LENGTH + 1);
  cache_p->text[EB_MAX_ALTERNATION_TEXT_LENGTH] = '\0';
  cache_p->character_number = character_number;

  succeeded:
  LOG(("out: eb_narrow_alt_character_text_latin(text=%s) = %s",
    eb_quoted_string(text), eb_error_string(EB_SUCCESS)));
  return EB_SUCCESS;

  /*
   * An error occurs...
   */
  failed:
  *text = '\0';
  LOG(("out: eb_narrow_alt_character_text_latin() = %s",
    eb_error_string(error_code)));
  return error_code;
}


/*
 * Return next `n'th character number from `*character_number'.
 */
EB_Error_Code
eb_forward_narrow_alt_character(EB_Appendix *appendix, int n,
  int *character_number)
{
  EB_Error_Code error_code;
  int start;
  int end;
  int i;

  if (n < 0) {
    return eb_backward_narrow_alt_character(appendix, -n,
      character_number);
  }

  eb_lock(&appendix->lock);
  LOG(("in: eb_forward_narrow_alt_character(appendix=%d, n=%d, \
character_number=%d)",
    (int)appendix->code, n, *character_number));

  /*
   * Current subbook must have been set.
   */
  if (appendix->subbook_current == NULL) {
    error_code = EB_ERR_NO_CUR_APPSUB;
    goto failed;
  }

  /*
   * The narrow font must exist in the current subbook.
   */
  if (appendix->subbook_current->narrow_page == 0) {
    error_code = EB_ERR_NO_ALT;
    goto failed;
  }

  start = appendix->subbook_current->narrow_start;
  end = appendix->subbook_current->narrow_end;

  if (appendix->subbook_current->character_code == EB_CHARCODE_ISO8859_1) {
    /*
     * Check for `*character_number'. (ISO 8859 1)
     */
    if (*character_number < start
      || end < *character_number
      || (*character_number & 0xff) < 0x01
      || 0xfe < (*character_number & 0xff)) {
      error_code = EB_ERR_NO_SUCH_CHAR_TEXT;
      goto failed;
    }

    /*
     * Get character number. (ISO 8859 1)
     */
    for (i = 0; i < n; i++) {
      if (0xfe <= (*character_number & 0xff))
        *character_number += 3;
      else
        *character_number += 1;
      if (end < *character_number) {
        error_code = EB_ERR_NO_SUCH_CHAR_TEXT;
        goto failed;
      }
    }
  } else {
    /*
     * Check for `*character_number'. (JIS X 0208)
     */
    if (*character_number < start
      || end < *character_number
      || (*character_number & 0xff) < 0x21
      || 0x7e < (*character_number & 0xff)) {
      error_code = EB_ERR_NO_SUCH_CHAR_TEXT;
      goto failed;
    }

    /*
     * Get character number. (JIS X 0208)
     */
    for (i = 0; i < n; i++) {
      if (0x7e <= (*character_number & 0xff))
        *character_number += 0xa3;
      else
        *character_number += 1;
      if (end < *character_number) {
        error_code = EB_ERR_NO_SUCH_CHAR_TEXT;
        goto failed;
      }
    }
  }

  LOG(("out: eb_forkward_narrow_alt_character(character_number=%d) = %s",
    *character_number, eb_error_string(EB_SUCCESS)));
  eb_unlock(&appendix->lock);

  return EB_SUCCESS;

  /*
   * An error occurs...
   */
  failed:
  *character_number = -1;
  LOG(("out: eb_forward_narrow_alt_character() = %s",
    eb_error_string(error_code)));
  eb_unlock(&appendix->lock);
  return error_code;
}


/*
 * Return previous `n'th character number from `*character_number'.
 */
EB_Error_Code
eb_backward_narrow_alt_character(EB_Appendix *appendix, int n,
  int *character_number)
{
  EB_Error_Code error_code;
  int start;
  int end;
  int i;

  if (n < 0) {
    return eb_forward_narrow_alt_character(appendix, -n, character_number);
  }

  eb_lock(&appendix->lock);
  LOG(("in: eb_backward_narrow_alt_character(appendix=%d, n=%d, \
character_number=%d)",
    (int)appendix->code, n, *character_number));

  /*
   * Current subbook must have been set.
   */
  if (appendix->subbook_current == NULL) {
    error_code = EB_ERR_NO_CUR_APPSUB;
    goto failed;
  }

  /*
   * The narrow font must exist in the current subbook.
   */
  if (appendix->subbook_current->narrow_page == 0) {
    error_code = EB_ERR_NO_ALT;
    goto failed;
  }

  start = appendix->subbook_current->narrow_start;
  end = appendix->subbook_current->narrow_end;

  if (appendix->subbook_current->character_code == EB_CHARCODE_ISO8859_1) {
    /*
     * Check for `*character_number'. (ISO 8859 1)
     */
    if (*character_number < start
      || end < *character_number
      || (*character_number & 0xff) < 0x01
      || 0xfe < (*character_number & 0xff)) {
      error_code = EB_ERR_NO_SUCH_CHAR_TEXT;
      goto failed;
    }

    /*
     * Get character number. (ISO 8859 1)
     */
    for (i = 0; i < n; i++) {
      if ((*character_number & 0xff) <= 0x01)
        *character_number -= 3;
      else
        *character_number -= 1;
      if (*character_number < start) {
        error_code = EB_ERR_NO_SUCH_CHAR_TEXT;
        goto failed;
      }
    }
  } else {
    /*
     * Check for `*character_number'. (JIS X 0208)
     */
    if (*character_number < start
      || end < *character_number
      || (*character_number & 0xff) < 0x21
      || 0x7e < (*character_number & 0xff)) {
      error_code = EB_ERR_NO_SUCH_CHAR_TEXT;
      goto failed;
    }

    /*
     * Get character number. (JIS X 0208)
     */
    for (i = 0; i < n; i++) {
      if ((*character_number & 0xff) <= 0x21)
        *character_number -= 0xa3;
      else
        *character_number -= 1;
      if (*character_number < start) {
        error_code = EB_ERR_NO_SUCH_CHAR_TEXT;
        goto failed;
      }
    }
  }

  LOG(("out: eb_backward_narrow_alt_character(character_number=%d) = %s",
    *character_number, eb_error_string(EB_SUCCESS)));
  eb_unlock(&appendix->lock);

  return EB_SUCCESS;

  /*
   * An error occurs...
   */
  failed:
  *character_number = -1;
  LOG(("out: eb_backward_narrow_alt_character() = %s",
    eb_error_string(error_code)));
  eb_unlock(&appendix->lock);
  return error_code;
}


