#include <stdio.h>
#include <sys/fcntl.h>
#include "display.h"
#include "text.h"

static int text_screen, text_x, text_y, text_w, text_h;
static int cursor_x, cursor_y, scroll_y;
static int text_buffer[TEXT_MAX]; /* not char */ 
static int text_length; /* in bytes */
static int text_chars;
static int text_pos, scroll_pos;

void init_text(int screen, int x, int y, int w, int h) {
   text_screen = screen;
   text_x = x;
   text_y = y;
   text_w = w;
   text_h = h;
   cursor_x = 0;
   cursor_y = 0;
   scroll_y = 0;
   text_pos = 0;
   scroll_pos = 0;
}

void print_all() {
   int x, y, i, c;

   x = 0; 
   y = 0;
   for (i=0; scroll_pos + i <= text_length && y < text_h; i++) {
      if (scroll_pos + i == text_length) 
	c = 0x02; 
      else 
	c = text_buffer[scroll_pos + i];
      draw_char(text_screen, text_x+x, text_y+y, c);
      if (c == 0x0a) {
	 x++;
	 if (x < text_w)
	   clear_rect(text_screen, text_x+x, text_y+y, text_w-x, 1);
	 y++;
	 x = 0;
      } else {
	 x++;
	 if (x >= text_w) {
	    y++;
	    x = 0;
	 }
      }
   }
   if (y < text_h) 
     clear_rect(text_screen, text_x+x, text_y+y, text_w-x, 1);
   y++;
   if (y < text_h)
     clear_rect(text_screen, text_x, text_y+y, text_w, text_h-y);
}

void hide_cursor() {
   r_set_attr1(text_screen, text_x+cursor_x, text_y+cursor_y, 0);
}

void show_cursor() {
   r_set_attr1(text_screen, text_x+cursor_x, text_y+cursor_y, 1);
}

int search_BOL(int pos) {
   int i;
   for(i=pos-1; i>=0; i--) {
      if (text_buffer[i] == 0x0a)
	return i+1;
   }
   return 0;
}

/* if no "next line", returns -1 */
int search_next_line(int pos) {
   int i;
   for(i=pos; i<text_length; i++) {
      if (text_buffer[i] == 0x0a)
	return i+1;
   }
   return -1;
}

int search_EOL(int pos) {
   int i;
   for(i=pos; i<text_length; i++) {
      if (text_buffer[i] == 0x0a)
	return i;
   }
   return text_length;
}

void check_scroll(int redraw) {
   int pos;
   if (cursor_y < 0) {
      cursor_y = 0;
      scroll_y --;
      pos = search_BOL(scroll_pos - 1);
      scroll_pos -= (scroll_pos - pos -1)%text_w + 1;
      if (redraw)
	print_all();
   } 
   if (cursor_y >= text_h) {
      cursor_y = text_h - 1;
      scroll_y ++;
      pos = search_next_line(scroll_pos);
      scroll_pos = (pos - scroll_pos < text_w) ? pos:(scroll_pos + text_w);
      if (redraw)
	print_all();
   } 
}

void cursor_right() {
   if (text_pos >= text_length) 
     return;
   if (text_buffer[text_pos] == 0x0a) {
      cursor_x = 0;
      cursor_y ++;
      check_scroll(1);
   } else {
      cursor_x++;
      if (cursor_x >= text_w) {
	 cursor_x = 0;
	 cursor_y ++;
	 check_scroll(1);
      }
   }
   text_pos++;
}

void cursor_left() {
   if (text_pos == 0)
     return;
   if (text_buffer[text_pos-1] == 0x0a) {
      cursor_x = ((text_pos-1) - search_BOL(text_pos-1)) % text_w;
      cursor_y--;
      check_scroll(1);
   } else {
      cursor_x--;
      if (cursor_x < 0) {
	 cursor_x = text_w - 1;
	 cursor_y--;
	 check_scroll(1);
      }
   }
   text_pos--;
}

void cursor_down_internal(int redraw) {
   int pos0, pos0_eol, pos1, pos1_eol;

   pos0 = search_BOL(text_pos);
   pos0_eol = search_EOL(text_pos);
   if ((pos0_eol-pos0)/text_w > (text_pos-pos0)/text_w) {
      cursor_y ++;
      check_scroll(redraw);
      if (pos0_eol - text_pos >= text_w) {
	 text_pos += text_w;
      } else {
	 text_pos = pos0_eol;
	 cursor_x = (pos0_eol - pos0)%text_w;
      }
   } else {
      if ((pos1 = search_next_line(text_pos)) < 0)
	return;
      cursor_y ++;
      check_scroll(redraw);
      pos1_eol = search_EOL(pos1);
      if (pos1_eol - pos1 >= cursor_x) {
	 text_pos = pos1 + cursor_x;
      } else {
	 cursor_x = pos1_eol - pos1;
	 text_pos = pos1_eol;
      }
   }
}
void cursor_down() {
   cursor_down_internal(1);
}

void cursor_up_internal(int redraw) {
   int pos0, pos1, pos1_eol;
   int x_eol;

   pos0 = search_BOL(text_pos);
   if ((text_pos-pos0)/text_w > 0) {
      cursor_y --;
      check_scroll(redraw);
      text_pos -= text_w;
   } else {
      if (pos0 == 0)
	return;
      pos1_eol = pos0 - 1;
      pos1 = search_BOL(pos1_eol);
      cursor_y --;
      check_scroll(redraw);
      x_eol = (pos1_eol-pos1)%text_w;

      if (x_eol >= cursor_x) {
	 text_pos = pos1_eol - x_eol + cursor_x;
      } else {
	 cursor_x = x_eol;
	 text_pos = pos1_eol;
      }
   }
}
void cursor_up() {
   cursor_up_internal(1);
}

void page_down() {
   int i;

   for(i=0; i<text_h-1; i++)
     cursor_down_internal(0);
   print_all();
}
void page_up() {
   int i;

   for(i=0; i<text_h-1; i++)
     cursor_up_internal(0);
   print_all();
}


void backspace_char() {
   int i;
   int c;
   int pos0, pos_eol;
   int x, y, w, h, i0, i1;
   int redraw_all = 1;

   if (text_pos == 0)
     return;
   c = text_buffer[text_pos-1];
   pos0 = search_BOL(text_pos);
   if ((text_pos - pos0)%text_w != 0) {
      pos_eol = search_EOL(text_pos);
      if ((pos_eol - pos0)%text_w != 0) {
	 redraw_all = 0;
	 x = text_x;
	 y = text_y + cursor_y;
	 w = text_w;
	 h = ((pos_eol - pos0)/text_w)+1
	   - ((text_pos - pos0)/text_w);
	 i0 = (text_pos - 1 - pos0)%text_w;
	 i1 = 1 + ((pos_eol - pos0)%text_w);
	 rotate_up_char(text_screen, x, y, w, h, i0, i1);
	 draw_char(text_screen, x+i1-1, y+h-1, ' ');
      }
      cursor_x --;
   }
   else if (c == 0x0a) {
      cursor_y --;
      check_scroll(0);
      cursor_x = (text_pos - 1 - search_BOL(text_pos - 1))%text_w;
   } else {
      cursor_y --;
      check_scroll(0);
      cursor_x = text_w - 1;
   }
   for(i=text_pos; i<text_length; i++) {
      text_buffer[i-1] = text_buffer[i];
   }
   text_chars -= ((c & 0xff00)? 2:1);
   text_length --;
   text_pos --;
   if (redraw_all)
     print_all();
   else {
   }
}

void insert_char(int c) {
   int i;
   int pos0, pos_eol;
   int x, y, w, h, i0, i1;
   int redraw_all = 1;

   if (text_chars + ((c & 0xff00)? 2:1) > TEXT_MAX)
     return;

   pos0 = search_BOL(text_pos);
   pos_eol = search_EOL(text_pos);
   if (c != 0x0a && (pos_eol + 1 - pos0)%text_w != 0) {
      redraw_all = 0;
      x = text_x;
      y = text_y + cursor_y;
      w = text_w;
      h = ((pos_eol - pos0)/text_w)+1
	- ((text_pos - pos0)/text_w);
      i0 = (text_pos - pos0)%text_w;
      i1 = 2 + ((pos_eol - pos0)%text_w);
      rotate_down_char(text_screen, x, y, w, h, i0, i1);
      draw_char(text_screen, x+i0, y, c);
   }
      
   for(i=text_length; i > text_pos; i--) {
      text_buffer[i] = text_buffer[i-1];
   }
   text_buffer[text_pos] = c;
   text_chars += ((c & 0xff00)? 2:1);
   text_length ++;
   cursor_right();
   if (redraw_all)
     print_all();
}

#define DEFAULT_CHARS 2048
int read_file(const char* filename) {
   int i, j;
   int c1, c2;
   FILE far *text_file;
   struct stat statbuf;

   while (stat(filename, &statbuf)==E_FS_FILE_NOT_FOUND) {
      creat(filename, 0x06, DEFAULT_CHARS/BLOCKSZ);
      if (stat(filename, &statbuf)==E_FS_FILE_NOT_FOUND)
	return 1;
   } 
   if (statbuf.count*BLOCKSZ > TEXT_MAX) { /* TOO LARGE */
      return 1;
   }
   if ((text_file = fopen(filename, "r")) == NULL) {
      text_length = 0;
      return 1; 
   }

   /* chars > TEXT_MAX are truncated. 
    * if (last char & 0x80), it is also removed. */
   i = 0;
   j = 0;
   while(1) {
      if ((c1 = fgetc(text_file))== EOF) 
	break;
      if (c1 & 0x80) {
	 if ((c2 = fgetc(text_file))== EOF) 
	   break;
	 text_buffer[j] = (c1<<8) + c2;
	 j++;
	 i += 2;
	 if (i >= TEXT_MAX) 
	   break;
      } else {
	 text_buffer[j] = c1;
	 j++;
	 i++;
	 if (i >= TEXT_MAX) 
	   break;
      }
   }
   text_chars = i;
   text_length = j;
   fclose(text_file);
   return 0;
}

int write_file(const char* filename) {
   int i;
   int c; 
   FILE far *text_file;
   struct stat statbuf;

   if (stat(filename, &statbuf)==E_FS_FILE_NOT_FOUND) {
      creat(filename, 0x06, (text_chars+BLOCKSZ-1)/BLOCKSZ);
      if (stat(filename, &statbuf)==E_FS_FILE_NOT_FOUND)
	return 1;
   }
   if (statbuf.count*BLOCKSZ < text_chars) {
      return 1;
   }
   if ((text_file = fopen(filename, "w")) == NULL) {
      return -1;
   }

   for(i=0; i<text_length; i++) {
      c = text_buffer[i];
      if (c & 0x8000) {
	 fputc((c>>8), text_file);
	 fputc(c&0xff, text_file);
      } else {
	 fputc(c, text_file);
      }
   }
   fclose(text_file);
   return 0;
}
