
#include <tos.h>
#include <string.h>


#include <stdlib.h>
#include <stdio.h>

#include "gui.h"
#include "editor.h"
#include "keys.h"

struct s_edit_buf edit_buf;

int edit_buf_init( struct s_edit_buf * buf )
{
   memset( (char*)buf, 0, sizeof(struct s_edit_buf) );
   buf->blocksize = EDIT_BUF_DEFAULT_BLOCKSIZE;
   edit_buf_destroy( buf );
   buf->data = malloc( buf->blocksize );
   if( buf->data == NULL )
      return( 1 );
   buf->size = buf->blocksize;
   buf->data[0] = 0;
   buf->used = 1;
   return( 0 );
}

void edit_buf_destroy( struct s_edit_buf * buf )
{
   if( buf->data )
      free( buf->data );

   buf->data = NULL;
}

void edit_buf_alloc( struct s_edit_buf * buf, int amount )
{
   int blocks = 1;

   if( buf->size < (buf->used + amount)+1 ){
      if( amount > buf->blocksize)
         blocks = (amount / buf->blocksize)+1;
      buf->data = realloc( buf->data, buf->size+(buf->blocksize*blocks));
      buf->size += (buf->blocksize*blocks);
   }
}

long edit_buf_find_linestart( struct s_edit_buf * buf, long curpos, long skip, long direction )
{
   long i;

   if( direction == 0 )
      return( -1 );

   if( buf == NULL)
      return( -1 );

   if( curpos > buf->used-1 )
      return( buf->used-1 );

   if( skip > 0 ) {
      curpos = edit_buf_find_linestart( buf, curpos, 0, direction );
      skip--;
      if( curpos > 0 && skip >0 ){
         return( edit_buf_find_linestart( buf, curpos, skip, direction ) );
      }

      return( curpos );
   }

   curpos += direction;
   if( direction > 0 && curpos <= 0 ) {
      return( 0 );
   }


   for( i=curpos; i>=1; i=i+direction){
      if(buf->data[i] == 10 || buf->data[i] == 13 || buf->data[i] == 0 ){
         return( i ) ;
      }
   }
   return( -1 );
}

void edit_buf_redraw( struct s_edit_buf * buf )
{
   char * out = " ";
   int cury = 1;
   int curx = 0;
   int curline = 1;
   int i=0;
   int len;
   long lines_limit;
   long lines_done=0;
   short inv=0;

   len = strlen( buf->data );
   lines_limit = c_rows-2;

#ifndef WITHOUT_GFX
	cury = 4;
	lines_limit -= 3;
#endif

   if( i >= buf->redraw_start_at_char )
      fillspace(0, cury, c_cols, 0);
   for( i = 0; i<=len; i++ ) {
      if( buf->data[i] == 13 || buf->data[i] == 10 ) {
         if( i >= buf->redraw_start_at_char ){
            fillspace(curx, cury, c_cols, 0);
            if( buf->cursor == i )
               textxy( curx, cury, 1, " " );
            lines_done++;
            if( lines_done >= lines_limit )
               break;
         }
         curx = 0;
         cury++;
         if( cury > c_rows-2 )
            break;
         continue;
      }
      else {
            if( buf->scroll_y < cury && curx < c_cols-1 )
            {
               *out = buf->data[i];
               if( *out == 0 )
                  *out = ' ';
               if( i == buf->cursor )
                  inv = 1;
               if( i >= buf->redraw_start_at_char ){
                  textxy( curx, cury, inv, out );
               }
               if( inv == 1 )
                  inv = 0;
               if( buf->data[i] == 0 ){
                  break;
               }
            }
            curx++;
      }
   }
}

void edit_buf_insert(
   struct s_edit_buf * buf,
   unsigned char * c
)
{
   int i, pos;
   int len = strlen( c );
   edit_buf_alloc( buf, len );
   memmove(
         &buf->data[buf->cursor+len],
         &buf->data[buf->cursor],
         strlen(&buf->data[buf->cursor])
   );
   for( i=0; i<len; i++) {
      pos = buf->cursor+i;
      if( c[i] == 13 || c[i] == 10 ) {
         buf->lines++;
      }
      buf->data[pos] = c[i];
      buf->used++;
   }
   buf->data[buf->used-1] = 0;
   buf->redraw_start_at_char = (buf->cursor>0 ) ? buf->cursor-1 : 0;
   buf->redraw_limit_lines = -1; /* newline may cause reflow */
   buf->cursor += len;
   edit_buf_redraw( buf );
}

void edit_buf_remove( struct s_edit_buf * buf )
{
   buf->used--;
}

char * edit_text( char * title, char * text )
{
   short code, key;
   char ascii, sh;
   char exit = 0;
   int i=0, x=0, z=0;
   char ig=0;
   char tmp[3]="";
   char * result=NULL;
   long nextbest;
   long maybest;

   draw_info("ESC=Exit without save, F2=Save");
   for( i=1; i<c_rows-1; i++ ){
      fillspace(0, i, c_cols, 0);
   }
   draw_title( title );

   edit_buf_init( (struct s_edit_buf*)&edit_buf );
   edit_buf_insert( &edit_buf, text );

   do{
      ig = 0;
      key = getkey(&sh, &code, &ascii);
      switch( code ){

         case ESC:
            exit = 1;
         break;

      case F1_BASE+1*0x100:
         /*case CTRL_S: */
            /*if( ascii ==  ){*/
               result = edit_buf.data;
               exit = 1;
            /*}*/
         break;

         case BACKSPACE:
            ig = 1;
            if( edit_buf.cursor > 0 ) {
               edit_buf.cursor--;
               edit_buf.redraw_limit_lines = 2;
               edit_buf.redraw_start_at_char = edit_buf.cursor;
               edit_buf_redraw( &edit_buf );
               edit_buf.cursor++;
               memmove( &edit_buf.data[edit_buf.cursor-1],
                  &edit_buf.data[edit_buf.cursor],
                  strlen(&edit_buf.data[edit_buf.cursor])+1
               );
               edit_buf.cursor--;
               edit_buf.redraw_start_at_char = (edit_buf.cursor>0 ) ? edit_buf.cursor-1 : 0;
               edit_buf.redraw_limit_lines = -1;
               edit_buf.used--;
               edit_buf.data[edit_buf.used-1] = 0;
               edit_buf_redraw( &edit_buf );
            }
         break;

         case CURS_UP:
            ig = 1;
            nextbest = edit_buf_find_linestart( &edit_buf, edit_buf.cursor, 0, -1);
            maybest = edit_buf_find_linestart( &edit_buf, nextbest-1, 0, -1);
            if( nextbest != -1 && maybest < nextbest && maybest >= 0 ) {
               if( maybest+(edit_buf.cursor-nextbest) < nextbest ){
                  nextbest = maybest+(edit_buf.cursor-nextbest);
               }
            }
            if( nextbest > -1 ) {
               edit_buf.redraw_start_at_char = nextbest;
               edit_buf.redraw_limit_lines = 4;
               edit_buf.cursor = nextbest;
               edit_buf_redraw( &edit_buf );
            }
         break;

         case CURS_DOWN:
            ig = 1;
            z = edit_buf_find_linestart( &edit_buf, edit_buf.cursor, 0, -1);
            if( z>=0 ){
               z = edit_buf.cursor-z;
            } else {
               z = 0;
            }
            nextbest = edit_buf_find_linestart( &edit_buf, edit_buf.cursor-1, 0, +1);
            if( nextbest != -1 ) {
               maybest = edit_buf_find_linestart( &edit_buf, nextbest+1, 0, +1);
               nextbest += z;
               if( nextbest > maybest ) {
                  nextbest = maybest;
               }
               if( nextbest > -1 ) {
                  edit_buf.redraw_start_at_char = edit_buf.cursor;
                  edit_buf.redraw_limit_lines = 4;
                  edit_buf.cursor = nextbest;
                  edit_buf_redraw( &edit_buf );
               }
            }
         break;

         case CURS_LEFT:
            if( edit_buf.cursor > 0 ){
               edit_buf.cursor--;
               edit_buf.redraw_limit_lines = 2;
               edit_buf.redraw_start_at_char = edit_buf.cursor;
               edit_buf_redraw( &edit_buf );
            }
            ig = 1;
         break;

         case CURS_RIGHT:
            if( edit_buf.cursor < edit_buf.used-1 ){
               edit_buf.redraw_start_at_char = edit_buf.cursor;
               edit_buf.cursor++;
               edit_buf.redraw_limit_lines = 2;
               edit_buf_redraw( &edit_buf );
            }
            ig = 1;
         break;


         default: break;
      }
      if( ig == 1 || exit == 1 )
         continue;

      if( (char)key == 0 )
         continue;
      if( key == 13 )
         key = 10;
      *tmp = (char)key;
      edit_buf_insert( &edit_buf, tmp );

   } while( exit != 1 );

   return( result );
}

void end_edit_text(  )
{
   edit_buf_destroy( &edit_buf );
}