/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.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.mozilla.org/NPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See
 * the License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is Mozilla Communicator client code.
 *
 * The Initial Developer of the Original Code is Netscape Communications
 * Corporation.  Portions created by Netscape are Copyright (C) 1998
 * Netscape Communications Corporation.  All Rights Reserved.
 */

#include "nsIComponentManager.h"
#include "nsISO2022JPToUnicode.h"
#include "nsUCVJA2Dll.h"

//----------------------------------------------------------------------
// Global functions and data [declaration]

static PRInt16 cs1ByteShiftTable[] =  {
  0, u1ByteCharset ,
  ShiftCell(0,0,0,0,0,0,0,0)
};

static PRInt16 cs1ByteGLShiftTable[] =  {
  0, u1ByteGLCharset ,
  ShiftCell(0,0,0,0,0,0,0,0)
};

static PRInt16 cs2ByteShiftTable[] =  {
  0, u2BytesCharset,
  ShiftCell(0,0,0,0,0,0,0,0)
};

//----------------------------------------------------------------------
// Class nsISO2022JPToUnicode [implementation]

nsISO2022JPToUnicode::nsISO2022JPToUnicode() 
: nsBufferDecoderSupport()
{
  mHelper = nsnull;

  Reset();
}

nsISO2022JPToUnicode::~nsISO2022JPToUnicode() 
{
  NS_IF_RELEASE(mHelper);
}

nsresult nsISO2022JPToUnicode::CreateInstance(nsISupports ** aResult) 
{
  *aResult = new nsISO2022JPToUnicode();
  return (*aResult == NULL)? NS_ERROR_OUT_OF_MEMORY : NS_OK;
}

nsresult nsISO2022JPToUnicode::ConvertBuffer(const char ** aSrc, 
                                             const char * aSrcEnd,
                                             PRUnichar ** aDest, 
                                             PRUnichar * aDestEnd)
{
  // XXX redo the logic here - are we using pointers or length ints?
  const char * src = *aSrc;
  PRUnichar * dest = *aDest;
  PRUnichar val;
  PRInt32 srcLen = aSrcEnd - src;
  PRInt32 destLen = aDestEnd - dest;
  nsresult res;

  if (mHelper == nsnull) {
    res = nsComponentManager::CreateInstance(kUnicodeDecodeHelperCID, NULL, 
        kIUnicodeDecodeHelperIID, (void**) & mHelper);
    
    if (NS_FAILED(res)) return NS_ERROR_UDEC_NOHELPER;
  }

  if (mCharset == kASCII) {
    // single byte
    if (srcLen > destLen) {
      aSrcEnd = src + destLen;
      res = NS_PARTIAL_MORE_OUTPUT;
    } else res = NS_OK;
    // XXX use a table here too.
    for (;src<aSrcEnd;) {
      val = ((PRUint8)*src++);
      *dest++ = (val < 0x80)?val:0xfffd;
    }
  } else if ((mCharset == kJISX0201_1976) || (mCharset == kJISX0201_1976Kana)){
    // single byte
    if (srcLen > destLen) {
      aSrcEnd = src + destLen;
      res = NS_PARTIAL_MORE_OUTPUT;
    } else res = NS_OK;

    res = mHelper->ConvertByTable(src, &srcLen, dest, &destLen, 
          ( (mCharset == kJISX0201_1976) ?
            (uShiftTable*) &cs1ByteShiftTable : 
            (uShiftTable*) &cs1ByteGLShiftTable), 
          (uMappingTable*)&g_ut0201Mapping);


    *aSrc = src + srcLen;
    *aDest = dest + destLen;
    return res;
  } else {
    // XXX hello, use different tables for JIS X 0208-1978 and 1983 !!!
    // double byte
    if (srcLen > 2*destLen) {
      aSrcEnd = src + 2*destLen;
      res = NS_PARTIAL_MORE_OUTPUT;
    } else if (srcLen%2) {
      aSrcEnd--;
      res = NS_PARTIAL_MORE_INPUT;
    } else res = NS_OK;

    res = mHelper->ConvertByTable(src, &srcLen, dest, &destLen, 
        (uShiftTable*) &cs2ByteShiftTable, 
        ( (mCharset == kJISX0212_1990) ?
          (uMappingTable*)&g_ut0212Mapping :
          (uMappingTable*)&g_ut0208Mapping));
    *aSrc = src + srcLen;
    *aDest = dest + destLen;
    return res;
  }

  *aSrc = src;
  *aDest = dest;
  return res;
}

//----------------------------------------------------------------------
// Subclassing of nsBufferDecoderSupport class [implementation]

NS_IMETHODIMP nsISO2022JPToUnicode::ConvertNoBuff(const char * aSrc, 
                                                  PRInt32 * aSrcLength, 
                                                  PRUnichar * aDest, 
                                                  PRInt32 * aDestLength)
{
  const char * srcEnd = aSrc + (*aSrcLength);
  const char * src = aSrc;
  PRUnichar * destEnd = aDest + (*aDestLength);
  PRUnichar * dest = aDest;
  const char * p;
  nsresult res = NS_OK;

  for (; ((src < srcEnd) && (res == NS_OK)); src++) {
    switch (mState) {

      case 0:
        if (*src == 27) {
          mState = 1;
        } else if (dest >= destEnd) {
          res = NS_PARTIAL_MORE_OUTPUT;
          src--; // don't advance!
        } else {
          // here: src < srcEnd, dest < destEnd
          // find the end of this run
          for (p=src; ((p < srcEnd) && (*p != 27)); p++) {}
          res = ConvertBuffer(&src, p, &dest, destEnd);
          src--; // we did our own advance here
        }
        break;

      case 1:
        switch (*src) {
          case '(':
            mState = 2;
            break;
          case '$':
            mState = 3;
            break;
          default:
            res = NS_ERROR_ILLEGAL_INPUT;
        }
        break;

      case 2:
        switch (*src) {
          case 'B':
            mState = 0;
            mCharset = kASCII;
            break;
          case 'J':
            mState = 0;
            mCharset = kJISX0201_1976;
            break;
          case 'I':
            mState = 0;
            mCharset = kJISX0201_1976Kana;
            break;
          default:
            res = NS_ERROR_ILLEGAL_INPUT;
        }
        break;

      case 3:
        switch (*src) {
          case '@':
            mState = 0;
            mCharset = kJISX0208_1978;
            break;
          case 'B':
            mState = 0;
            mCharset = kJISX0208_1983;
            break;
          case '(':
            mState = 4;
            break;
          default:
            res = NS_ERROR_ILLEGAL_INPUT;
        }
        break;

      case 4:
        switch (*src) {
          case 'D':
            mState = 0;
            mCharset = kJISX0212_1990;
            break;
          default:
            res = NS_ERROR_ILLEGAL_INPUT;
        }
        break;

      default:
        res = NS_ERROR_UNEXPECTED;
    }
  }

  if ((res == NS_OK) && (mState != 0)) res = NS_PARTIAL_MORE_INPUT;

  *aSrcLength = src - aSrc;
  *aDestLength = dest - aDest;
  return res;
}

NS_IMETHODIMP nsISO2022JPToUnicode::GetMaxLength(const char * aSrc, 
                                                 PRInt32 aSrcLength, 
                                                 PRInt32 * aDestLength)
{
  // worst case
  *aDestLength = aSrcLength;
  return NS_OK;
}

NS_IMETHODIMP nsISO2022JPToUnicode::Reset()
{
  mState = 0;
  mCharset = kASCII;
  return nsBufferDecoderSupport::Reset();
}
