#include "Smart.hpp"

#define FLAG_BOLD                               0x0001
#define FLAG_ITALIC                             0x0002
#define FLAG_UNDERLINE                          0x0004
#define FLAG_STRIKED                            0x0008
#define FLAG_PARAGRAPH                          0x1000

static  DWORD   gMode;
static  LPSTR   gTargetSave;
static  LPSTR   gTargetData;
static  LPSTR   gSourceData;
static  SIZE_T  gSourceSize;
static  LPCSTR* gHashTable;
static  HANDLE  gProcessHeap;

template <size_t size> inline void AppendString(LPCSTR ptr)
{
    AppendString<4>(ptr);
    AppendString<size - 4>(ptr + 4);
}
template <> inline void AppendString<1>(LPCSTR ptr)
{
    *LPBYTE(gTargetSave) = *LPBYTE(ptr);
    gTargetSave = gTargetSave + 1;
}
template <> inline void AppendString<2>(LPCSTR ptr)
{
    *LPWORD(gTargetSave) = *LPWORD(ptr);
    gTargetSave = gTargetSave + 2;
}
template <> inline void AppendString<3>(LPCSTR ptr)
{
    AppendString<1>(ptr);
    AppendString<2>(ptr + 1);
}
template <> inline void AppendString<4>(LPCSTR ptr)
{
    *LPDWORD(gTargetSave) = *LPDWORD(ptr);
    gTargetSave = gTargetSave + 4;
}
#define AppendString(text)       AppendString<sizeof(text) - 1>(text);
static inline bool IsBlank(BYTE ch)
{
    return (' ' == ch) || ('\t' == ch);
}
static inline bool IsBreak(BYTE ch)
{
    return ('\r' == ch) || ('\n' == ch);
}
static inline bool IsWhite(BYTE ch)
{
    return IsBlank(ch) || IsBreak(ch);
}
static inline void AppendSymbol(CHAR ch)
{
    *gTargetSave++ = ch;
}
static inline void AppendBuffer(LPCSTR pHead, LPCSTR pTail)
{
#if 1
    while((pHead < pTail) && (3 & BYTE(gTargetSave)))
        *gTargetSave++ = *pHead++;

    while((pHead + 4) < pTail)
    {
        *LPDWORD(gTargetSave) = *LPDWORD(pHead);
        pHead = pHead + 4;
        gTargetSave = gTargetSave + 4;
    }
#endif
    while(pHead < pTail)
        *gTargetSave++ = *pHead++;
}
static void Write(HANDLE hTarget, DWORD dwPage)
{
    DWORD  count;
    LPSTR  pArray = gTargetData;
    SIZE_T uCount = gTargetSave - gTargetData;
    while((0 != uCount) && WriteFile(hTarget, pArray, uCount < dwPage ? uCount : dwPage, &count, NULL))
    {
        pArray = pArray + count;
        uCount = uCount - count;
    }
    
    gTargetSave = gTargetData;
}
static inline LPCSTR Last(LPCSTR pHead)
{
    while((0 != *pHead) && !IsBreak(*pHead)) pHead++;
    return pHead;
}
static LPCSTR Next(LPCSTR pHead)
{
    CHAR last = *pHead++;
    if (IsBreak(*pHead) && (last != *pHead))
        pHead++;
    return pHead;
}
static inline LPCSTR Skip(LPCSTR pHead)
{
    while(IsBlank(*pHead)) pHead++;
    return pHead;
}
static inline LPCSTR Head(LPCSTR pHead, DWORD dwID, bool toc)
{
    BYTE count = 0;
    while(('#' == *pHead)) 
    {
        pHead++;
        count++;
    }

    CHAR  data[16]; 
    LPSTR save = data;
    do
    {
        *save++ = CHAR('A' + (dwID & 0x0F));
        dwID = dwID >> 4;
    } while(0 != dwID);

    LPCSTR pTail = Last(pHead = Skip(pHead));
    if (toc)
    {
       AppendString("<a dwID='toc_");
       AppendBuffer(data, save);
       AppendString("' href='#");
       AppendBuffer(data, save);
       AppendString("'>");
       AppendBuffer(pHead, pTail);
       AppendString("</a>");
    }
    else
    {
        LPSTR head = gTargetSave;
        AppendString("<h0 dwID='");
        head[2] = BYTE(head[2] + count);
        AppendBuffer(data, save);
        AppendString("'>");
        AppendBuffer(pHead, pTail);

        AppendString("<a dwID='");
        AppendBuffer(data, save);
        AppendString("' href='#toc_");
        AppendBuffer(data, save);
        AppendString("'>^</a>");
        
        LPSTR tail = gTargetSave;
        AppendString("</h0>");
        tail[3] = BYTE(tail[3] + count);
    }

    AppendString("<br/>\r\n");
    return Next(pTail);
}
static LPCSTR Hash(LPCSTR pHead, bool set)
{
    DWORD dwHash = 0x811C9DC5;
    for (NOOP; (']' != *pHead) && !IsBreak(*pHead); pHead++)
        if (!IsWhite(*pHead))
            dwHash = (dwHash ^ BYTE(*pHead)) * 0x1000193;

    UINT uIndex = WORD(((dwHash >> 16) ^ dwHash) & 0xFFFF);
    if (set)
        return Last(gHashTable[uIndex] = Skip(pHead + 1));

    LPCSTR rv = gHashTable[uIndex];
    return rv ? rv : "\r\n";
}
static void _fastcall First(LPCSTR pHead, LPCSTR pTail)
{
    DWORD id = 0;
    CHAR last = 0x0D;
    while(pHead < pTail)
    {
        CHAR ch = *pHead;
        if (!IsBreak(last))
            pHead++;
        else if ('[' == ch)
            pHead = Hash(pHead + 1, true);
        else if ('#' == ch)
            pHead = Head(pHead, id++, true);
        else
            pHead++;
        last = ch;
    }
}
static LPCSTR Mode(LPCSTR pLine, LPCSTR pHead, CHAR last, CHAR tag, UINT flag)
{
    AppendBuffer(pLine, pHead);
    if (last == *pHead)
    {
        AppendSymbol(last);
    }
    else if (flag & gMode)
    {
        AppendString("</");
        AppendSymbol(tag);
        AppendSymbol('>');
        gMode = gMode ^ flag;
    }
    else if (!IsWhite(last))
    {
        AppendSymbol(*pHead);
    }
    else
    {
        AppendSymbol('<');
        AppendSymbol(tag);
        AppendSymbol('>');
        gMode = gMode ^ flag;
    }

    return pHead + 1;
}
static LPCSTR Text(LPCSTR pHead, LPCSTR pTail)
{
    CHAR last = 0;
    LPCSTR pLine = pHead;
    while((pHead < pTail) && !IsBreak(*pHead))
    {
        CHAR ch = *pHead;
        if ('[' == ch)
        {
            AppendBuffer(pLine, pHead);
            pHead = pLine = pHead + 1;
            
            AppendString("<a href='");
            LPCSTR pLink = Hash(pHead, false);
            AppendBuffer(pLink, Last(pLink));
            AppendString("'>");
        }
        else if (']' == ch)
        {
            AppendBuffer(pLine, pHead);
            pHead = pLine = pHead + 1;
            AppendString("<a/>");
        }
        else if ('*' == ch)
        {
            pHead = pLine = Mode(pLine, pHead, last, 'b', FLAG_BOLD);
        }
        else if ('/' == ch)
        {
            pHead = pLine = Mode(pLine, pHead, last, 'i', FLAG_ITALIC);
        }
        else if ('_' == ch)
        {
            pHead = pLine = Mode(pLine, pHead, last, 'u', FLAG_UNDERLINE);
        }
        else if ('-' == ch)
        {
            pHead = pLine = Mode(pLine, pHead, last, 's', FLAG_STRIKED);
        }
        else
        {
            pHead = pHead + 1;
        }

        last = ch;
    }

    AppendBuffer(pLine, pHead);
    return Next(pHead);
}
static void Second(LPCSTR pHead, LPCSTR pTail)
{
    DWORD id = 0;
    while(pHead < pTail)
    {
        CHAR ch = *pHead;
        if ('[' == ch)
        {
            pHead = Next(Last(pHead));
        }
        else if ('#' == ch)
        {
            pHead = Head(pHead, id++, false);
        }
        else if (IsBreak(ch))
        {
            pHead = Next(pHead);
            if (FLAG_PARAGRAPH & gMode)
            {
                AppendString("</p>\r\n");
                gMode = gMode ^ FLAG_PARAGRAPH;
            }
        }
        else
        {
            if (!(FLAG_PARAGRAPH & gMode))
            {
                AppendString("\r\n<p>");
                gMode = gMode ^ FLAG_PARAGRAPH;
            }
            pHead = Text(pHead, pTail);
        }
    }
}
static void Save(HANDLE hTarget, DWORD dwPage)
{
    gTargetSave = gTargetData = LPSTR(HeapAlloc(gProcessHeap, HEAP_NO_SERIALIZE, gSourceSize * 2));
    LPCSTR pTail = gSourceData + gSourceSize;
    AppendString("<html><head><meta charset='charset=utf-8'/></head><body>\r\n");
    First(gSourceData, gSourceData + gSourceSize);
    Write(hTarget, dwPage);
    Second(gSourceData, gSourceData + gSourceSize);
    AppendString("</body></html>");
    Write(hTarget, dwPage);
}
static void Save(HANDLE hTarget)
{
    Save(hTarget, FILE_TYPE_DISK != GetFileType(hTarget) ? 0x1000 : MAXDWORD);
}
static void Load(HANDLE hSource)
{
    DWORD  count;
    SIZE_T uCount = 0;
    gHashTable = (LPCSTR*) HeapAlloc(gProcessHeap, HEAP_NO_SERIALIZE|HEAP_ZERO_MEMORY, 0x10000 * sizeof(LPCSTR));
    gSourceData = LPSTR(HeapAlloc(gProcessHeap, HEAP_NO_SERIALIZE, (gSourceSize = 0x1000) + 2));
    while(ReadFile(hSource, gSourceData + uCount, 0x1000, &count, NULL) && (0 != count))
    {
        uCount = uCount + count;
        if (uCount == gSourceSize)
            gSourceData = LPSTR(HeapReAlloc(gProcessHeap, HEAP_NO_SERIALIZE, gSourceData, (gSourceSize = gSourceSize * 2) + 2));
    }

    gSourceData = LPSTR(HeapReAlloc(gProcessHeap, HEAP_NO_SERIALIZE, gSourceData, uCount));
    gSourceData[gSourceSize = uCount] = 0;
}
#include <mmsystem.h>
#pragma comment(lib, "WINMM")
INT WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, INT)
{
    gProcessHeap = GetProcessHeap();
    Load(GetStdHandle(STD_INPUT_HANDLE));
    Save(GetStdHandle(STD_OUTPUT_HANDLE));
    return 0;
}
