/*
 * widgets/TextPaneWidget.cc:
 *
 * Copyright (C) 2000 John Watson, jwatson@tempusmud.com
 * 
 * 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 "TextPaneWidget.h"
#include "Viewport.h"
#include "../util/TextureFont.h"

#include <iostream>
#include <iomanip>
#include <string>

#include <ctype.h>

TextPaneWidget::TextPaneWidget( int w, int h ) : Widget( w, h ) {
        _numSaveLines = 100;
        _bufIndex = 0;

        setBGColor4fv( getLookAndFeel()->getBlackColor() );
        setFGColor4fv( getLookAndFeel()->getWhiteColor() );

        setOpaque( true );

        _name = "TextPaneWidget";

        _keyEventHijacker = 0;

        _fontWidth = max( 1, _font->getFontWidth() );
        _numColumns = _size[0] / _fontWidth;

        setScrollableUnitIncrement( 1, _font->getFontHeight() );
        setScrollableBlockIncrement( 1, ( _size[1] / _font->getFontHeight()  ) * _font->getFontHeight());
}

TextPaneWidget::~TextPaneWidget() {
        list< char * >::const_iterator end = _textList.end();
        for ( list<char *>::const_iterator iter = _textList.begin();
              iter != end; ++iter ) {     
                free( *iter );
        }
}

void TextPaneWidget::appendText( const char * str ) {

        string astr = str;

        string::size_type pos = astr.find_first_not_of( '\n', 0 );
        
        while ( pos < string::npos ) {
                string::size_type endpos = astr.find_first_of( '\n', pos );
                appendLine( astr.substr( pos, 
                                         ( endpos == string::npos ) ? 
                                         endpos : 
                                         endpos - pos ).c_str() );
                            
                if ( endpos != string::npos )
                        pos = endpos + 1;
                else
                        pos = endpos;
        }
}

void TextPaneWidget::appendLineRaw( const char * str ) {

        if ( _textList.size() >= _numSaveLines ) {
                char * oldstr = _textList.back();
                _textList.pop_back();
                free( oldstr );
        }

        _textList.push_front( strdup( str ) );

        int height = max( 1, _font->getFontHeight() );
        
        height *= _textList.size();

        if ( height > _size[1] ) {
                resize( _size[0], height );
        }
}


static int peelLine( const char *str, int maxlen ) {

        if ( ! *str )
                return 0;
        
        int len = strlen( str );

        if ( len <= maxlen ) {
                return len;
        }

        //
        // scan backwards from maximum length looking for a space at which to break
        //

        for ( int i = maxlen - 1; i > 0; --i ) {
                if ( str[i] == ' ' || str[i] == '-' )
                        return i+1;
        }
        
        return maxlen;
}

/**
 * ignores control characters and linebreaks
 * simply breaks the line as needed to fit in the widget pane
 */
void TextPaneWidget::appendLine( const char * str ) {

        const char * ptr = str;
        int len = 0;

        char buf[1024];
        
        while ( ( len = peelLine( ptr, _numColumns ) ) ) {
                len = min( len, 1022 );
                strncpy( buf, ptr, len );
                buf[len] = 0;

                ptr += len;
                appendLineRaw( buf );
        }
}

static int shift_up( int keysym ) {
        if ( isalpha( keysym ) )
                return toupper( keysym );

        switch ( keysym ) {
        case SDLK_QUOTE:        return SDLK_QUOTEDBL;
        case SDLK_1:            return SDLK_EXCLAIM;
        case SDLK_2:            return SDLK_AT;
        case SDLK_3:            return SDLK_HASH;
        case SDLK_4:            return SDLK_DOLLAR;
        case SDLK_5:            return '%';
        case SDLK_6:            return SDLK_CARET;
        case SDLK_7:            return SDLK_AMPERSAND;
        case SDLK_9:            return SDLK_LEFTPAREN;
        case SDLK_0:            return SDLK_RIGHTPAREN;
        case SDLK_8:            return SDLK_ASTERISK;
        case SDLK_EQUALS:       return SDLK_PLUS;
        case SDLK_COMMA:        return SDLK_LESS;
        case SDLK_MINUS:        return SDLK_UNDERSCORE;
        case SDLK_PERIOD:       return SDLK_GREATER;
        case SDLK_SLASH:        return SDLK_QUESTION;
        case SDLK_SEMICOLON:    return SDLK_COLON;
        case SDLK_LEFTBRACKET:  return '{';
        case SDLK_BACKSLASH:    return '|';
        case SDLK_RIGHTBRACKET: return '}';
        case SDLK_BACKQUOTE:    return '~';
        }
        return ( keysym );

}

bool TextPaneWidget::keyPressed( SDL_KeyboardEvent * evt, int x, int y ) {

        if ( _keyEventHijacker ) {
                return _keyEventHijacker->keyPressed( evt, x, y );
        }

        if ( evt->keysym.sym >= 32 && evt->keysym.sym <= 126 ) {

                _buf[ _bufIndex++ ] = 
                        ( evt->keysym.mod & KMOD_SHIFT ) ? 
                        shift_up( evt->keysym.sym ) :
                        evt->keysym.sym;
        }
        
        if ( _bufIndex >= 1000 || evt->keysym.sym == SDLK_RETURN ) {
                _buf[ _bufIndex ] = 0;
                _bufIndex = 0;
                appendLine( _buf );
        }

        return true;
}

void TextPaneWidget::paint() {
        Widget::paint();
        
        list< char * >::const_iterator end = _textList.end();
        
        glColor4fv( getLookAndFeel()->getWhiteColor() ); 

        _font->startStringDraw();

        int ypos = 0;
        int fontheight = _font->getFontHeight();
        const class Viewport * v = 0;

        int clip[4];
        
        int startpos;
        int endpos;

        if ( ( v = findViewportClipRectangle( clip ) ) ) {
                startpos  = clip[1] - fontheight;
                endpos    = startpos + clip[3] + fontheight;
        }
        else {
                startpos  = -fontheight;
                endpos    = getSize()[1];
        }

        for ( list<char *>::const_iterator iter = _textList.begin();
              iter != end; ++iter ) {

                if ( ypos < startpos ) {
                        ypos += fontheight;

                        glTranslatef( 0, fontheight, 0 );
                        continue;
                }

                if ( ypos > endpos ) {
                        break;
                }

                glPushMatrix();
                
                ypos += fontheight;

                _font->drawStringFragment( *iter, 0, strlen( *iter ) );
                
                glPopMatrix();
                glTranslatef( 0, fontheight, 0 );
        }
        _font->endStringDraw();

}
