#include "stdafx.h"
/*
 * This code is derivative of guess.c of Gauche-0.8.3.
 * The following is the original copyright notice.
 */

/*
 * guess.c - guessing character encoding
 *
 *   Copyright (c) 2000-2003 <NAME>, All rights reserved.
 *
 *   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 authors 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 COPYRIGHT HOLDERS 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 COPYRIGHT
 *   OWNER 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 "ed.h"
# include "byte-stream.h"
# include "guess.h"

/* data types */
typedef struct guess_arc_rec
{
    unsigned int next;          /* next state */
    double score;               /* score */
} guess_arc;

typedef struct guess_dfa_rec
{
    signed char (*states)[256];
    guess_arc *arcs;
    int state;
    double score;
} guess_dfa;

/* macros */
#define DFA_INIT(st, ar) \
    { st, ar, 0, 1.0 }

#define DFA_NEXT(dfa, ch)                               \
    do {                                                \
        int arc__;                                      \
        if (dfa.state >= 0) {                           \
            arc__ = dfa.states[dfa.state][ch];          \
            if (arc__ < 0) {                            \
                dfa.state = -1;                         \
            } else {                                    \
                dfa.state = dfa.arcs[arc__].next;       \
                dfa.score *= dfa.arcs[arc__].score;     \
            }                                           \
        }                                               \
    } while (0)

#define DFA_ALIVE(dfa)  ((dfa).state >= 0)

/* include DFA table generated by guess.scm */
#include "guess_tab.c"

static lisp
get_utf8_encoding (char *sig, size_t siglen)
{
  return (utf8_signature_p (sig, siglen)
          ? Vencoding_default_utf8
          : Vencoding_default_utf8n);
}

static lisp
make_pair (lisp encoding, double score)
{
  return xcons (symbol_value_char_encoding (encoding),
                make_double_float (score));
}

lisp
Fguess_char_encoding (lisp string)
{
  guess_dfa eucj = DFA_INIT(guess_eucj_st, guess_eucj_ar);
  guess_dfa sjis = DFA_INIT(guess_sjis_st, guess_sjis_ar);
  guess_dfa utf8 = DFA_INIT(guess_utf8_st, guess_utf8_ar);
  guess_dfa big5 = DFA_INIT(guess_big5_st, guess_big5_ar);

  xstream_ibyte_helper is (string);
  if (is-> eofp ())
    return Qnil;

  char sig[3];
  size_t siglen = 0;
  for (int c = is->get (), i = 0; c != xstream::eof; c = is->get (), i++)
    {
      if (siglen < sizeof sig)
        sig[siglen++] = c;

      /* special treatment of iso-2022 escape sequence */
      if (c == 0x1b && !is->eofp ())
        {
          c = (unsigned char) is->peek ();
          if (c == '$' || c == '(')
            return list (make_pair (Vencoding_jis, 1.0));
        }

      /* special treatment of BOM */
      if (i == 0 && c == 0xff && !is->eofp ())
        {
          c = (unsigned char) is->peek ();
          if (c == 0xfe)
            return list (make_pair (Vencoding_default_utf16le_bom, 1.0));
        }
      if (i == 0 && c == 0xfe && !is->eofp ())
        {
          c = (unsigned char) is->peek ();
          if (c == 0xff)
            return list (make_pair (Vencoding_default_utf16be_bom, 1.0));
        }

      if (DFA_ALIVE(eucj))
        {
          if (!DFA_ALIVE(sjis) && !DFA_ALIVE(utf8) && !DFA_ALIVE(big5))
            return list (make_pair (Vencoding_euc_jp, 1.0));
          DFA_NEXT(eucj, c);
        }
      if (DFA_ALIVE(sjis))
        {
          if (!DFA_ALIVE(eucj) && !DFA_ALIVE(utf8) && !DFA_ALIVE(big5))
            return list (make_pair (Vencoding_sjis, 1.0));
          DFA_NEXT(sjis, c);
        }
      if (DFA_ALIVE(utf8))
        {
          if (!DFA_ALIVE(sjis) && !DFA_ALIVE(eucj) && !DFA_ALIVE(big5))
            return list (make_pair (get_utf8_encoding (sig, siglen), 1.0));
          DFA_NEXT(utf8, c);
        }
      if (DFA_ALIVE(big5))
        {
          if (!DFA_ALIVE(sjis) && !DFA_ALIVE(eucj) && !DFA_ALIVE(utf8))
            return list (make_pair (Vencoding_big5, 1.0));
          DFA_NEXT(big5, c);
        }

      if (!DFA_ALIVE(eucj) && !DFA_ALIVE(sjis) && !DFA_ALIVE(utf8) && !DFA_ALIVE(big5))
        {
          /* we ran out the possibilities */
          return Qnil;
        }
    }

  /* Now, we have ambigous code. */
  guess_dfa *order[] = { &utf8, &sjis, &eucj, &big5 };
  lisp result = Qnil;
  for (int i = numberof (order) - 1; i >= 0; i--)
    {
      lisp encoding = Qnil;
      if (!DFA_ALIVE (*order[i])) continue;
      if (order[i] == &eucj)
        encoding = Vencoding_euc_jp;
      else if (order[i] == &utf8)
        encoding = get_utf8_encoding (sig, siglen);
      else if (order[i] == &sjis)
        encoding = Vencoding_sjis;
      else if (order[i] == &big5)
        encoding = Vencoding_big5;
      else
        assert (0);

      result = xcons (make_pair (encoding, order[i]->score), result);
    }

  return result;
}
