// ============================================================================
// Internet Character Set Conversion: Input from ISO-2022-KR
// ============================================================================

#include "private.h"
#include "fechrcnv.h"
#include "kscobj.h"
#include "codepage.h"

/******************************************************************************
**************************   C O N S T R U C T O R   **************************
******************************************************************************/

CInccKscIn::CInccKscIn(UINT uCodePage, int nCodeSet) : CINetCodeConverter(uCodePage, nCodeSet)
{
    Reset();    // initialization
    return ;
}

/******************************************************************************
*******************************   R E S E T   *********************************
******************************************************************************/

void CInccKscIn::Reset()
{
	m_pfnConv = ConvMain;
	m_pfnCleanUp = CleanUpMain;
	m_fShift = FALSE;
    // bug #57570, Korean ISP DACOM only labels one designator in the 
    // conversion of a MIME mail. To decode the other part of MIME correctly, 
    // we need to decode the ISO document or MIME message even there is no
    // designator "esc ) C".
	m_fKorea = TRUE;
	m_nESCBytes = 0 ;
	m_fLeadByte = FALSE ;
    return ;
}

/******************************************************************************
*************************   C O N V E R T   C H A R   *************************
******************************************************************************/

HRESULT CInccKscIn::ConvertChar(UCHAR tc, int cchSrc)
{
	BOOL fDone = (this->*m_pfnConv)(tc);
    if (fDone)
        return S_OK;
    else
        return E_FAIL;
}

/******************************************************************************
*****************************   C L E A N   U P   *****************************
******************************************************************************/

BOOL CInccKscIn::CleanUp()
{
	return (this->*m_pfnCleanUp)();
}

/******************************************************************************
****************************   C O N V   M A I N   ****************************
******************************************************************************/

BOOL CInccKscIn::ConvMain(UCHAR tc)
{
	BOOL fDone = TRUE;

	if (tc == ESC) {
		m_pfnConv = ConvEsc;
		m_pfnCleanUp = CleanUpEsc;
		m_nESCBytes++ ;
	} else {
		if (m_fKorea) {
			switch (tc) {
			case SO:
				m_fShift = TRUE;
				break;

			case SI:
				m_fShift = FALSE;
				m_fLeadByte = FALSE ;
				break;

			default:
				if (m_fShift) {
					switch (tc) {
					case ' ':
					case '\t':
					case '\n':
						fDone = Output(tc);
						break;

					default:
						fDone = Output(tc | 0x80);
						m_fLeadByte = ~m_fLeadByte ;
						break;
					}
				} else {
					fDone = Output(tc);
				}
				break;
			}
		} else {
			fDone = Output(tc);
		}
	}
	return fDone;
}

/******************************************************************************
************************   C L E A N   U P   M A I N   ************************
******************************************************************************/

BOOL CInccKscIn::CleanUpMain()
{
	return TRUE;
}

/******************************************************************************
*****************************   C O N V   E S C   *****************************
******************************************************************************/

BOOL CInccKscIn::ConvEsc(UCHAR tc)
{
	if (tc == ISO2022_IN_CHAR) {
		m_pfnConv = ConvIsoIn;
		m_pfnCleanUp = CleanUpIsoIn;
		m_nESCBytes++ ;
		return TRUE;
	} else {
		m_pfnConv = ConvMain;
		m_pfnCleanUp = CleanUpMain;
		m_nESCBytes = 0 ;
		(void)Output(ESC);
        if (SUCCEEDED(ConvertChar(tc)))
            return TRUE;
        else
            return FALSE;
    }
}

/******************************************************************************
*************************   C L E A N   U P   E S C   *************************
******************************************************************************/

BOOL CInccKscIn::CleanUpEsc()
{
	m_pfnConv = ConvMain;
	m_pfnCleanUp = CleanUpMain;
	m_nESCBytes = 0 ;
	return Output(ESC);
}

/******************************************************************************
**************************   C O N V   I S O   I N   **************************
******************************************************************************/

BOOL CInccKscIn::ConvIsoIn(UCHAR tc)
{
	if (tc == ISO2022_IN_KR_CHAR_1) {
		m_pfnConv = ConvIsoInKr;
		m_pfnCleanUp = CleanUpIsoInKr;
		m_nESCBytes++ ;
		return TRUE;
	} else {
		m_pfnConv = ConvMain;
		m_pfnCleanUp = CleanUpMain;
		m_nESCBytes = 0 ;
		(void)Output(ESC);
		(void)ConvertChar(ISO2022_IN_CHAR);
        if (SUCCEEDED(ConvertChar(tc)))
            return TRUE;
        else
            return FALSE;
	}
}

/******************************************************************************
**********************   C L E A N   U P   I S O   I N   **********************
******************************************************************************/

BOOL CInccKscIn::CleanUpIsoIn()
{
	m_pfnConv = ConvMain;
	m_pfnCleanUp = CleanUpMain;
   	m_nESCBytes = 0 ;

	(void)Output(ESC);
	(void)ConvertChar(ISO2022_IN_CHAR);
	return CleanUp();
}

/******************************************************************************
***********************   C O N V   I S O   I N   K R   ***********************
******************************************************************************/

BOOL CInccKscIn::ConvIsoInKr(UCHAR tc)
{
	m_pfnConv = ConvMain;
	m_pfnCleanUp = CleanUpMain;
   	m_nESCBytes = 0 ;

	if (tc == ISO2022_IN_KR_CHAR_2) {
		m_fKorea = TRUE;
		return TRUE;
	} else {
		(void)Output(ESC);
		(void)ConvertChar(ISO2022_IN_CHAR);
		(void)ConvertChar(ISO2022_IN_KR_CHAR_1);
        if (SUCCEEDED(ConvertChar(tc)))
            return TRUE;
        else
            return FALSE;
	}
}

/******************************************************************************
*******************   C L E A N   U P   I S O   I N   K R   *******************
******************************************************************************/

BOOL CInccKscIn::CleanUpIsoInKr()
{
	m_pfnConv = ConvMain;
	m_pfnCleanUp = CleanUpMain;
   	m_nESCBytes = 0 ;

	(void)Output(ESC);
	(void)ConvertChar(ISO2022_IN_CHAR);
	(void)ConvertChar(ISO2022_IN_KR_CHAR_1);
	return CleanUp();
}

int CInccKscIn::GetUnconvertBytes()
{
    if ( m_fLeadByte )
        return 1 ;
    else if ( m_nESCBytes )
        return m_nESCBytes < 4 ? m_nESCBytes : 3 ;
    else
        return 0 ;
}

DWORD CInccKscIn::GetConvertMode()
{
    // 0xC431 -> 50225 ISO-2022-KR
    return ( m_fKorea ? 1 : 0 ) + ( m_fShift ? 2 : 0 ) | 0xC4310000 ;
}

void CInccKscIn::SetConvertMode(DWORD mode)
{
    Reset();    // initialization

    if ( mode & 0x00000001 )
        m_fKorea = TRUE ;
    if ( mode & 0x00000002 ) 
        m_fShift = TRUE ;
    return ;
}

// ============================================================================
// Internet Character Set Conversion: Output to ISO-2022-KSC
// ============================================================================

/******************************************************************************
**************************   C O N S T R U C T O R   **************************
******************************************************************************/

CInccKscOut::CInccKscOut(UINT uCodePage, int nCodeSet, DWORD dwFlag, WCHAR *lpFallBack) : CINetCodeConverter(uCodePage, nCodeSet)
{
    Reset();    // initialization
    _dwFlag = dwFlag;
    _lpFallBack = lpFallBack;
    return ;
}

/******************************************************************************
*******************************   R E S E T   *********************************
******************************************************************************/

void CInccKscOut::Reset()
{
	m_fDoubleByte = FALSE;
	m_fShift = FALSE;
	m_fKorea = FALSE;
	m_tcLeadByte = 0 ;
    return ;
}

/******************************************************************************
*************************   C O N V E R T   C H A R   *************************
******************************************************************************/

HRESULT CInccKscOut::ConvertChar(UCHAR tc, int cchSrc)
{
    BOOL fDone = TRUE;
    HRESULT hr = S_OK;

//
//IE RAID #103403 weiwu 03/16/00
//
//Per Korean PM (sykim), we don't have to prepend iso-2022-kr designator to conversion result string
//Also considering that URLMON can't handle encoded ASCII iso-2022-kr string
//We now remove following code, if it triggers any compatibility issues, we should re-enable it
//
#if 0
    // put designator to the top of the document
	if (!m_fKorea) {
		(void)Output(ESC);
		(void)Output(ISO2022_IN_CHAR);
		(void)Output(ISO2022_IN_KR_CHAR_1);
		(void)Output(ISO2022_IN_KR_CHAR_2);
		m_fKorea = TRUE;
	}
#endif

	if (!m_fDoubleByte) {
        //
        // We're not using IsDBCSLeadByteEx() due to perf. concern
        // We should assert that our hard code table match IsDBCSLeadByteEx(), 
        // But, MLang ships with down level platforms and assert won't be valid if there is a range change
        //
		if (IS_KOR_LEADBYTE(tc)) {
			m_fDoubleByte = TRUE;
			m_tcLeadByte = tc;
		} else {
			if (m_fKorea && m_fShift) {
				(void)Output(SI);
				m_fShift = FALSE;
			}
			fDone = Output(tc);
		}
	} else {
		m_fDoubleByte = FALSE;
		if (tc > 0x40) { // Check if trail byte indicates Hangeul
			if (m_tcLeadByte > 0xa0 && tc > 0xa0) { // Check if it's a Wansung
			    if (!m_fShift) {
				    if (!m_fKorea) {
					    (void)Output(ESC);
					    (void)Output(ISO2022_IN_CHAR);
					    (void)Output(ISO2022_IN_KR_CHAR_1);
					    (void)Output(ISO2022_IN_KR_CHAR_2);
					    m_fKorea = TRUE;
				    }
				    (void)Output(SO);
				    m_fShift = TRUE;
			    }                
				(void)Output(m_tcLeadByte & 0x7f);
				fDone = Output(tc & 0x7f);
			} else {
                UCHAR szDefaultChar[3] = {0x3f}; // possible DBCS + null    


                if (_lpFallBack && (_dwFlag & MLCONVCHARF_USEDEFCHAR))
                {
                    // only take SBCS, no DBCS character
                    if ( 1 != WideCharToMultiByte(CP_KOR_5601, 0,
                               (LPCWSTR)_lpFallBack, 1,
                               (LPSTR)szDefaultChar, ARRAYSIZE(szDefaultChar), NULL, NULL ))
                        szDefaultChar[0] = 0x3f;
                }

			    // shift out if we're in DBCS mode
                if (m_fKorea && m_fShift) {
				    (void)Output(SI);
				    m_fShift = FALSE;
			    }

                if (_dwFlag & (MLCONVCHARF_NCR_ENTITIZE|MLCONVCHARF_NAME_ENTITIZE))
                {
                    char    szChar[2];
                    char    szDstStr[10];
                    WCHAR   szwChar[2];
                    int     cCount;

                    szChar[0] = m_tcLeadByte;
                    szChar[1] = tc;
                
                    if (MultiByteToWideChar(CP_KOR_5601, 0, szChar, 2, szwChar, ARRAYSIZE(szwChar)))
                    {
                        // Output NCR entity
                        Output('&');
                        Output('#');
                        _ultoa((unsigned long)szwChar[0], (char*)szDstStr, 10);
                        cCount = lstrlenA(szDstStr);
                        for (int i=0; i< cCount; i++)
                        {
                            Output(szDstStr[i]);
                        }
                        fDone = Output(';');
                    }
                    else
                    {
				        fDone = Output(szDefaultChar[0]); // use default char
                        hr = S_FALSE;
                    }
                }
                else
                {
				    fDone = Output(szDefaultChar[0]); // use default char
                    hr = S_FALSE;
                }
			}
		} else {
			if (m_fKorea && m_fShift) {
				(void)Output(SI);
				m_fShift = FALSE;
			}
			(void)Output(m_tcLeadByte);
			fDone = Output(tc);
		}
		m_tcLeadByte = 0 ;
	}

    if (!fDone)
        hr = E_FAIL;

	return hr;
}

/******************************************************************************
*****************************   C L E A N   U P   *****************************
******************************************************************************/

BOOL CInccKscOut::CleanUp()
{
    BOOL fDone = TRUE;

    if ( m_fShift) 
    {
        fDone = Output(SI);
        m_fShift = FALSE;
    }
    return fDone ;
}

int CInccKscOut::GetUnconvertBytes()
{
    if (m_tcLeadByte)
        return 1 ;
    else
        return 0 ;
}

DWORD CInccKscOut::GetConvertMode()
{
    // for output, we don't need write back code page. 0xC431 -> 50225 ISO-2022-KR
    return ( m_fKorea ? 1 : 0 ) +  ( m_fShift ? 2 : 0 ) ;
}

void CInccKscOut::SetConvertMode(DWORD mode)
{
    Reset();    // initialization

    if ( mode & 0x00000001 ) 
        m_fKorea = TRUE ;
    if ( mode & 0x00000002 ) 
        m_fShift = TRUE ;
    return ;
}
