/*
Copyright (C) 1998 Pyrosoft Inc. (www.pyrosoftgames.com), Matthew Bogue
 
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
#include <config.h>

#include "ConsoleInterface.hpp"

#include <time.h>
#include "GameConfig.hpp"
#include "Util/Log.hpp"
#include "Console.hpp"

bool ConsoleInterface::stdout_pipe;

long ConsoleInterface::console_size;
ConsoleLineArray ConsoleInterface::line_list;

iXY ConsoleInterface::surface_size;
iRect    ConsoleInterface::bounds;
iXY ConsoleInterface::line_offset;
long     ConsoleInterface::vertical_spacing;
long     ConsoleInterface::horizontal_spacing;
long     ConsoleInterface::max_char_per_line;

long ConsoleInterface::line_index;

bool ConsoleInterface::input_string_active;

int  ConsoleInterface::maxCharCount;
char ConsoleInterface::inputString[256];
char ConsoleInterface::inputPrompt[256];
int  ConsoleInterface::cursorPos;

void ConsoleInterface::initialize( long size )
{
    assert( size > 0 );

    console_size = size;
    line_list.initialize( size );

    line_index = console_size - 1;

    surface_size = iXY( 640, 480 );
    bounds = iRect( 5, 5, 640 - 5, 480 - 5 );

    max_char_per_line = (bounds.max.x - bounds.min.x) / 8;

    vertical_spacing = 2;

    line_offset.x = 0;
    line_offset.y = (8 + vertical_spacing);

    input_string_active = false;

    long line_loop;

    for ( line_loop = 0; line_loop < console_size; line_loop++ ) {
        line_list[ line_loop ].color = Color::white;
        line_list[ line_loop ].string[0] = 0;
        line_list[ line_loop ].life_timer.changePeriod( 8 );
    }

    stdout_pipe = false;
}

void ConsoleInterface::setToSurfaceSize( iXY pix )
{
    surface_size = pix;

    bounds.min.x = 5;
    bounds.min.y = 5;
    bounds.max = pix - iXY(5, 5);

    int CHAR_XPIX = 8; // XXX hardcoded
    max_char_per_line = (bounds.max.x - bounds.min.x) / CHAR_XPIX;
}

void ConsoleInterface::setStdoutPipe( bool on_off )
{
    stdout_pipe = on_off;
}

void ConsoleInterface::update( Surface &surface )
{
    update_overlap( surface );
}

void ConsoleInterface::postMessage(const char *format, ...)
{
    char temp_str[256];
    char *temp_str_ptr;
    long temp_str_length;
    va_list vap;

    va_start( vap, format );
    vsnprintf( temp_str, 256, format, vap );
    va_end( vap );

    LOGGER.debug("C: %s", temp_str);

    if (stdout_pipe && Console::server) {
        *Console::server << temp_str << std::endl;
    }

    temp_str_ptr = temp_str;
    temp_str_length = (long) strlen(temp_str);

    if( temp_str_length > max_char_per_line ) {
        long partion_count = temp_str_length / max_char_per_line;

        for( int i = 0; i < partion_count; i++ ) {
            if (line_index == 0)
                line_index = console_size - 1;
            else
                line_index = (line_index - 1) % console_size;

            strncpy( line_list[ line_index ].string, temp_str_ptr, max_char_per_line);
            line_list[ line_index ].string[ max_char_per_line ] = 0;

            line_list[ line_index ].color = Color::white;
            line_list[ line_index ].visible = true;
            line_list[ line_index ].life_timer.reset();

            temp_str_ptr = temp_str_ptr + max_char_per_line;
        }

    }

    if (line_index == 0)
        line_index = console_size - 1;
    else
        line_index = (line_index - 1) % console_size;

    strcpy( line_list[ line_index ].string, temp_str_ptr );

    line_list[ line_index ].color = Color::white;
    line_list[ line_index ].visible = true;
    line_list[ line_index ].life_timer.reset();
}

void ConsoleInterface::update_overlap( Surface &surface )
{
    iXY current_line;
    long  index;
    short visible_count;

    index = line_index;

    visible_count = 0;

    do {
        if ( line_list[ index ].life_timer.count() ) {
            line_list[ index ].visible = 0;
        }

        if ( line_list[ index ].visible )
            visible_count++;

        index = (index + 1) % console_size;

    } while( index != line_index );

    if( input_string_active == true ) {
        current_line.y = bounds.min.y + (line_offset.y * visible_count ) + (line_offset.y * 2);
        current_line.x = bounds.min.x + line_offset.x;
    } else {
        current_line.y = bounds.min.y + (line_offset.y * visible_count );
        current_line.x = bounds.min.x + line_offset.x;
    }

    do {
        if ( line_list[ index ].visible ) {
            surface.bltStringShadowed(current_line.x, current_line.y, line_list[ index ].string, line_list[ index ].color, Color::black );

            current_line.y = current_line.y - line_offset.y;
        }

        index = (index + 1) % console_size;

    } while( index != line_index );


    if( input_string_active == true ) {
        iXY input_offset;
        unsigned long max_char_space;
        unsigned long input_string_length;
        char *string_ptr;

        current_line.y = current_line.y - line_offset.y;

        surface.bltStringShadowed(current_line.x, current_line.y, inputPrompt, Color::white,
                Color::black );

        int CHAR_XPIX = 8; // XXX hardcoded
        input_offset.x = current_line.x + ( (long) strlen( inputPrompt ) ) * CHAR_XPIX;
        input_offset.y = current_line.y;

        max_char_space = (bounds.max.x - input_offset.x ) / CHAR_XPIX;
        input_string_length = strlen(inputString);

        if( input_string_length > max_char_space ) {
            string_ptr = inputString + (input_string_length - (max_char_space - 1) );
        } else {
            string_ptr = inputString;
        }

        surface.bltStringShadowed(input_offset.x, input_offset.y, string_ptr , Color::white,
                Color::black );

        surface.bltStringShadowed(  input_offset.x + cursorPos * CHAR_XPIX,
                                    input_offset.y,
                                    "_", Color::white, Color::black );
    }
}

void ConsoleInterface::setInputStringStatus( bool on_off )
{
    input_string_active = on_off;
}

void ConsoleInterface::resetInputString( char *prompt )
{
    cursorPos = 0;
    inputString[0] = 0;
    maxCharCount = 256;
    strcpy( inputPrompt, prompt );
}

void ConsoleInterface::getInputString( char *string )
{
    strcpy( string, inputString );
}

void ConsoleInterface::addChar(int newChar)
{
    // Check if the character should be excluded.
    // Add the character.
    int length = strlen(inputString) + 1;

    inputString[cursorPos] = newChar;

    inputString[length] = '\0';

    if (cursorPos < maxCharCount - 1) {
        cursorPos++;
    }
} // addChar

void ConsoleInterface::addExtendedChar(int newExtendedChar)
{
    // Process the extendedChar accordingly.
    switch (newExtendedChar) {
    case SDLK_RETURN: {}
        break;

    case SDLK_HOME: {
            cursorPos = 0;
        }
        break;

    case SDLK_LEFT: {
            if(--cursorPos < 0) {
                cursorPos = 0;
            }
        }
        break;

    case SDLK_RIGHT: {
            int length = strlen(inputString);
            if(++cursorPos > length) {
                if (cursorPos > maxCharCount) {
                    cursorPos = maxCharCount - 1;
                } else {
                    cursorPos = length;
                }
            }
        }
        break;

    case SDLK_END: {
            cursorPos = strlen(inputString);

            if (cursorPos > maxCharCount) {
                cursorPos = maxCharCount - 1;
            }
        }
        break;

    case SDLK_INSERT: {}
        break;

    case SDLK_DELETE: {
            if (cursorPos == (int) strlen(inputString + cursorPos)) {
                break;
            }

            memcpy(inputString + cursorPos, inputString + cursorPos + 1, strlen(inputString + cursorPos + 1) + 1);
        }
        break;

    case SDLK_BACKSPACE: {
            if (cursorPos >= 1) {
                int byteCount = strlen(inputString + cursorPos);

                // Only do this if we are not at the end of the string.
                if (byteCount > 0) {
                    memcpy(inputString + cursorPos - 1, inputString + cursorPos, byteCount);
                }

                cursorPos--;
                inputString[cursorPos + byteCount] = '\0';
            }

        }
        break;

    default: break;
    }

} // addExtendedChar
