/*
 * Copyright (C) 2008, Chaoji Li<lichaoji@gmail.com>, All rights reserved.
 */

#include <cstdio>
#include <cstring>
#include <cctype>

const char * keywords[] = {
    "const", "static", "extern", "friend",
    "bool",  "sizeof",
    "long", "char", "int", "signed", "short", "unsigned",
    "float", "double",
    "while", "for", "break", "continue", "default", "switch", "case",
    "if", "else", "return",
    "new", "delete",
    "struct", "class", "public", "private", "protected",
    "try", "catch", "throw",
    "typedef", "enum",
    "true", "false", "void",
    "#define", "#include", "#if", "#else", "#ifdef",
    "#ifndef", "#elsif", "#end", "#endif",
};

bool is_keyword(char *s);

#define COLOR_STRING  "#A31515"
#define COLOR_COMMENT "#008000"
#define COLOR_KEYWORD "#0000FF"

#define NKEYWORDS (sizeof(keywords)/sizeof(char*))
#define MAXBUF 1000
char buf[MAXBUF] = "";
int buflen = 0;
bool in_comment = false;
bool in_string = false;
bool in_word = false;
char quote = 0;

enum StringType {
    ST_ENUM,
    ST_KEYWORD,
    ST_STRING,
    ST_COMMENT,
    ST_NORMAL
};

void print_buf(char *buf, StringType st);
void print_string(char *s);
void add_to_buf(char ch);

int main(int argc, char* argv[])
{
    FILE *fp = NULL;
    if (argc != 2) 
    {
        fp = stdin;
    }
    else
    {
        fp = fopen(argv[1], "r");
        if (fp == NULL) 
        {
            fprintf(stderr, "can't open file '%s' for read\n", argv[1]);
            return -1;
        }
    }

    printf("<html><body background=#FFFFFF foreground=#000000><pre style=\"font-family: FixedSys\">");
    int ch = fgetc(fp);
    while (ch != EOF)
    {
        switch (ch)
        {
        case '\\':
            add_to_buf(ch);

            if (in_string)
            {
                ch = fgetc(fp);
                add_to_buf(ch);
            }
            break;

        case '\'':
        case '\"':
            if (in_string && ch == quote)
            { 
                add_to_buf(ch);
                print_buf(buf, ST_STRING);
                in_string = false;
            }
            else if (!in_string)
            {
                print_buf(buf, ST_NORMAL);
                in_string = true;
                quote = ch;
                add_to_buf(ch);
            }
            else
            {
                add_to_buf(ch);
            }
            break;
        case '\n':
            if (!in_string && !in_comment)
            {
                add_to_buf(ch);
                print_buf(buf, ST_NORMAL);
            }
            else
            {
                add_to_buf(ch);
            }
            break;
        case '/': 
            if (!in_string && !in_comment)
            {
                char ch2 = fgetc(fp);
                if (ch2 == '/')
                {
                    // One line comment, print at once
                    print_buf(buf, ST_NORMAL);
                    add_to_buf(ch);
                    add_to_buf(ch2);
                    while ( (ch=fgetc(fp)) != EOF && ch != '\n')
                        add_to_buf(ch);
                    if (ch == '\n') add_to_buf(ch);
                    print_buf(buf, ST_COMMENT);
                }
                else if (ch2 == '*')
                {
                    // Multiple line comment
                    print_buf(buf, ST_NORMAL);
                    add_to_buf(ch);
                    add_to_buf(ch2);
                    in_comment = true;
                }
                else
                {
                    // This is not a comment
                    add_to_buf(ch);
                    add_to_buf(ch2);
                }
            }
            else if (in_comment)
            {
                if (buf[buflen-1] == '*')
                {
                    add_to_buf(ch);
                    print_buf(buf, ST_COMMENT);
                    in_comment = false;
                }
                else
                {
                    add_to_buf(ch);
                }
            }
            else
            {
                add_to_buf(ch);
            }
            break;

        case '#':
            if(!in_string && !in_comment)
            {
                print_buf(buf, ST_NORMAL);
                add_to_buf(ch);
                while (isalpha(ch = fgetc(fp)))
                {
                    add_to_buf(ch);
                }
                if (isspace(ch) && is_keyword(buf))
                {
                    print_buf(buf, ST_KEYWORD);   
                }
                continue;
            }
            else
            {
                add_to_buf(ch);
            }
            break;
        default:
            if (!in_string && !in_comment && isalpha(ch))
            {
                print_buf(buf, ST_NORMAL);
                while (isalnum(ch) || ch == '_')
                {
                    add_to_buf(ch);
                    ch = fgetc(fp);
                }
                if (is_keyword(buf))
                {
                    print_buf(buf, ST_KEYWORD);
                }
                continue;
            }
            else
            {
                add_to_buf(ch);
            }
            break;
        }
        ch = fgetc(fp);
    }

    print_buf(buf,ST_NORMAL);
    printf("</pre></body></html>");
    fclose(fp);
}

void add_to_buf(char ch)
{
    if (buflen < MAXBUF - 1)
    {
        buf[buflen++] = ch;
        buf[buflen] = '\0';
    }
}

void print_buf(char *buf, StringType st)
{
    if (buflen <= 0 || buf[0] == '\0')
        return;

    switch (st)
    {
    case ST_KEYWORD:
        printf("<font color=" COLOR_KEYWORD ">");
        print_string(buf);        
        printf("</font>");
        break;
    case ST_STRING:
        printf("<font color=" COLOR_STRING ">");
        print_string(buf);        
        printf("</font>");
        break;
    case ST_COMMENT:
        printf("<font color=" COLOR_COMMENT ">");
        print_string(buf);        
        printf("</font>");
        break;
    case ST_NORMAL:
    default:
        print_string(buf);
        break;
    }

    buflen = 0;
    buf[0] = '\0';
}

/* Print html representation of string s */
void print_string(char *s)
{
    while (*s != '\0')
    {
        switch (*s)
        {
        case '<':
        case '>':
        case '&':
            printf("&#%d;", *s);
            break;
        default:
            putchar(*s);
            break;
        }
        s++;
    }
}

bool is_keyword(char *s)
{
    for (int i = 0; i <NKEYWORDS; i++)
    {
        if (strcmp(s, keywords[i]) == 0)
            return true;
    }
    return false;
}