/*
 * widgets/TextFieldWidget.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 "TextFieldWidget.h"
#include "../util/TextureFont.h"

const char * const TextFieldWidget::HISTORY_RECALL_UP_COMMAND   = "histrec_up";
const char * const TextFieldWidget::HISTORY_RECALL_DOWN_COMMAND = "histrec_dn";

TextFieldWidget::TextFieldWidget( int w, int h ) : Widget( w, h ) {
        _point = 0;
        _mark = 0;
        _scrolledTo = 0;

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

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

        _name = "TextFieldWidget";
}

void TextFieldWidget::setText( const char * str ) {
        _text.erase();

        if ( str ) {
                _text.append( str );
        }

        _point = _text.size();                
        _scrolledTo = max( 0, (int) _point - (int) _numColumns );
}


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 TextFieldWidget::keyPressed( SDL_KeyboardEvent * evt, int x, int y ) {

        switch ( evt->keysym.sym ) {
                
                //
                // control-a and SDLK_HOME are equivalent
                //

        case SDLK_a:
                if ( ! ( evt->keysym.mod & KMOD_CTRL ) ) {
                        break;
                }
                
                //
                // fall through to SDLK_HOME
                //

        case SDLK_HOME:
                
                _point = 0;
                _scrolledTo = 0;
                return true;

                //
                // control-e and SDLK_END are equivalent
                //

        case SDLK_e:
                if ( ! ( evt->keysym.mod & KMOD_CTRL ) ) {
                        break;
                }

                //
                // fall through to SDLK_END
                //

        case SDLK_END:
                
                _point = _text.size();
                _scrolledTo = max( 0, (int) _point - (int) _numColumns );
                return true;
                
                //
                // control-k deletes to EOL
                //

        case SDLK_k:
                if ( evt->keysym.mod & KMOD_CTRL ) {
                        size_t len = _text.size();
                        if ( _point < len ) {
                                _text.erase( _point, len - _point );
                                //                                _point =  - len;
                                _scrolledTo = max( 0, (int) _point - (int) _numColumns );
                        }
                        else {
                                cout << " TODO: beep" << endl;
                        }

                        return true;
                }
                break;

        case SDLK_c:
                if ( evt->keysym.mod & KMOD_CTRL ) {
                        _text.erase();
                        _scrolledTo = 0;
                        _point = 0;
                        return true;
                }
                break;

        case SDLK_BACKSPACE:
                
                if ( _point == 0 ) {
                        cout << " TODO: beep" << endl;
                        return true;
                }
                
                --_point;
                
                _text.erase( _point, 1 );
                return true;
                

        case SDLK_DELETE:
                
                if ( _point == _text.size() ) {
                        // TODO: beep
                        return true;
                }

                _text.erase( _point, 1 );
                return true;

        case SDLK_LEFT:

                _point = max( 0, (int) _point - 1 );

                if ( _point < _scrolledTo )
                        _scrolledTo = _point;
               
                return true;

        case SDLK_RIGHT:
                _point = min( _text.size(), _point + 1 );

                if ( _point > ( _scrolledTo + _numColumns ) )
                        _scrolledTo = _point - _numColumns;
                
                return true;

        case SDLK_RETURN:
                
                notifyActionListeners( this, _text.c_str() );
                
                _text.erase();
                _scrolledTo = _mark = _point = 0;
                return true;

        case SDLK_UP:
                notifyActionListeners( this, HISTORY_RECALL_UP_COMMAND );
                return true;

        case SDLK_DOWN:
                notifyActionListeners( this, HISTORY_RECALL_DOWN_COMMAND );
                return true;

        default:
                break;
        }

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

                if ( _text.length() >= MAX_INPUT_LEN ) {
                        // TODO: beep
                        return true;
                }

                //                cout << " inserted " << (long unsigned int)evt->keysym.sym << endl;

                _text.insert( _point, 1,
                              ( evt->keysym.mod & KMOD_SHIFT ) ? 
                              shift_up( evt->keysym.sym ) :
                              evt->keysym.sym );
                
                ++_point;

                if ( _point > ( _scrolledTo + _numColumns ) )
                        _scrolledTo = _point - _numColumns;
                
        }

        return true;
}


void TextFieldWidget::paint() {
        Widget::paint();
        
        glColor4fv( getLookAndFeel()->getPrimaryColor( COLOR_ONE ) );

        glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
        glBegin( GL_QUADS );
        {
                glVertex2d( 0,            0 );
                glVertex2d( _size[0] - 1, 0 );
                glVertex2d( _size[0] - 1, _size[1] - 1 );
                glVertex2d( 0,            _size[1] - 1 );
        }
        glEnd();
        glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
        
        glColor4fv( getLookAndFeel()->getPrimaryColor( COLOR_THREE ) );

        int pointx[2] = { _fontWidth * ( _point - _scrolledTo ),
                          _fontWidth * ( _point + 1 - _scrolledTo ) };
                          
        glBegin( GL_LINES );
        {
                glVertex2f( pointx[0], 1 );
                glVertex2f( pointx[1], 1 );
        }
        glEnd();

        glTranslatef( 2, 2, 0 );

        if ( _scrolledTo || ( _text.size() > ( _numColumns + _scrolledTo )  ) ) {
                glColor3f( 1, 0.6, 0.1 );
                glBegin( GL_LINES );

                {
                        if ( _scrolledTo ) {
                                glVertex2f( 2, 0 );
                                glVertex2f( 2, _size[1] - 1 );
                        }

                        if ( _text.size() > ( _numColumns + _scrolledTo ) ) {
                                glVertex2f( _size[0] - 5, 0 );
                                glVertex2f( _size[0] - 5, _size[1] - 1 );
                        }
                }
                glEnd();
        }

        glColor4fv( getLookAndFeel()->getWhiteColor() ); 

        glTranslatef( 0, 2, 0 );

        _font->drawString( _text.c_str() + _scrolledTo, _numColumns );

}

