#include "puLocal.h"

void puInput::normalize_cursors ( void )
{
  char val [ PUSTRING_MAX ] ;
  getValue ( val ) ;
  int sl = strlen ( val ) ;

  /* Clamp the positions to the limits of the text.  */

  if ( cursor_position       <  0 ) cursor_position       =  0 ;
  if ( select_start_position <  0 ) select_start_position =  0 ;
  if ( select_end_position   <  0 ) select_end_position   =  0 ;
  if ( cursor_position       > sl ) cursor_position       = sl ;
  if ( select_start_position > sl ) select_start_position = sl ;
  if ( select_end_position   > sl ) select_end_position   = sl ;

  /* Swap the ends of the select window if they get crossed over */

  if ( select_end_position < select_start_position )
  {
    int tmp = select_end_position ;     
    select_end_position = select_start_position ;     
    select_start_position = tmp ;
  }
}

void puInput::draw ( int dx, int dy )
{
  normalize_cursors () ;

  if ( !visible ) return ;

  /* 3D Input boxes look nicest if they are always in inverse style. */

  abox . draw ( dx, dy, ( (style==PUSTYLE_SMALL_BEVELLED ||
                           style==PUSTYLE_SMALL_SHADED) ) ? -style :
                        (accepting ? -style : style ), colour, FALSE ) ;

  int xx = puGetStringWidth ( legendFont, " " ) ;
  int yy = ( abox.max[1] - abox.min[1] - puGetStringHeight(legendFont) ) / 2 ;

  if ( accepting )
  {
    char val [ PUSTRING_MAX ] ;
    getValue ( val ) ;

    /* Highlight the select area */

    if ( select_end_position > 0 &&
         select_end_position != select_start_position )    
    {
      val [ select_end_position ] = '\0' ;
      int cpos2 = puGetStringWidth ( legendFont, val ) + xx + dx + abox.min[0] ;
      val [ select_start_position ] = '\0' ;
      int cpos1 = puGetStringWidth ( legendFont, val ) + xx + dx + abox.min[0] ;

      glColor3f ( 1.0f, 1.0f, 0.7f ) ;
      glRecti ( cpos1, dy + abox.min[1] + 6 ,
                cpos2, dy + abox.max[1] - 6 ) ;
    }
  }

  /* Draw the text */

  {
    /* If greyed out then halve the opacity when drawing the label and legend */

    if ( active )
      glColor4fv ( colour [ PUCOL_LEGEND ] ) ;
    else
      glColor4f ( colour [ PUCOL_LEGEND ][0],
		  colour [ PUCOL_LEGEND ][1],
		  colour [ PUCOL_LEGEND ][2],
		  colour [ PUCOL_LEGEND ][3] / 2.0f ) ; /* 50% more transparent */

    char val [ PUSTRING_MAX ] ;
    getValue ( val ) ;

    puDrawString ( legendFont, val,
                  dx + abox.min[0] + xx,
                  dy + abox.min[1] + yy ) ;

    draw_label ( dx, dy ) ;
  }

  if ( accepting )
  { 
    char val [ PUSTRING_MAX ] ;
    getValue ( val ) ;

    /* Draw the 'I' bar cursor. */

    if ( cursor_position >= 0 )
    {
      val [ cursor_position ] = '\0' ;

      int cpos = puGetStringWidth ( legendFont, val ) + xx + dx + abox.min[0] ;

      glColor3f ( 0.1f, 0.1f, 1.0f ) ;
      glBegin   ( GL_LINES ) ;
      glVertex2i ( cpos    , dy + abox.min[1] + 7 ) ;
      glVertex2i ( cpos    , dy + abox.max[1] - 7 ) ;
      glVertex2i ( cpos - 1, dy + abox.min[1] + 7 ) ;
      glVertex2i ( cpos - 1, dy + abox.max[1] - 7 ) ;
      glVertex2i ( cpos - 4, dy + abox.min[1] + 7 ) ;
      glVertex2i ( cpos + 3, dy + abox.min[1] + 7 ) ;
      glVertex2i ( cpos - 4, dy + abox.max[1] - 7 ) ;
      glVertex2i ( cpos + 3, dy + abox.max[1] - 7 ) ;
      glEnd      () ;
    }
  }
}


void puInput::doHit ( int button, int updown, int x, int /* y */ )
{
  if ( button == PU_LEFT_BUTTON )
  {
    /* Most GUI's activate a button on button-UP not button-DOWN. */

    if ( updown == active_mouse_edge || active_mouse_edge == PU_UP_AND_DOWN )
    {
      lowlight () ;

      char *strval ;
      getValue ( & strval ) ;
      char *tmpval = new char [ strlen(strval) + 1 ] ;
      strcpy ( tmpval, strval ) ;

      int i = strlen ( tmpval ) ;

      while ( x <= puGetStringWidth ( legendFont, tmpval ) + abox.min[0] &&
              i >= 0 )
        tmpval[--i] = '\0' ;
    
      accepting = TRUE ;
      cursor_position = i ;
      normalize_cursors () ;
      invokeCallback () ;
    }
    else
      highlight () ;
  }
  else
    lowlight () ;
}

int puInput::checkKey ( int key, int updown )
{
  (updown,updown);

  if ( ! isAcceptingInput() || ! isActive () || ! isVisible () )
    return FALSE ;

  normalize_cursors () ;

  char *p ;

  switch ( key )
  {
    case PU_KEY_PAGE_UP   :
    case PU_KEY_PAGE_DOWN :
    case PU_KEY_INSERT    : return FALSE ; 

    case PU_KEY_UP   :
    case PU_KEY_DOWN :
    case 0x1B /* ESC */ :
    case '\t' :
    case '\r' :
    case '\n' : /* Carriage return/Line Feed/TAB  -- End of input */
      rejectInput () ;
      normalize_cursors () ;
      invokeCallback () ;
      break ;

    case '\b' : /* Backspace */
      if ( cursor_position > 0 ) 
        for ( p = & string [ --cursor_position ] ; *p != '\0' ; p++ )
          *p = *(p+1) ;
      break ;

    case 0x7F : /* DEL */
      if ( select_start_position != select_end_position )
      {
        char *p1 = & string [ select_start_position ] ;
        char *p2 = & string [ select_end_position   ] ;

        while ( *p1 != '\0' )
          *p1++ = *p2++ ;

        select_end_position = select_start_position ;
      }
      else
	for ( p = & string [ cursor_position ] ; *p != '\0' ; p++ )
	  *p = *(p+1) ;
      break ;

    case 0x15 /* ^U */ : string [ 0 ] = '\0' ; break ;
    case PU_KEY_HOME   : cursor_position = 0 ; break ;
    case PU_KEY_END    : cursor_position = PUSTRING_MAX ; break ;
    case PU_KEY_LEFT   : cursor_position-- ; break ;
    case PU_KEY_RIGHT  : cursor_position++ ; break ;

    default:
      if ( key < ' ' || key > 127 ) return FALSE ;

      if ( strlen ( string ) >= PUSTRING_MAX )
        return FALSE ;

      for ( p = & string [ strlen(string) ] ;
               p != &string[cursor_position] ; p-- )
        *(p+1) = *p ;

      *p = key ;
      cursor_position++ ;
      break ;
  }

  setValue ( string ) ;
  normalize_cursors () ;
  return TRUE ;
}