#include "CURcurses.h"
#include "Malloc.h"
#include "signal.h"

#if !defined(mips) && !defined(sequent) && !defined(n16)
#include <stdlib.h>
#endif

/*
 * Initialize data space for various screen information
 */

CursesObj *
CURnew()
{
     CursesObj *cur;

     cur = (CursesObj *) malloc(sizeof(CursesObj));

     cur->Screen       = NULL;
     cur->Termtype     = STRnew();
     cur->Clearscreen  = STRnew();
     cur->AudibleBell  = STRnew();
     cur->Highlighton  = STRnew();
     cur->Highlightoff = STRnew();

     cur->inCurses     = FALSE;
     cur->sigtstp      = (void*)-1;
     cur->sigwinch     = (void*)-1;

     CURinit(cur);
     
     return(cur);

}


/*
 * Initialize various strings and such. 
 */

void
CURinit(cur)
  CursesObj *cur;
{
     int err;
     static char terminal[1024];
     static char capabilities[1024];   /* String for cursor motion */
     static char *ptr = capabilities;  /* for buffering         */
     char *cp;


     /*** Set the terminal type ***/
     if (getenv("TERM") != NULL)
	  CURsetTerm(cur, getenv("TERM"));
     else 
	  CURsetTerm(cur, "unknown");

     err = tgetent(terminal, CURgetTerm(cur));
     
     if (err !=1)
	  CURsetTerm(cur, "unknown");

     /*** Get the clearscreen code ***/
     if ((cp = (char *)tgetstr("cl", &ptr)) != NULL)
	  CURsetCLS(cur, cp);
     else
	  CURsetCLS(cur, "");

     /*** Set the bell ***/
     if ((cp = (char *) tgetstr("bl", &ptr)) != NULL)
	  CURsetBell(cur, cp);
     else
	  CURsetBell(cur, "\007");

     /*** Set the highlight codes ***/
     if ((cp = (char *) tgetstr("so", &ptr)) != NULL) {
	  CURsetHighon(cur, cp);
	  if ((cp = (char *) tgetstr("se", &ptr)) != NULL)
	       CURsetHighoff(cur, cp);
     } else {
	  CURsetHighon(cur, "");
	  CURsetHighoff(cur, "");
     }
     CURsetScreen(cur,initscr());
     cur->inCurses = FALSE;
     
}


/* 
 * Given a properly "CURnew" cursesobj, initialize the screen..
 */

void
CURenter(cur)
  CursesObj *cur;
{
     /* for safety */
     if (cur->inCurses == TRUE)
	  return;

     tputs(CURgetCLS(cur),1,CURoutchar);
     fflush(stdout);

     cur->inCurses = TRUE;

     cbreak();
     noecho();
/*     nonl();*/
#ifdef SYSVCURSES
     intrflush(stdscr, FALSE);
     nodelay(stdscr, FALSE);
#ifndef ultrix			/** Causes wgetch to dump core in ultrix **/
     keypad(stdscr, TRUE);
#endif
#endif
#ifdef SIGWINCH
     if (cur->sigwinch != (void*)-1)
	  signal(SIGWINCH, cur->sigwinch);
#endif
     if (cur->sigtstp != (void*)-1)
	  signal(SIGTSTP, cur->sigtstp);
}


/*
 * Exit curses system.
 */

void
CURexit(cur)
  CursesObj *cur;
{
     
     echo();
     endwin();

#ifdef SYSVCURSES
     keypad(stdscr, FALSE);
#endif
     tputs(CURgetCLS(cur),1,CURoutchar);     
     fflush(stdout);

     if (cur->inCurses) {
	  cur->inCurses = FALSE;
	  cur->sigtstp = signal(SIGTSTP, SIG_DFL);

#ifdef SIGWINCH
	  cur->sigwinch = signal(SIGWINCH, SIG_DFL);
#endif
     }

}



/*
 * send a character to stdout, not really curses, but we do use it...
 */

int
CURoutchar(c)
  char c;
{
     /** output the given character.  From tputs... **/
     /** Note: this CANNOT be a macro!              **/
     
     putc(c, stdout);
     return(c);
}


/*
 * Centerline, uses curses routines to center a line.
 */
void CURcenterline(cur, theline, yval)
  CursesObj *cur;
  char      *theline;
  int       yval;
{
     mvaddstr(yval, (COLS - strlen(theline))/2, theline);
}


/*
 * CURgetstr is a replacement of getstr that allows editing of the string
 *
 * if the user aborts (ctrl-g) then -1 is returned.
 */

int
CURgetstr(cur, inputline)
  CursesObj *cur;
  char      *inputline;
{
     int pointer = 0;
     int ch;

     cbreak();
     noecho();     

     /*** Check to see if there's something in the inputline already ***/
     
     while (inputline[pointer] != '\0') {
	  addch(inputline[pointer]);
	  pointer ++;
     }

     refresh();

     for (;;) {
	  ch = CURgetch(cur);

	  switch (ch) {

	  case '\n':
	  case '\r':
	  case '\t':
	  case KEY_ENTER:
	       inputline[pointer] = '\0';
	       return(ch);
	       break;

	  /**  Backspace and delete **/

	  case '\010':
	  case '\177':
	       if (pointer > 0) {
		    addch('\010');
		    addch(' ');
		    addch('\010');
	       
		    inputline[--pointer] = '\0';
		    refresh();
	       }
	       break;

	  case '\007':
	       CURBeep(cur);
	       return(-1);
	       break;

	  default:
	       if (isprint(ch)) {
		    inputline[pointer++]= ch;
		    addch(ch);
		    refresh();
	       }
	       else
		    return(ch);
	  }
     }
}


/*
 * This stuff is stolen and modified from hytelnet  Thanks Earl!
 */

int
CURgetch(cur)
  CursesObj *cur;
{
    int a, b, c;

    c = wgetch(stdscr);

    if (c == 27) {      /* handle escape sequence */
        b = getch();
        if (b == '[' || b == 'O')
            a = getch();
        else
            a = b;

        switch (a) {
        case 'A': c = KEY_UP; break;
        case 'B': c = KEY_DOWN; break;
        case 'C': c = KEY_RIGHT; break;
        case 'D': c = KEY_LEFT; break;
        case '5':                       /* vt 300 prev. screen */
            if (b == '[' && getch() == '~')
                c = KEY_PPAGE;
            break;
        case '6':                       /* vt 300 next screen */
            if (b == '[' && getch() == '~')
                c = KEY_NPAGE;
            break;
        }
   }
   if ((c == KEY_ENTER)|| (c=='\r')) 
	c = '\n'; /** SYSV curses Gack! **/

   return(c);
}

/*
 * Resets the screen when a size change has happened
 */

void
CURresize(cur)
  CursesObj *cur;
{
     if (cur->inCurses) {
	  CURexit(cur);
	  CURsetScreen(cur, initscr());
	  CURenter(cur);
     }
}

/*
 * Get one option displays a message, and gets a response
 *
 * If the Response has something in it, it is displayed and editable
 * 
 * If the user wants to abort, GetOneOption returns a -1, otherwise it
 * returns a 0
 */

int
CURGetOneOption(cur, OptionName, Response)
  CursesObj *cur;
  char *OptionName, *Response;
{
     int i;
     
     mvaddstr(LINES-1, 0, OptionName);
     clrtoeol();
     refresh();
     echo();
     i = CURgetstr(cur, Response);
     noecho();
     
     return(i);
}

/*
 * Fills in the Response with either a lowercase 'y' or 'n'
 */

void
CURgetYesorNo(cur, OptionName, Response)
  CursesObj *cur;
  char *OptionName, *Response;
{
     int c;
     int posx, posy;

     mvaddstr(LINES-1, 0, OptionName);
     clrtoeol();
     noecho();
     getyx(cur->Screen, posy, posx);
     addch(' ');

     if (*Response == 'y')
	  mvaddstr(posy, posx+1, "y");
     else {
	  *Response = 'n';
	  mvaddstr(posy, posx+1, "n ");
     }
     move(posy, posx+1);

     refresh();

     while (1) {
	  c = CURgetch(cur);

	  if (c == 'y') {
	       mvaddstr(posy, posx+1, "Yes");
	       move(posy, posx+1);
	       refresh();
	       *Response = 'y';
	       *(Response +1) = '\0';
	       return;
	  }
	  else if (c == 'n') {
	       mvaddstr(posy, posx+1, "No ");
	       move(posy, posx+1);
	       refresh();
	       *Response = 'n';
	       *(Response +1) = '\0';
	       return;
	  }
	  
	  else if ((c == '\n')||(c=='\r')) {
	       return;
	  }
	  else {
	       CURBeep(cur);
	  }
     }
}
	  
void 
CURBeep(cur)
  CursesObj *cur;
{
#ifdef SYSVCURSES
     beep();
#else
     tputs(CURgetBell(cur),1,CURoutchar);
     fflush(stdout);
#endif
}
