/*
 *  Copyright (C) 1997-2002 Kikutani Makoto (poo@momonga-linux.org)
 *
 *  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. 
 */

/*
 * ext/slang/slmodule.c
 * 
 */

#include <stdio.h>
#include <stdarg.h>
#include <signal.h>
#include <slang.h>
#include <errno.h>

#ifndef SLMEMSET
# ifdef HAVE_MEMSET
#  define SLMEMSET memset
# else
#  define SLMEMSET SLmemset
# endif
#endif

#include "ruby.h"
#include "slmodule.h"

static char *Color_str [] =
{
   "black",
   "red",
   "green",
   "brown",
   "blue",
   "magenta",
   "cyan",
   "lightgray",
   "gray",
   "brightred",
   "brightgreen",
   "yellow",
   "brightblue",
   "brightmagenta",
   "brightcyan",
   "white"
};

static VALUE mSlang;
static VALUE cScrW;

static int Want_Window_Size_Change = 0;

#ifdef SIGWINCH
static void sig_winch_handler (int sig)
{
   sig = errno;
   Want_Window_Size_Change = 1;
   SLsignal_intr (SIGWINCH, sig_winch_handler);
   errno = sig;
}

static VALUE
sl_get_winch_flag(void)
{
   return INT2FIX(Want_Window_Size_Change);
}

static VALUE
sl_clr_winch_flag(void)
{
   Want_Window_Size_Change = 0;
   return Qnil; 
}
#endif

static VALUE
sltt_get_screen_size(void)
{
   SLtt_get_screen_size ();
   return Qnil; 
}

static VALUE
slsig_block_signals(void)
{
   SLsig_block_signals ();
   return Qnil;
}

static VALUE
slsmg_suspend_smg(void)
{
   SLsmg_suspend_smg ();
   return Qnil;
}

static VALUE
slsmg_resume_smg(void)
{
   SLsmg_resume_smg ();
   return Qnil;
}

static VALUE
slsmg_erase_eos(void)
{
   SLsmg_erase_eos ();
   return Qnil;
}

static VALUE
slsmg_normal_video(void)
{
   SLsmg_normal_video ();
   return Qnil;
}
static VALUE
slsmg_reverse_video(void)
{
   SLsmg_reverse_video ();
   return Qnil;
}

static VALUE
slsmg_set_char_set(VALUE obj, VALUE set)
{
   SLsmg_set_char_set (NUM2INT(set));
   return Qnil;
}

static VALUE
slsmg_forward(VALUE obj, VALUE set)
{
   SLsmg_forward (NUM2INT(set));
   return Qnil;
}

static VALUE
slsmg_set_color(obj, color)
   VALUE obj;
   VALUE color;
{
   FIXNUM_P(color);
   SLsmg_set_color(FIX2INT(color));
   return Qnil;
}
 
static VALUE
slsmg_gotorc(VALUE obj, VALUE row, VALUE col)
{
   SLsmg_gotorc (NUM2INT(row), NUM2INT(col));
   return Qnil;
}

static VALUE
slsmg_printf(argc, argv, self)
   int argc;                                                                 
   VALUE *argv;                                                              
   VALUE self;                                                               
{                                                                             
   VALUE str = rb_f_sprintf(argc, argv);                                         
   /*  SLsmg_printf("%s", RSTRING(str)->ptr); */
   SLsmg_write_string(RSTRING(str)->ptr);
   return Qnil;                                                               
}                                                                             

static VALUE
slsmg_write_string(obj, str)
   VALUE obj;
   VALUE str;
{                                                                             
   Check_Type(str, T_STRING);
   SLsmg_write_string(RSTRING(str)->ptr);
   return Qnil;                                                               
}

static VALUE
slsmg_write_nchars(obj, str, n)
   VALUE obj;
   VALUE str;
   VALUE n;
{
   Check_Type(str, T_STRING);
   FIXNUM_P(n);
   SLsmg_write_nchars(RSTRING(str)->ptr, FIX2INT(n));
   return Qnil;                                                               
}

static VALUE
slsmg_write_char(obj, c)
   VALUE obj;
   VALUE c;
{
   FIXNUM_P(c);
   SLsmg_write_char(FIX2INT(c));
   return Qnil;                                                               
}

static VALUE
slsmg_erase_eol(void)
{
   SLsmg_erase_eol();
   return Qnil;
}

static VALUE
slsmg_cls(void)
{
   SLsmg_cls();
   return Qnil;
}

static VALUE
slsmg_refresh(void)
{
   SLsmg_refresh();
   return Qnil;
}

static VALUE
sltt_get_terminfo(void)
{
   SLtt_get_terminfo();
   return Qnil;
}

static VALUE
slkp_init(void)
{
   return INT2NUM(SLkp_init ());
}

static VALUE
sltt_set_mouse_mode(obj, mode, force)
VALUE obj;
VALUE mode;
VALUE force;
{
   SLtt_set_mouse_mode(NUM2INT(mode), NUM2INT(force));
   return Qnil;
}

static VALUE
sltty_set_suspend_state(obj, mode)
VALUE obj;
VALUE mode;
{
   SLtty_set_suspend_state(NUM2INT(mode));
   return Qnil;
}


static VALUE
slsig_unblock_signals(void)
{
   SLsig_unblock_signals();
   return Qnil;
}

static VALUE
slsmg_char_at(void)
{
   return INT2NUM(SLsmg_char_at());
}


static VALUE
slsmg_draw_object(obj, r_r, r_c, r_object)
VALUE obj;
VALUE r_r;
VALUE r_c;
VALUE r_object;
{
   int r = NUM2INT(r_r);
   int c = NUM2INT(r_c);
   unsigned char object = NUM2INT(r_object);

   SLsmg_draw_object (r, c, object);
   return Qnil;
}

static VALUE
slkp_getkey(void)
{
   return INT2NUM(SLkp_getkey()); 
#if 0
   int c;
   while ((c = SLkp_getkey()) == 26)
     kill (0, SIGTSTP);
   return INT2NUM(c);
#endif
}

static VALUE
slang_getkey(void)
{
   return INT2NUM(SLang_getkey());
}

static VALUE
sltt_beep(void)
{
   SLtt_beep();
   return Qnil;
}

static VALUE
slang_reset_tty(void)
{
   SLang_reset_tty ();
   return Qnil;
}

static VALUE
slsmg_reset_smg(void)
{
   SLsmg_reset_smg ();
   return Qnil;
}

static VALUE
slsmg_init_smg(void)
{
   SLsmg_init_smg ();
   return Qnil;
}

static VALUE
slsmg_set_screen_start(VALUE obj, VALUE r_r, VALUE r_c)
{
   int r = NUM2INT(r_r);
   int c = NUM2INT(r_c);
   VALUE ary;

   SLsmg_set_screen_start(&r, &c);

   ary = rb_ary_new2(2);
   rb_ary_push(ary, INT2FIX(r));
   rb_ary_push(ary, INT2FIX(c));
   return ary;
}

static VALUE
slsmg_fill_region(obj, r_r, r_c, r_dr, r_dc, r_ch)
VALUE obj;
VALUE r_r; 
VALUE r_c; 
VALUE r_dr; 
VALUE r_dc; 
VALUE r_ch;
{
   int r = NUM2INT(r_r);
   int c = NUM2INT(r_c);
   unsigned int dr = NUM2INT(r_dr);
   unsigned int dc = NUM2INT(r_dc);
   unsigned char ch = NUM2INT(r_ch);

   SLsmg_fill_region (r, c, dr, dc, ch);
   return Qnil;
}

static VALUE
slsmg_write_color_chars(VALUE obj, VALUE str, VALUE len)
{
   unsigned short *s;
   Check_Type(str, T_ARRAY);
   s = (unsigned short *) RARRAY(str)->ptr;
   SLsmg_write_color_chars (s, NUM2INT(len));
   return Qnil;
}

static VALUE
slsmg_read_raw(VALUE obj, VALUE l)
{
   unsigned short *buf;
   int res, len, i;
   VALUE bary, ary;

   len = NUM2INT(l);
   if (NULL == (buf = (unsigned short *) SLMALLOC (len))) {
      fprintf(stderr, "malloc error.\n");
      exit(-1);
   }

   res = SLsmg_read_raw (buf, len);
   bary = rb_ary_new2(len);
   for (i = 0; i < len; ++i)
     rb_ary_push(bary, INT2FIX(buf[i]));
   free(buf);
   ary = rb_ary_new2(2);
   rb_ary_push(ary, INT2FIX(res));
   rb_ary_push(ary, bary);
   return ary;
}

static VALUE
slsmg_write_raw(VALUE obj, VALUE b, VALUE l)
{
   unsigned short *buf;
   int res, len;
   Check_Type(b, T_ARRAY);

   len = NUM2INT(l);
   buf = (unsigned short *) RARRAY(b)->ptr;

   res = SLsmg_write_raw (buf, len);
   return INT2FIX(res);
}

static VALUE
slsmg_set_color_in_region(obj, r_color, r_r, r_c, r_dr, r_dc)
VALUE obj;
VALUE r_color;
VALUE r_r;
VALUE r_c;
VALUE r_dr;
VALUE r_dc;
{
   int color = NUM2INT(r_color);
   int r = NUM2INT(r_r);
   int c = NUM2INT(r_c);
   int dr = NUM2INT(r_dr);
   int dc = NUM2INT(r_dc);
   SLsmg_set_color_in_region(color, r, c, dr, dc);
   return Qnil;
}

static VALUE
slsmg_write_wrapped_string (obj, str,  r_r, r_c, 
				 r_dr, r_dc, 
				 r_fill)
VALUE obj;
VALUE str;
VALUE r_r;
VALUE r_c;
VALUE r_dr;
VALUE r_dc;
VALUE r_fill;
{
   char *s;
   unsigned int dc = NUM2INT(r_dc);
   int r = NUM2INT(r_r);
   int c = NUM2INT(r_c);
   unsigned int dr = NUM2INT(r_dr);
   int fill = NUM2INT(r_fill);

   Check_Type(str, T_STRING);
   s = RSTRING(str)->ptr;
   
   SLsmg_write_wrapped_string (s, r, c, dr, dc, fill);
   return Qnil;
}

static VALUE
slang_init_tty(obj, abort_char, no_flow_control, opost)
VALUE obj;
VALUE abort_char;
VALUE no_flow_control;
VALUE opost;
{
   SLang_init_tty (NUM2INT(abort_char), NUM2INT(no_flow_control), NUM2INT(opost));
   return Qnil;
}
int Mouse_on = 0;

static void reset_exit (int sig)
{
   SLsmg_gotorc (SLtt_Screen_Rows - 1, 0);
   SLsmg_refresh ();
   if (Mouse_on)
     SLtt_set_mouse_mode(0, 1);
   SLang_reset_tty ();
   SLsmg_reset_smg ();

   if (sig)
     {
	fprintf (stderr, "Exiting on signal %d\n", sig);
	exit (1);
     }
   exit (sig);
}

#ifdef SIGTSTP
static void sigtstp_handler (int sig)
{
   
   if (rb_respond_to(mSlang, rb_intern("slang_sigstp_handler_hook"))) {
      rb_funcall(mSlang, rb_intern("slang_sigstp_handler_hook"), 0);
   }
   if (Mouse_on)
     SLtt_set_mouse_mode(0, 1);
   SLsmg_suspend_smg ();
   kill (0, SIGSTOP);
}
#endif

#ifdef SIGCONT
static void sigcont_handler (int sig)
{
   if (Mouse_on)
     SLtt_set_mouse_mode(1, 1);
   SLsmg_resume_smg ();
   SLang_reset_tty ();
   SLang_init_tty (-1, 0, 1);
   SLtty_set_suspend_state (1);
   if (rb_respond_to(mSlang, rb_intern("slang_sigcont_handler_hook"))) {
      rb_funcall(mSlang, rb_intern("slang_sigcont_handler_hook"), 0);
   }
}
#endif

#ifdef SIGINT
static void sigint_handler (int sig)
{
   reset_exit (sig);
}
#endif

#define ruby_signal(sig,handle) posix_signal((sig),(handle))
static void init_signals (void)
{
#ifdef SIGTSTP
   SLsignal (SIGTSTP, sigtstp_handler);
#endif
#ifdef SIGCONT
   SLsignal (SIGCONT, sigcont_handler);
#endif
#ifdef SIGINT
   SLsignal (SIGINT, sigint_handler);
#endif
#ifdef SIGWINCH
   SLsignal_intr (SIGWINCH, sig_winch_handler);
#endif
}

static VALUE
sl_init_signals(void)
{
   init_signals();
   return Qnil;
}

static void init_colors (void)
{
   int i;
   char *fg, *bg;
   
   /* fg = color, bg = white */
   bg = "white";
   for (i = SL_BLACK; i <= SL_WHITE; ++i) {
      fg = Color_str[i];
      SLtt_set_color (SL_BG_WHITE+i, NULL, fg, bg);
   }

   /* fg = color, bg = lightgray */
   bg = "lightgray";
   for (i = SL_BLACK; i <= SL_WHITE; ++i) {
      fg = Color_str[i];
      SLtt_set_color (SL_BG_LGRAY+i, NULL, fg, bg);
   }

   /* fg = color, bg = blue */
   bg = "blue";
   for (i = SL_BLACK; i <= SL_WHITE; ++i) {
      fg = Color_str[i];
      SLtt_set_color (SL_BG_BLUE+i, NULL, fg, bg);
   }

   /* fg = black, bg = color */
   fg = "black";
   for (i = SL_BLACK; i <= SL_WHITE; ++i) {
      bg = Color_str[i];
      SLtt_set_color (SL_BG_COLORS+i, NULL, fg, bg);
   }

   /* normal color */
   SLtt_set_color (0, NULL, "black", "white");
   /* reverse color */
   SLtt_set_color (1, NULL, "white", "black");

}

static VALUE
sl_init_colors(void)
{
   init_colors();
   return Qnil;
}

static VALUE
sl_color_terminal(void)
{
   if (SLtt_Use_Ansi_Colors)
     return TRUE;
   return FALSE;
}

static VALUE
init_terminal(obj, t, s)
VALUE obj;
VALUE t;
VALUE s;
{
   int tty = NUM2INT(t);
   int smg = NUM2INT(s);

   SLsig_block_signals ();
   
   SLtt_get_terminfo ();
   
   /* SLkp_init assumes that SLtt_get_terminfo has been called. */
   if (tty && (-1 == SLkp_init ()))
     {
	SLsig_unblock_signals ();
	return FALSE;
     }
   
   init_signals ();
   
   if (tty) SLang_init_tty (-1, 0, 1);
   if (tty) SLtty_set_suspend_state (1);
   if (smg) SLsmg_init_smg ();

   SLsig_unblock_signals ();

   if (SLtt_Use_Ansi_Colors)
     init_colors();

   if (Mouse_on)
     SLtt_set_mouse_mode(1, 1);

   return TRUE;
}

static VALUE
sl_reset(VALUE obj)
{
   SLsmg_gotorc (SLtt_Screen_Rows - 1, 0);
   SLsmg_refresh ();
   if (Mouse_on)
     SLtt_set_mouse_mode(0, 1);
   SLang_reset_tty ();
   SLsmg_reset_smg ();
   return Qnil;
}

static VALUE
sl_input_pending(VALUE obj, VALUE n)
{
   int i;
   i = SLang_input_pending(NUM2INT(n));
   return INT2FIX(i);
}

static VALUE
slsmg_draw_hline(VALUE obj, VALUE n)
{
   SLsmg_draw_hline(NUM2INT(n));
   return Qnil;
}

static VALUE
slsmg_draw_vline(VALUE obj, VALUE n)
{
   SLsmg_draw_vline(NUM2INT(n));
   return Qnil;
}

static VALUE
slsmg_draw_box(VALUE obj, VALUE x1, VALUE y1, VALUE x2, VALUE  y2)
{
   SLsmg_draw_box(NUM2INT(x1), NUM2INT(y1), NUM2INT(x2), NUM2INT(y2));
   return Qnil;
}


static VALUE
slsmg_touch_lines(VALUE obj, VALUE r, VALUE c)
{
   SLsmg_touch_lines(NUM2INT(r), NUM2INT(c));
   return Qnil;
}

static VALUE
slkp_define_keysym(VALUE obj, VALUE kseq, VALUE code)
{
   
   Check_Type(kseq, T_STRING);
   (void) SLkp_define_keysym (RSTRING(kseq)->ptr, NUM2INT(code));
   return Qnil; 
}

int Mouse_Row, Mouse_Col;

static VALUE
sl_get_mouse_rc (void)
{
   VALUE ary;

   Mouse_Col = (unsigned char) SLang_getkey () - 32 - 1;
   Mouse_Row = (unsigned char) SLang_getkey () - 32 - 1;

   ary = rb_ary_new2(2);
   rb_ary_push(ary, INT2FIX(Mouse_Col));
   rb_ary_push(ary, INT2FIX(Mouse_Row));
   return ary;
}

static VALUE
sl_mouse_on(void)
{
   Mouse_on = 1;
   return Qnil;
}

static VALUE
sl_mouse_off(void)
{
   Mouse_on = 0;
   return Qnil;
}

static VALUE
slsmg_get_column(void)
{
   return INT2FIX(SLsmg_get_column());
}

static VALUE
slsmg_get_row(void)
{
   return INT2FIX(SLsmg_get_row());
}


static VALUE
sltt_set_color(VALUE obj, VALUE n, VALUE fg, VALUE bg)
{
   FIXNUM_P(n);
   Check_Type(fg, T_STRING);
   Check_Type(bg, T_STRING);
   SLtt_set_color (FIX2INT(n), NULL, RSTRING(fg)->ptr, RSTRING(bg)->ptr);
   return Qnil;
}

static VALUE
sltt_screen_rows(void)
{
   return INT2FIX(SLtt_Screen_Rows);
}

static VALUE
sltt_screen_cols(void)
{
   return INT2FIX(SLtt_Screen_Cols);
}


static VALUE
slang_cursor_on(void)
{
   (void) SLtt_set_cursor_visibility (1);
   SLtt_flush_output ();
   return Qnil;
}

static VALUE
slang_cursor_off(void)
{
   (void) SLtt_set_cursor_visibility (0);
   SLtt_flush_output ();
   return Qnil;
}
 
/* the following minibuffer functions are 
   retrieved from 'most' sources */

SLang_RLine_Info_Type *RLI;

   
static void rline_update (unsigned char *buf, int len, int col)
{
   SLsmg_gotorc (SLtt_Screen_Rows - 1, 0);
   SLsmg_write_nchars ((char *) buf, len);
   SLsmg_erase_eol ();
   SLsmg_gotorc (SLtt_Screen_Rows - 1, col);
   SLsmg_refresh ();
}

/* I want to use mouse left button click as RETURN key */   
static void mouse_cmd (void)
{
   /* Discard mouse info */
   (void) SLang_getkey ();  
   (void) SLang_getkey ();  
   (void) SLang_getkey ();
   
   (void) SLang_ungetkey ('\r');
}

static void g_abort (void)
{
   /* (void) SLang_getkey (); */
   (void) SLang_ungetkey ('\r');
   SLKeyBoard_Quit = 1;
/*   SLang_Error == SL_USER_BREAK; */
}

static void completion (void)
{
   VALUE comp_str;
   char *s;

   if (rb_respond_to(mSlang, rb_intern("completion"))) {
      comp_str = rb_funcall(mSlang, rb_intern("completion"), 1,
		 rb_str_new2(RLI->buf));
   }
   else comp_str = Qnil;

   if (comp_str != Qnil) {
      Check_Type(comp_str, T_STRING);
      s = RSTRING(comp_str)->ptr;
      strncpy ((char *) RLI->buf, s, LONG_STRING - 1);
      RLI->point = strlen (s);
      RLI->len = strlen (s);
   }
}

static SLang_RLine_Info_Type  *init_readline (void)
{
   unsigned char *buf = NULL;
   SLang_RLine_Info_Type *rli;

   if ((NULL == (rli = (SLang_RLine_Info_Type *) SLMALLOC (sizeof(SLang_RLine_Info_Type))))
       || (NULL == (buf = (unsigned char *) SLMALLOC (LONG_STRING))))
     {
        fprintf(stderr, "malloc error.\n");
        exit(-1);
     }

   SLMEMSET ((char *) rli, 0, sizeof (SLang_RLine_Info_Type));
   rli->buf = buf;
   rli->buf_len = LONG_STRING - 1;
   rli->tab = 8;
   rli->dhscroll = 20;
   rli->getkey = SLang_getkey;
   rli->tt_goto_column = NULL;
   rli->update_hook = rline_update;
   
   if (SLang_init_readline (rli) < 0)
     {
        SLang_exit_error ("Unable to initialize readline library.");
     }

   SLkm_define_key ("\033[M", (FVOID_STAR)mouse_cmd, rli->keymap);
   SLkm_define_key ("\011", (FVOID_STAR)completion, rli->keymap); /* tab */
   SLkm_define_key ("\007", (FVOID_STAR)g_abort, rli->keymap);

   return rli;
}

static int read_from_line(char *prompt, char *what, int noecho)
{
   int i;

   if (RLI == NULL)
     {
        RLI = init_readline ();
     }

   RLI->edit_width = SLtt_Screen_Cols - 1;
   RLI->prompt = prompt;
   *RLI->buf = 0;

   if (noecho)
     RLI->flags |= SL_RLINE_NO_ECHO;

   /* SLsmg_refresh (); */
   /* Last_Message = NULL; */
   /* Minibuffer_Selected = 1; */

   /* do not use default.  The up arrow can always get it back. */

   if (*what)
     {
        strncpy ((char *) RLI->buf, what, LONG_STRING - 1);
        RLI->point = strlen (what);
     }

   /* next routine returns -1 upon quit */
   i = SLang_read_line (RLI);
   if ((i > 0) && !SLang_Error && !SLKeyBoard_Quit)
     {
        SLang_rline_save_line (RLI);
        strncpy(what, (char *) RLI->buf, LONG_STRING - 1);
     }
   if (SLKeyBoard_Quit) /*  && SLang_Error == SL_USER_BREAK) */
     i = -1;
   SLang_Error = SLKeyBoard_Quit = 0;

   /* Minibuffer_Selected = 0; */

   /* if (i == -1) mini_message ("Aborted!", 1); */

   return(i);
}

static VALUE
sl_read_line(VALUE obj, VALUE prompt, VALUE init_val, VALUE noecho)
{
   char what[LONG_STRING];
   VALUE ary;
   int n;

   Check_Type(prompt, T_STRING);
   if (init_val != Qnil) {
      Check_Type(init_val, T_STRING);
      strncpy(what,  RSTRING(init_val)->ptr, LONG_STRING - 1);
   }
   else
     what[0] = 0;
   n = read_from_line(RSTRING(prompt)->ptr, what, (noecho != Qnil)?1:0);

   ary = rb_ary_new2(2);
   rb_ary_push(ary, INT2FIX(n));
   rb_ary_push(ary, rb_str_new2(what));
   return ary;
}


static VALUE
sl_set_slang_error(VALUE obj, VALUE n)
{
   SLang_Error = NUM2INT(n);
   return Qnil;
}

static VALUE
sl_set_slkeyboard_quit(VALUE obj, VALUE n)
{
   SLKeyBoard_Quit = NUM2INT(n);
   return Qnil;
}

static VALUE
slsmg_tab_width(void)
{
   return INT2FIX(SLsmg_Tab_Width);
}
/* -------------------- SLscroll_window class -------------------- */

#if 0 /* from slang.h */
typedef struct
{
   unsigned int flags;
   SLscroll_Type *top_window_line;   /* list element at top of window */
   SLscroll_Type *bot_window_line;   /* list element at bottom of window */
   SLscroll_Type *current_line;    /* current list element */
   SLscroll_Type *lines;               /* first list element */
   unsigned int nrows;                 /* number of rows in window */
   unsigned int hidden_mask;           /* applied to flags in SLscroll_Type */
   unsigned int line_num;              /* current line number (visible) */
   unsigned int num_lines;             /* total number of lines (visible) */
   unsigned int window_row;            /* row of current_line in window */
   unsigned int border;                /* number of rows that form scroll borde */
   int cannot_scroll;                  /* should window scroll or recenter */
}
SLscroll_Window_Type;
#endif

typedef struct _File_Line_Type
{
   struct _File_Line_Type *next;
   struct _File_Line_Type *prev;
   VALUE data;			       /* pointer to line data */
} File_Line_Type;

typedef struct _scrWdata {
   SLscroll_Window_Type *Line_Window;
   File_Line_Type *Lines;
   File_Line_Type *Last_Line;
   /* File_Line_Type *line; */
   int cursor_line_color;
   int normal_color;
   int bar;
   int tilde;
} scrWdata;

static void free_lines(scrWdata *scrWp)
{
   File_Line_Type *line, *next;

   line = scrWp->Lines;
   while (line != NULL) {
      next = line->next;
      free (line);
      line = next;
   }
}

static void
free_scrW (scrWdata *scrWp)
{
   if (scrWp->Line_Window)
     free(scrWp->Line_Window);

   free_lines(scrWp);
}

/* def new */
static VALUE
scrW_new(class)
    VALUE class;
{
   VALUE obj;
   scrWdata *scrWp;

   obj = Data_Make_Struct(class, scrWdata, 0, 
			   free_scrW, scrWp);

   scrWp->Line_Window = (SLscroll_Window_Type *)
     malloc(sizeof(SLscroll_Window_Type));
   if (NULL == scrWp->Line_Window)
     return Qnil;
   memset ((char *)scrWp->Line_Window, 0, sizeof (SLscroll_Window_Type));

   scrWp->Lines = NULL;
   scrWp->Last_Line = NULL;

   /* scrWp->line = NULL; */

   scrWp->cursor_line_color = SL_BG_COLORS + SL_CYAN; /* FG=BLACK BG=CYAN */
   scrWp->normal_color = SL_BG_LGRAY + SL_BLACK; /* FG=BLACK BG=LIGHTGRAY */

   scrWp->bar = 0;
   scrWp->tilde = 1;
   return obj;
}

/* def add_line(data) */
static VALUE
scrW_add_line(obj, data)
    VALUE obj;
    VALUE data;
{
   scrWdata *scrWp;
   File_Line_Type *line;
   
   line = (File_Line_Type *) malloc (sizeof (File_Line_Type));
   if (line == NULL) return INT2NUM((long)0);
   
   memset ((char *) line, sizeof (File_Line_Type), 0);
   
   line->data = data;
   
   Data_Get_Struct(obj, scrWdata, scrWp);

   if (scrWp->Lines == NULL) {
      scrWp->Lines = line;
      line->prev = NULL;
   }
   else { /* find last */
      scrWp->Last_Line->next = line;	
      line->prev = scrWp->Last_Line;
   }
   line->next = NULL;
   scrWp->Last_Line = line;
   return (VALUE)(((long)line) | FIXNUM_FLAG);
}

/* def insert_line(id, data) */
static VALUE
scrW_insert_line(obj, id, data)
    VALUE obj;
    VALUE id;
    VALUE data;
{
   scrWdata *scrWp;
   File_Line_Type *line;
   
   line = (File_Line_Type *) malloc (sizeof (File_Line_Type));
   if (line == NULL) return INT2NUM((long)0);
   
   memset ((char *) line, sizeof (File_Line_Type), 0);
   
   line->data = data;
   
   Data_Get_Struct(obj, scrWdata, scrWp);

   if (scrWp->Lines == NULL) {
      scrWp->Lines = line;
      line->prev = NULL;
      line->next = NULL;
      scrWp->Line_Window->top_window_line = (SLscroll_Type *) line;
      scrWp->Line_Window->lines = (SLscroll_Type *) line;
   }
   else { /* find id */
      File_Line_Type *p, *q;
      q = (File_Line_Type *)(id & ~FIXNUM_FLAG);
      for (p = scrWp->Lines; NULL != p->next; p = p->next)
	if (p == q) break;

      if (p != q)
	return INT2NUM((long)0);

      if (NULL == p->next) { /* add to last */
	 p->next = line;	
	 line->prev = p;
	 line->next = NULL;
	 scrWp->Last_Line = line;
      }
      else {
	 line->next = p->next;
	 line->prev = p;
	 p->next->prev = line;
	 p->next = line;	
      }
   }
   return (VALUE)(((long)line) | FIXNUM_FLAG);
}

/* def replace_line(id, data) */
static VALUE
scrW_replace_line(obj, id, data, cr)
    VALUE obj;
    VALUE id;
    VALUE data;
    VALUE cr;
{
   scrWdata *scrWp;
   File_Line_Type *line;
   File_Line_Type *p, *q;
   
   line = (File_Line_Type *) malloc (sizeof (File_Line_Type));
   if (line == NULL) return INT2NUM((long)0);
   
   memset ((char *) line, sizeof (File_Line_Type), 0);
   
   line->data = data;
   
   Data_Get_Struct(obj, scrWdata, scrWp);

   if (scrWp->Lines == NULL) {
      return INT2NUM((long)0);
   }

   /* find id */
   q = (File_Line_Type *)(id & ~FIXNUM_FLAG);
   for (p = scrWp->Lines; NULL != p->next; p = p->next)
     if (p == q) break;
   
   if (p != q)
     return INT2NUM((long)0);

   if (p == scrWp->Lines) { /* first */
      line->prev = NULL;
      line->next = p->next;
      if (NULL == p->next)
	scrWp->Last_Line = line;
      else
	p->next->prev = line;
      free(p);
      scrWp->Lines = line;
      /* because I modified the 1st entry ... */
      scrWp->Line_Window->lines = (SLscroll_Type *) line;
      scrWp->Line_Window->top_window_line = (SLscroll_Type *) line;
/*
      scrWp->Line_Window->current_line = (SLscroll_Type *) line;
*/
   } else if (NULL == p->next) { /* last */
      line->prev = p->prev;
      line->next = NULL;
      p->prev->next = line;
      scrWp->Last_Line = line;
      free(p);
   }
   else {
      line->prev = p->prev;
      line->next = p->next;
      p->prev->next = line;
      p->next->prev = line;
      free(p);
   }
   /* the following seems redundant, but necessary, why ? */
   if (scrWp->Line_Window->top_window_line == (SLscroll_Type *)p)
     scrWp->Line_Window->top_window_line = (SLscroll_Type *) line;
   /* if (NUM2INT(cr) == 0)
     scrWp->Line_Window->top_window_line = (SLscroll_Type *) line; */
   if (scrWp->Line_Window->current_line == (SLscroll_Type *)p)
     scrWp->Line_Window->current_line = (SLscroll_Type *) line;
   if (scrWp->Line_Window->bot_window_line == (SLscroll_Type *)p)
     scrWp->Line_Window->bot_window_line = (SLscroll_Type *) line;
   return (VALUE)(((long)line) | FIXNUM_FLAG);
}


/* def remove_line(id) */
static VALUE
scrW_remove_line(obj, id)
    VALUE obj;
    VALUE id;
{
   scrWdata *scrWp;
   
   Data_Get_Struct(obj, scrWdata, scrWp);

   if (scrWp->Lines == NULL) {
      return Qnil;
   }
   else { /* find id */
      File_Line_Type *p, *q;
      /* q = (File_Line_Type *) NUM2INT(id); */
      q = (File_Line_Type *)(id & ~FIXNUM_FLAG);
      for (p = scrWp->Lines; NULL != p->next; p = p->next)
	if (p == q) break;

      if (p != q)
	return Qnil;

      if (p == scrWp->Lines) { /* first */
	 p->next->prev = NULL;
	 scrWp->Lines = p->next;
	 if (scrWp->Line_Window->top_window_line == (SLscroll_Type *)p)
	   scrWp->Line_Window->top_window_line = (SLscroll_Type *) p->next;
	 free(p);
      } 
      else if (NULL == p->next) {
	 if (p == q) { /* remove last entry */
	    p->prev->next = NULL;
	    scrWp->Last_Line = p->prev;
	    if (scrWp->Line_Window->top_window_line == (SLscroll_Type *)p)
	      scrWp->Line_Window->top_window_line = NULL;
	    free (p);	    
	 }
	 else
	   return Qnil;
      }
      else {
	 p->next->prev = p->prev;
	 p->prev->next = p->next;
	 if (scrWp->Line_Window->top_window_line == (SLscroll_Type *)p)
	   scrWp->Line_Window->top_window_line = (SLscroll_Type *) p->next;
	 free (p);
      }
   }
   return TRUE;
}

/* def init_lines(n) */
static VALUE
scrW_init_lines(obj, n)
    VALUE obj;
    VALUE n;
{
   scrWdata *scrWp;

   Data_Get_Struct(obj, scrWdata, scrWp);

   scrWp->Line_Window->current_line = (SLscroll_Type *) scrWp->Lines;
   scrWp->Line_Window->lines = (SLscroll_Type *) scrWp->Lines;
   scrWp->Line_Window->line_num = 1;
   scrWp->Line_Window->num_lines = NUM2INT(n);
   return Qnil;
}

static VALUE
scrW_current_line(obj)
    VALUE obj;
{
   scrWdata *scrWp;
   File_Line_Type *line;

   Data_Get_Struct(obj, scrWdata, scrWp);

   line = (File_Line_Type *) scrWp->Line_Window->current_line;
   return line->data;
}

void erase_eol (int last_col, int put_bar)
{
   int len;
   
   len = last_col - SLsmg_get_column () + 1;
   if (len < 0) return;

   
   while (len-- > 0)
     SLsmg_write_char(' ');
   if (put_bar)
      SLsmg_write_char(BAR_CHAR);
}

static VALUE
slsmg_erase_eol2(obj, last_col, put_bar)
   VALUE obj;
   VALUE last_col;
   VALUE put_bar;
{
   int lc = NUM2INT(last_col);
   int bar = NUM2INT(put_bar);

   erase_eol(lc, bar);
   return Qnil;
}

static int expand_tab(char *s, int cur_col, char *buf, int buf_size)
{
   char *pp, *d, *d_end;
   int len;
   int tab_pos;

   tab_pos = SLsmg_Tab_Width;
   d_end = buf + buf_size;
   for (d = buf, pp = s; 
	d < d_end-1 && *pp != '\n' && *pp != '\0';)
     if ('\t' == *pp) {
	while (d - buf + cur_col >= tab_pos)
	  tab_pos += SLsmg_Tab_Width;
	while (pp < d_end-1 && ((d - buf + cur_col) < tab_pos))
	  *d++ = ' ';
	++pp;
     }
     else
        *d++ = *pp++;
   *d = '\0';

   len = strlen(buf);
   return len;
}

static VALUE
sl_tabbed_len(VALUE obj, VALUE str)
{
   int len;
   char buf[LONG_STRING];

   Check_Type(str, T_STRING);
   len = expand_tab(RSTRING(str)->ptr, 0, buf, LONG_STRING);
   return INT2FIX(len);
}

static VALUE
sl_expand_tab(VALUE obj, VALUE str)
{
   int len;
   char buf[LONG_STRING];

   Check_Type(str, T_STRING);
   len = expand_tab(RSTRING(str)->ptr, 0, buf, LONG_STRING);
   return rb_str_new2(buf);
}

#ifdef HAVE_KANJI_POS
#define KANJI_POS(c, code) kanji_pos((c), (code))
#else
/* this code is in Japanese version S-Lang library slkanji.c 
  The original version was written by Kikutani,
  But this is written by Katsuta-san or Yoshino-san ??
  I'm not sure 
*/
#define ASCII	0
#define	EUC	1
#define	JIS	2
#define	SJIS	3

static int iskanji(int c, int code) /*{{{*/
{
/*   if(!code) return FALSE; */
   c = (c & 0xff);
   if(code == SJIS)
     {
	if((0x80 < c && c < 0xa0) || (0xe0 <= c && c <= 0xfc))
	  return	TRUE;
     }
   else if(code == EUC)
     {
	if(0xa0 < c && c < 0xff)
	  return	TRUE;
	if(c == 0x8e) return TRUE;	/* fake */
     }
    else if(code == JIS)
     {
	if(0x20 < c && c < 0x7f)
	  return	TRUE;
     }
   return	FALSE;
}
static int Mykanji_pos(unsigned char *beg, unsigned char *pos) /*{{{*/
{
   unsigned char *p = beg;

   if((beg == pos) || !iskanji(*(pos-1), EUC))
     {
	if (iskanji(*pos, EUC))
	  return 1;	/* KNAJI 1st byte */
	else
	  return ASCII;	/* ASCII: 0 */
     }

   while(p < pos)
     {
	if (iskanji(*p, EUC))	p++;
	p++;
     }
   
   if(p != pos)	return (p - pos +1);
   if(iskanji(*p, EUC))	return 1;
   
   return ASCII;
}
#define KANJI_POS(c, code) Mykanji_pos((c), (code))
#endif

static void write_string(VALUE self, char *s, int off, int last_col, VALUE hlrex, int normal_color)
{
   char blank = ' ';
   char *p = s+off;
   int len, cur_col, col_width;
   char buf[LONG_STRING];
   VALUE array, *ary; 
   int ary_len = 0;

   if (strlen(s) < off) return;

   if (KANJI_POS(s, p) == 2) { /* Kanji second byte */
      SLsmg_write_nchars (&blank, 1);
      ++p;
   }

   cur_col = SLsmg_get_column ();

   /* convert tabs */
   len = expand_tab(p, cur_col, buf, LONG_STRING);

   /* it is necesary that calculating regexp array for expanded string */
   if (rb_respond_to(self, rb_intern("get_hlarray"))) {
      array = rb_funcall(self, rb_intern("get_hlarray"), 2,
		 rb_str_new2(buf), hlrex);
//		 (VALUE)(long)buf, Qnil); // hlrex);
   }
   else
     array = Qnil;

   if (array != Qnil) {
      ary = (VALUE *)RARRAY(array)->ptr;
      ary_len = RARRAY(array)->len;
   }

   col_width = last_col - cur_col + 1;
   if (col_width <= len)
     SLsmg_write_nchars (buf, col_width); /* maybe consider regexp here, too */
   else {
      if (ary_len > 0) {
	 int i, b, ln, bufp;
	 bufp = 0;
	 for (i = 0; i < ary_len; i++) {
	    VALUE *a;
	    int color;

	    /* in slang.rb/ def get_hlarray
	    pa << [line.index(s), s.length, color]
	     */
	    a = (VALUE *) RARRAY(ary[i])->ptr;
	    b = NUM2INT(a[0]);
	    ln = NUM2INT(a[1]);
	    color = NUM2INT(a[2]); 
	    
	    if (b - bufp > 0) {
	       SLsmg_set_color(normal_color); 
	       SLsmg_write_nchars (buf+bufp, b-bufp);
	    }
	    SLsmg_set_color(color);
	    SLsmg_write_nchars (buf+b, ln);
	    bufp = b + ln;
	 }
	 SLsmg_set_color(normal_color);
	 if (bufp < len)
	   SLsmg_write_nchars (buf+bufp, len-bufp);
      }
      else {
	   SLsmg_write_nchars (buf, len);
#if 0 /* this is done by erase_eol ?? */
      for (i = 0; i < col_width - len; ++i)
	SLsmg_write_nchars (&blank, 1);
#endif
      }
   }

#ifndef HAVE_KANJI_POS /* means not slang-ja */
   if (KANJI_POS(s, p) == 1) { /* Kanji first byte */
      SLsmg_write_char (' ');
   }
#endif
}

static VALUE
sl_write_string_with_offset(VALUE obj, VALUE self, VALUE s, VALUE off, VALUE ce, 
			    VALUE hlrex, VALUE ncolor)
{
   int normal_color;
   scrWdata *scrWp;

   Check_Type(s, T_STRING);
   normal_color = NUM2INT(ncolor);

   if (strlen(RSTRING(s)->ptr) >= LONG_STRING){ 
      static char buf[LONG_STRING];
      strncpy(buf, RSTRING(s)->ptr, LONG_STRING);
      write_string (self, buf, NUM2INT(off), NUM2INT(ce), hlrex, normal_color);
   }
   else
     write_string (self, RSTRING(s)->ptr, NUM2INT(off), NUM2INT(ce), hlrex, normal_color);
   return Qnil;
}


/* def update_region(n) */
static VALUE
scrW_update_region(obj, self, rbegin, rend, cbegin, 
		   cend, offset, cursor_line)
    VALUE obj;
    VALUE self;
    VALUE rbegin;
    VALUE rend;
    VALUE cbegin;
    VALUE cend;
    VALUE offset;
    VALUE cursor_line;
{
   int row;
   File_Line_Type *line;
   scrWdata *scrWp;
   int b = NUM2INT(rbegin);
   int e = NUM2INT(rend);
   int cb = NUM2INT(cbegin);
   int ce = NUM2INT(cend);
/*   int off = NUM2INT(offset); */
   int curline = NUM2INT(cursor_line);
   Data_Get_Struct(obj, scrWdata, scrWp);

   if (e <= b || ce <= cb) return FALSE;
   scrWp->Line_Window->nrows = e - b + 1;
   if (scrWp->Line_Window->top_window_line != NULL) {
      scrWp->Line_Window->current_line = scrWp->Line_Window->top_window_line;
      if (SLscroll_find_line_num (scrWp->Line_Window) < 0)
	return FALSE;
   }
   
   SLscroll_find_top (scrWp->Line_Window);

   row = b;
   line = (File_Line_Type *) scrWp->Line_Window->top_window_line;
      
   while (row < e) {
      SLsmg_gotorc (row, cb);
   

      if (SLtt_Use_Ansi_Colors) {
	 if (row == curline)
	   SLsmg_set_color(scrWp->cursor_line_color);
	 else
	   SLsmg_set_color(scrWp->normal_color);
      }
      else {
	 if (row == curline)
	   SLsmg_normal_video ();
	 else
	   SLsmg_reverse_video ();
      }

      if (line == NULL) {
	 if (scrWp->tilde != 0)
	   SLsmg_write_char ('~');
      }
      else {
	 if (rb_respond_to(self, rb_intern("write_line"))) {
	    rb_funcall(self, rb_intern("write_line"), 2,
		       (VALUE)(long)line->data, INT2NUM(row));
/*	    rb_funcall(self, rb_intern("write_line"), 2, 
		       line->data, INT2NUM(row)); */
	 }
	 else { /* assume string */
	    Check_Type((VALUE)line->data, T_STRING);
	    SLsmg_write_string (RSTRING(line->data)->ptr);
         }

/*	 if (strlen(line->data) <= off+1)
	   SLsmg_write_string (""); */

	 line = line->next;
      }
      erase_eol(ce, scrWp->bar);

#if 0
      if (row == curline) {
	 if (SLtt_Use_Ansi_Colors)
	   SLsmg_set_color(scrWp->normal_color);
	 else
	   SLsmg_normal_video ();
      }
#endif

      row++;
   }
   return TRUE;
}

static VALUE
scrW_main_loop(VALUE obj, VALUE self)
{
   VALUE res;
   int key;

   for (;;) {
      rb_funcall(self, rb_intern("check_winch"), 0);
      rb_funcall(self, rb_intern("update_display"), 0);
      res = Qfalse;
      key = SLkp_getkey();
      if (key == 'j')
	rb_funcall(self, rb_intern("forw_scr"), 0);
      else if (key == 'k')
	rb_funcall(self, rb_intern("back_scr"), 0);
      else
	res = rb_funcall(self, rb_intern("key_actions_call"), 1, 
		       INT2NUM(key));
      if (rb_ivar_get(self, rb_intern("@do_break")) == Qtrue)
	break;
   }
   return Qnil;
}
      
/*   SLsmg_normal_video (); */
   
static VALUE
scrW_pageup(obj)
    VALUE obj;
{
   scrWdata *scrWp;
   int res;

   Data_Get_Struct(obj, scrWdata, scrWp);
   res = SLscroll_pageup (scrWp->Line_Window);

   return INT2NUM(res);
}

static VALUE
scrW_pagedown(obj)
    VALUE obj;
{
   scrWdata *scrWp;
   int res;

   Data_Get_Struct(obj, scrWdata, scrWp);
   res = SLscroll_pagedown (scrWp->Line_Window);

   return INT2NUM(res);
}


static VALUE
scrW_prev_n(obj, n)
    VALUE obj;
    VALUE n;
{
   scrWdata *scrWp;
   int res;

   Data_Get_Struct(obj, scrWdata, scrWp);
   res = SLscroll_prev_n(scrWp->Line_Window, NUM2INT(n));
   scrWp->Line_Window->top_window_line = scrWp->Line_Window->current_line;
   return INT2NUM( res );
}

static VALUE
scrW_next_n(obj, n)
    VALUE obj;
    VALUE n;
{
   scrWdata *scrWp;
   int res;

   Data_Get_Struct(obj, scrWdata, scrWp);
   res = SLscroll_next_n(scrWp->Line_Window, NUM2INT(n));
   scrWp->Line_Window->top_window_line = scrWp->Line_Window->current_line;
   return INT2NUM( res );
}

static VALUE
scrW_top_window_line_is_current_line(obj)
    VALUE obj;
{
   scrWdata *scrWp;

   Data_Get_Struct(obj, scrWdata, scrWp);
   scrWp->Line_Window->top_window_line = scrWp->Line_Window->current_line;
   return Qnil;
}

static VALUE
scrW_nrows(obj)
    VALUE obj;
{
   int n;
   scrWdata *scrWp;

   Data_Get_Struct(obj, scrWdata, scrWp);
   n = scrWp->Line_Window->nrows;
   return INT2FIX(n);
}

static VALUE
scrW_line_num(obj)
    VALUE obj;
{
   int n;
   scrWdata *scrWp;

   Data_Get_Struct(obj, scrWdata, scrWp);
   n = scrWp->Line_Window->line_num;
   return INT2FIX(n);
}

static VALUE
scrW_get_cursor_line_color(obj)
    VALUE obj;
{
   scrWdata *scrWp;

   Data_Get_Struct(obj, scrWdata, scrWp);
   return INT2FIX(scrWp->cursor_line_color);
}

static VALUE
scrW_get_normal_color(obj)
    VALUE obj;
{
   scrWdata *scrWp;

   Data_Get_Struct(obj, scrWdata, scrWp);
   return INT2FIX(scrWp->normal_color);
}

static VALUE
scrW_num_lines(obj)
    VALUE obj;
{
   int n;
   scrWdata *scrWp;

   Data_Get_Struct(obj, scrWdata, scrWp);
   n = scrWp->Line_Window->num_lines;
   return INT2FIX(n);
}

static VALUE
scrW_window_row(obj)
    VALUE obj;
{
   int n;
   scrWdata *scrWp;

   Data_Get_Struct(obj, scrWdata, scrWp);
   n = scrWp->Line_Window->window_row;
   return INT2FIX(n);
}

static VALUE
scrW_set_nrows(obj, n)
    VALUE obj;
    VALUE n;
{
   scrWdata *scrWp;

   Data_Get_Struct(obj, scrWdata, scrWp);

   scrWp->Line_Window->nrows = NUM2INT(n);

   return Qnil;
}

static VALUE
scrW_set_cursor_line_color(obj, n)
    VALUE obj;
    VALUE n;
{
   scrWdata *scrWp;

   Data_Get_Struct(obj, scrWdata, scrWp);

   scrWp->cursor_line_color = NUM2INT(n);

   return Qnil;
}


static VALUE
scrW_set_normal_color(obj, n)
    VALUE obj;
    VALUE n;
{
   scrWdata *scrWp;

   Data_Get_Struct(obj, scrWdata, scrWp);

   scrWp->normal_color = NUM2INT(n);

   return Qnil;
}

static VALUE
scrW_set_bar(obj, n)
    VALUE obj;
    VALUE n;
{
   scrWdata *scrWp;

   Data_Get_Struct(obj, scrWdata, scrWp);

   scrWp->bar = NUM2INT(n);

   return Qnil;
}

static VALUE
scrW_set_tilde(obj, n)
    VALUE obj;
    VALUE n;
{
   scrWdata *scrWp;

   Data_Get_Struct(obj, scrWdata, scrWp);

   scrWp->tilde = NUM2INT(n);

   return Qnil;
}

/* -- used fo search -- */
static File_Line_Type *find_line = NULL;
/* def position_nth_line(n) */
static VALUE
scrW_find_set_nth_line(obj, n)
    VALUE obj;
    VALUE n;
{
   File_Line_Type *line;
   scrWdata *scrWp;
   int i;

   Data_Get_Struct(obj, scrWdata, scrWp);

   for (i = 0, line = (File_Line_Type *) scrWp->Line_Window->lines;
	line && i < NUM2INT(n); ++i, line = line->next)
     ;

   find_line = line;
   return Qnil;
}

static VALUE
scrW_find_next_line(void)
{
   File_Line_Type *next;

   if (find_line) {
      next = find_line->next;
      if (NULL == next)
	return FALSE;
      find_line = next;
      return TRUE;
   }
   return FALSE;
}

static VALUE
scrW_find_prev_line(void)
{
   File_Line_Type *prev;

   if (find_line) {
      prev = find_line->prev;
      if (NULL == prev)
	return FALSE;
      find_line = prev;
      return TRUE;
   }
   return FALSE;
}


#if 0
static VALUE
scrW_find_line(obj, cur_line)
    VALUE obj;
    VALUE cur_line;
{
   scrWdata *scrWp;
   File_Line_Type *line;
   VALUE argv[2];
   
   Data_Get_Struct(obj, scrWdata, scrWp);

   if (!rb_respond_to(self, rb_intern("find_line"))) {
      return Qnil;
   }

   if (scrWp->Lines == NULL) {
      return Qnil;
   }
   else { /* find id */
      File_Line_Type *p, *q;
      q = (File_Line_Type *)(cur_id & ~FIXNUM_FLAG);
      for (p = scrWp->Lines; NULL != p; p = p->next)
	;
      if (NULL == p)
	return Qnil;

      argv[0] = line->data;
      for (; NULL != p; p = p->next)
	;
	    rb_funcall2(self, rb_intern("write_line"), 2, argv);
   return FALSE;
}
#endif

static VALUE
scrW_find_current_line(void)
{
   if (find_line)
     return find_line->data;
   return Qnil;
}

#ifdef HAVE_ISKCODE /* slang for jed */
#include <slkanji.h>
#endif
/*------------------------- Initialization -------------------------*/
void
Init_slanglib()
{
   mSlang = rb_define_module("Slanglib");

   rb_define_module_function(mSlang, "sl_get_winch_flag", sl_get_winch_flag, 0);
   rb_define_module_function(mSlang, "sl_clr_winch_flag", sl_clr_winch_flag, 0);
   rb_define_module_function(mSlang, "sltt_get_screen_size", sltt_get_screen_size, 0);
   rb_define_module_function(mSlang, "slsig_block_signals", slsig_block_signals, 0);
   rb_define_module_function(mSlang, "slsmg_set_char_set", slsmg_set_char_set, 1);
   rb_define_module_function(mSlang, "slsmg_suspend_smg", slsmg_suspend_smg, 0);
   rb_define_module_function(mSlang, "slsmg_resume_smg", slsmg_resume_smg, 0);
   rb_define_module_function(mSlang, "slsmg_erase_eos", slsmg_erase_eos, 0);
   rb_define_module_function(mSlang, "slsmg_char_at", slsmg_char_at, 0);
   rb_define_module_function(mSlang, "slsmg_draw_object", slsmg_draw_object, 3);
   rb_define_module_function(mSlang, "slsmg_set_screen_start", slsmg_set_screen_start, 2);
   rb_define_module_function(mSlang, "slsmg_write_color_chars", slsmg_write_color_chars, 2);
   rb_define_module_function(mSlang, "slsmg_read_raw", slsmg_write_raw, 1);
   rb_define_module_function(mSlang, "slsmg_write_raw", slsmg_write_raw, 2);
   rb_define_module_function(mSlang, "slsmg_set_color_in_region", slsmg_set_color_in_region, 5);
   rb_define_module_function(mSlang, "slsmg_normal_video", slsmg_normal_video, 0);
   rb_define_module_function(mSlang, "slsmg_reverse_video", slsmg_reverse_video, 0);
   rb_define_module_function(mSlang, "slsmg_gotorc", slsmg_gotorc, 2);
   rb_define_module_function(mSlang, "slsmg_printf", slsmg_printf, -1);
   rb_define_module_function(mSlang, "slsmg_erase_eol", slsmg_erase_eol, 0);
   rb_define_module_function(mSlang, "slsmg_erase_eol2", slsmg_erase_eol2, 2);
   rb_define_module_function(mSlang, "slsmg_refresh", slsmg_refresh, 0);
   rb_define_module_function(mSlang, "slsig_unblock_signals", slsig_unblock_signals, 0);
   rb_define_module_function(mSlang, "slkp_getkey", slkp_getkey, 0);
   rb_define_module_function(mSlang, "slang_getkey", slang_getkey, 0);
   rb_define_module_function(mSlang, "sl_reset", sl_reset, 0);
   rb_define_module_function(mSlang, "sltt_set_color", sltt_set_color, 3);
   rb_define_module_function(mSlang, "sltt_beep", sltt_beep, 0);
   rb_define_module_function(mSlang, "slkp_define_keysym", slkp_define_keysym, 2);
   rb_define_module_function(mSlang, "sltt_screen_rows", sltt_screen_rows, 0);
   rb_define_module_function(mSlang, "sltt_screen_cols", sltt_screen_cols, 0);
   rb_define_module_function(mSlang, "slsmg_write_string", slsmg_write_string, 1);
   rb_define_module_function(mSlang, "slsmg_write_wrapped_string", slsmg_write_wrapped_string, 6);
   rb_define_module_function(mSlang, "slsmg_set_color", slsmg_set_color, 1);
   rb_define_module_function(mSlang, "slsmg_forward", slsmg_forward, 1);
   rb_define_module_function(mSlang, "sl_color_terminal?", sl_color_terminal, 0);
   rb_define_module_function(mSlang, "slsmg_write_nchars", slsmg_write_nchars, 2);
   rb_define_module_function(mSlang, "slsmg_write_char", slsmg_write_char, 1);
   rb_define_module_function(mSlang, "sl_mouse_on", sl_mouse_on, 0);
   rb_define_module_function(mSlang, "sl_mouse_off", sl_mouse_off, 0);
   rb_define_module_function(mSlang, "sl_get_mouse_rc", sl_get_mouse_rc, 0);
   rb_define_module_function(mSlang, "slsmg_cls", slsmg_cls, 0);
   rb_define_module_function(mSlang, "slsmg_draw_hline", slsmg_draw_hline, 1);
   rb_define_module_function(mSlang, "slsmg_draw_vline", slsmg_draw_vline, 1);
   rb_define_module_function(mSlang, "slsmg_draw_box", slsmg_draw_box, 4);
   rb_define_module_function(mSlang, "slsmg_touch_lines", slsmg_touch_lines, 2);
   rb_define_module_function(mSlang, "slsmg_get_column", slsmg_get_column, 0);
   rb_define_module_function(mSlang, "slsmg_tab_width", slsmg_tab_width, 0);
   rb_define_module_function(mSlang, "sl_tabbed_len", sl_tabbed_len, 1);
   rb_define_module_function(mSlang, "sl_expand_tab", sl_expand_tab, 1);
   rb_define_module_function(mSlang, "slsmg_get_row", slsmg_get_row, 0);
   rb_define_module_function(mSlang, "slang_reset_tty", slang_reset_tty, 0);
   rb_define_module_function(mSlang, "slsmg_reset_smg", slsmg_reset_smg, 0);
   rb_define_module_function(mSlang, "slsmg_init_smg", slsmg_init_smg, 0);
   rb_define_module_function(mSlang, "slang_init_tty", slang_init_tty, 3);
   rb_define_module_function(mSlang, "sltt_get_terminfo", sltt_get_terminfo, 0);
   rb_define_module_function(mSlang, "slkp_init", slkp_init, 0);
   rb_define_module_function(mSlang, "sltt_set_mouse_mode", sltt_set_mouse_mode, 2);
   rb_define_module_function(mSlang, "sltty_set_suspend_state", sltty_set_suspend_state, 1);
   rb_define_module_function(mSlang, "sl_init_signals", sl_init_signals, 0);
   rb_define_module_function(mSlang, "sl_init_colors", sl_init_colors, 0);
   rb_define_module_function(mSlang, "slang_cursor_on", slang_cursor_on, 0);
   rb_define_module_function(mSlang, "slang_cursor_off", slang_cursor_off, 0);
   rb_define_module_function(mSlang, "sl_read_line", sl_read_line, 3);
   rb_define_module_function(mSlang, "sl_input_pending", sl_input_pending, 1);
   rb_define_module_function(mSlang, "sl_set_slang_error", sl_set_slang_error, 1);
   rb_define_module_function(mSlang, "sl_set_slkeyboard_quit", sl_set_slkeyboard_quit, 1);
   rb_define_module_function(mSlang, "sl_write_string_with_offset", sl_write_string_with_offset, 6);
   rb_define_module_function(mSlang, "slsmg_fill_region", slsmg_fill_region, 5);

/*   rb_define_module_function(mSlang, "", , 0); */

#if 0 /* not yet implemented */
extern void SLsmg_vprintf (char *, va_list);
extern int SLsmg_Scroll_Hash_Border;
extern int SLsmg_Display_Eight_Bit;
#endif

   rb_define_const(mSlang, "SL_KEY_ERR", INT2FIX(SL_KEY_ERR));
   rb_define_const(mSlang, "SL_KEY_UP", INT2FIX(SL_KEY_UP));
   rb_define_const(mSlang, "SL_KEY_DOWN", INT2FIX(SL_KEY_DOWN));
   rb_define_const(mSlang, "SL_KEY_LEFT", INT2FIX(SL_KEY_LEFT));
   rb_define_const(mSlang, "SL_KEY_RIGHT", INT2FIX(SL_KEY_RIGHT));
   rb_define_const(mSlang, "SL_KEY_PPAGE", INT2FIX(SL_KEY_PPAGE));
   rb_define_const(mSlang, "SL_KEY_NPAGE", INT2FIX(SL_KEY_NPAGE));
   rb_define_const(mSlang, "SL_KEY_HOME", INT2FIX(SL_KEY_HOME));
   rb_define_const(mSlang, "SL_KEY_END", INT2FIX(SL_KEY_END));
   rb_define_const(mSlang, "SL_KEY_A1", INT2FIX(SL_KEY_A1));
   rb_define_const(mSlang, "SL_KEY_A3", INT2FIX(SL_KEY_A3));
   rb_define_const(mSlang, "SL_KEY_B2", INT2FIX(SL_KEY_B2));
   rb_define_const(mSlang, "SL_KEY_C1", INT2FIX(SL_KEY_C1));
   rb_define_const(mSlang, "SL_KEY_C3", INT2FIX(SL_KEY_C3));
   rb_define_const(mSlang, "SL_KEY_REDO", INT2FIX(SL_KEY_REDO));
   rb_define_const(mSlang, "SL_KEY_UNDO", INT2FIX(SL_KEY_UNDO));
   rb_define_const(mSlang, "SL_KEY_BACKSPACE", INT2FIX(SL_KEY_BACKSPACE));
   rb_define_const(mSlang, "SL_KEY_ENTER", INT2FIX(SL_KEY_ENTER));
   rb_define_const(mSlang, "SL_KEY_IC", INT2FIX(SL_KEY_IC));
   rb_define_const(mSlang, "SL_KEY_DELETE", INT2FIX(SL_KEY_DELETE));
   rb_define_const(mSlang, "SL_KEY_F0", INT2FIX(SL_KEY_F0));
   rb_define_const(mSlang, "SL_KEY_F1", INT2FIX(SL_KEY_F(1)));
   rb_define_const(mSlang, "SL_KEY_F2", INT2FIX(SL_KEY_F(2)));
   rb_define_const(mSlang, "SL_KEY_F3", INT2FIX(SL_KEY_F(3)));
   rb_define_const(mSlang, "SL_KEY_F4", INT2FIX(SL_KEY_F(4)));
   rb_define_const(mSlang, "SL_KEY_F5", INT2FIX(SL_KEY_F(5)));
   rb_define_const(mSlang, "SL_KEY_F6", INT2FIX(SL_KEY_F(6)));
   rb_define_const(mSlang, "SL_KEY_F7", INT2FIX(SL_KEY_F(7)));
   rb_define_const(mSlang, "SL_KEY_F8", INT2FIX(SL_KEY_F(8)));
   rb_define_const(mSlang, "SL_KEY_F9", INT2FIX(SL_KEY_F(9)));
   rb_define_const(mSlang, "SL_KEY_F10", INT2FIX(SL_KEY_F(10)));
   rb_define_const(mSlang, "SL_KEY_F11", INT2FIX(SL_KEY_F(11)));
   rb_define_const(mSlang, "SL_KEY_F12", INT2FIX(SL_KEY_F(12)));

   rb_define_const(mSlang, "SL_BG_WHITE", INT2FIX(SL_BG_WHITE));
   rb_define_const(mSlang, "SL_BG_LGRAY", INT2FIX(SL_BG_LGRAY));
   rb_define_const(mSlang, "SL_BG_BLUE", INT2FIX(SL_BG_BLUE));
   rb_define_const(mSlang, "SL_BG_COLORS", INT2FIX(SL_BG_COLORS));
   rb_define_const(mSlang, "SL_USER_COLOR", INT2FIX(SL_USER_COLOR));
   rb_define_const(mSlang, "SL_BLACK", INT2FIX(SL_BLACK));
   rb_define_const(mSlang, "SL_RED", INT2FIX(SL_RED));
   rb_define_const(mSlang, "SL_GREEN", INT2FIX(SL_GREEN));
   rb_define_const(mSlang, "SL_BROWN", INT2FIX(SL_BROWN));
   rb_define_const(mSlang, "SL_BLUE", INT2FIX(SL_BLUE));
   rb_define_const(mSlang, "SL_MAGENTA", INT2FIX(SL_MAGENTA));
   rb_define_const(mSlang, "SL_CYAN", INT2FIX(SL_CYAN));
   rb_define_const(mSlang, "SL_LIGHTGRAY", INT2FIX(SL_LIGHTGRAY));
   rb_define_const(mSlang, "SL_GRAY", INT2FIX(SL_GRAY));
   rb_define_const(mSlang, "SL_BRIGHTRED", INT2FIX(SL_BRIGHTRED));
   rb_define_const(mSlang, "SL_BRIGHTGREEN", INT2FIX(SL_BRIGHTGREEN));
   rb_define_const(mSlang, "SL_YELLOW", INT2FIX(SL_YELLOW));
   rb_define_const(mSlang, "SL_BRIGHTBLUE", INT2FIX(SL_BRIGHTBLUE));
   rb_define_const(mSlang, "SL_BRIGHTMAGENTA", INT2FIX(SL_BRIGHTMAGENTA));
   rb_define_const(mSlang, "SL_BRIGHTCYAN", INT2FIX(SL_BRIGHTCYAN));
   rb_define_const(mSlang, "SL_WHITE", INT2FIX(SL_WHITE));

   cScrW = rb_define_class_under(mSlang, "Scroll_window", rb_cObject);
   rb_define_singleton_method(cScrW, "new", scrW_new, 0);
   rb_define_method(cScrW, "add_line", scrW_add_line, 1);
   rb_define_method(cScrW, "insert_line", scrW_insert_line, 2);
   rb_define_method(cScrW, "replace_line", scrW_replace_line, 3);
   rb_define_method(cScrW, "remove_line", scrW_remove_line, 1);
   rb_define_method(cScrW, "init_lines", scrW_init_lines, 1);
   rb_define_method(cScrW, "update_region", scrW_update_region, 7);
   rb_define_method(cScrW, "main_loop", scrW_main_loop, 1);
   rb_define_method(cScrW, "scroll_prev_n", scrW_prev_n, 1);
   rb_define_method(cScrW, "scroll_next_n", scrW_next_n, 1);
   rb_define_method(cScrW, "scroll_pageup", scrW_pageup, 0);
   rb_define_method(cScrW, "scroll_pagedown", scrW_pagedown, 0);
   rb_define_method(cScrW, "nrows", scrW_nrows, 0);
   rb_define_method(cScrW, "line_num", scrW_line_num, 0);
   rb_define_method(cScrW, "num_lines", scrW_num_lines, 0);
   rb_define_method(cScrW, "window_row", scrW_window_row, 0);
   rb_define_method(cScrW, "top_window_line_is_current_line", scrW_top_window_line_is_current_line, 0);
   rb_define_method(cScrW, "set_nrows", scrW_set_nrows, 1);
   rb_define_method(cScrW, "set_cursor_line_color", scrW_set_cursor_line_color, 1);
   rb_define_method(cScrW, "set_normal_color", scrW_set_cursor_line_color, 1);
   rb_define_method(cScrW, "set_bar", scrW_set_bar, 1);
   rb_define_method(cScrW, "set_tilde", scrW_set_tilde, 1);
   rb_define_method(cScrW, "current_line", scrW_current_line, 0);
   rb_define_method(cScrW, "find_set_nth_line", scrW_find_set_nth_line, 1);
   rb_define_method(cScrW, "find_current_line", scrW_find_current_line, 0);
   rb_define_method(cScrW, "find_prev_line", scrW_find_prev_line, 0);
   rb_define_method(cScrW, "find_next_line", scrW_find_next_line, 0);
   rb_define_method(cScrW, "get_cursor_line_color", scrW_get_cursor_line_color, 0);
   rb_define_method(cScrW, "get_normal_color", scrW_get_normal_color, 0);

   /* Init_slcurses(mSlang); */
#ifdef HAVE_ISKCODE /* slang for jed */
   kSLcode = EUC;
#endif
}
