/* program: dtio.c
   purpose: source file for direct-terminal I/O functions
            supporting both Borland C and *NIX platforms
   author:  danny abesdris
   date:    january 16, 2006 (modified january 19, 23, 2006)
*/

// for quick cerr << ... debugging:
//#include <iostream>
//using namespace std;


// see three-semicolon mark


#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "dtio.h"

/* screen initialization routines */
void dt_start(void)
{
        /* none required for DOS */
#if PLATFORM == AIX
        initscr();
        noecho();
        cbreak();
        keypad(stdscr, 1);
#endif
}

/* terminate screen i/o routines */
void dt_stop(void)
{
#if PLATFORM == AIX
        refresh();
        endwin();
#else
        clrscr();
#endif
}

int dt_rows(void)
{
        int rc;
#if PLATFORM == AIX
        rc = LINES;             /* LINES is defined in <curses.h> */
#else
        struct text_info si;    /* struct text_info defined in <conio.h> */
        gettextinfo(&si);
        rc = si.screenheight;
#endif
        return rc;
}

int dt_cols(void)
{
        int rc;
#if PLATFORM == AIX
        rc = COLS;              /* this line re-added (guess based on prev fn) */
#else
        struct text_info si;
        gettextinfo(&si);
        rc = si.screenwidth;
#endif
        return rc;
}

void dt_clear(void)
{
#if PLATFORM == AIX
        erase();
#else
        clrscr();
#endif
}

/* to allow screen to be updated in UNIX */
void dt_update(void)
{
        /* once again, nothing required in DOS */
#if PLATFORM == AIX
        refresh();
#endif
}

int dt_getkey(void)
{
        int key;
#if PLATFORM == AIX
        refresh();              /* capturing key press, but refreshing screen */
        /* first on UNIX */
        key = getch();
#else
        key = getch();          /* adding 1000 plus second getch( ) value */
        /* to allow for unique return value in DOS */
        /* when the extended keys are used */
        key = (key == 0) ? 1000 + getch() : key;
#endif
        return key;
}

void dt_cursor(int row, int col)
{
#if PLATFORM == AIX
        move(row, col);         /* upper left coords (0,0) on UNIX and (1,1) on PC */
#else                           /* note also how row/column are inversed PC/UNIX */
        gotoxy(col + 1, row + 1);
#endif
}

// Put a char at the current cursor location.
//
// Common error: Calling plain putchar by accident.
// This is called dt_putchar. Plain putchar is part of stdio.
// If called by accident in curses mode, plain putchar usu. prints offscreen.
void dt_putchar(char c)
{
#if PLATFORM == AIX
        addch(c);
#else
        putch(c);
#endif
}

void dt_putstring(char *s)
{
#if PLATFORM == AIX
        addstr(s);
#else
        cputs(s);
#endif
}

/* this edit function displays the string 's' on row 'row' */ // "edit"?
/* and column 'col' of the screen and allows a maximum of */
/* 'len' characters  to be displayed in this 'field' */

void dt_draw(char *s, int row, int col, int len)
{
     int i;

     dt_cursor(row, col);

     for (i = 0; s[i] != '\0' && i < len; i++) {
          dt_putchar(s[i]);
     }
}

/* algorithm:
 * note: this is old and contains mistaken switched variable names etc.
 * also, it is not complete.
 * please do not look at it.
for $i (0 .. h - 1) {
  if (0 == $i || h - 1 == $i) { $edgerow = 1 } else { $edgerow = 0 }
        for $j (0 .. l - 1) {
            if (0 == $j) {
                if (0 == $j || h - 1 == $j) { $edgecol = 1 } else { $edgecol = 0 }
*/

int dt_box(int row,     // starting row
           int col,
           int w,       // width of box
           int h,       // height
           char corner, // corner char, e.g. '+'
           char top_btm,        // top and bottom edge char, e.g. '-'
           char ls_rs) {        // left and right edge char, e.g. '|'

        int edgerow, edgecol; /* used in for loops below */
        int i, j; /* loop counters */
        // note, this isnt cleared after each j-iteration, its just overwritten.
        // the "+ 1" bit is for the null.

        // was: char rowstr[w + 1]; 
        char* rowstr = malloc((w + 1) * sizeof(char));

        // Check that the box will fit onscreen.
        // FIXMEp3: Maybe the lower-right corner isn't safe. main.c test 1 doesn't draw in it.
        if (row + h > dt_rows() || col + w > dt_cols()) {
                return 0; // returns false
        }

        for (i = 0; i < h; i++) { // 70%
                /* check if this is an edge row (top or bottom row) */
                if (0 == i || i == h - 1) {
                        edgerow = 1;
                } else {
                        edgerow = 0;
                }
                for (j = 0; j < w; j++) {
                        /* is this an edge column (left- / right-most)? */
                        if (0 == j || j == w - 1) {
                                edgecol = 1;
                        } else {
                                edgecol = 0;
                        } /* if */
                        if (edgerow) {
                                rowstr[j] = edgecol ? corner : top_btm;
                        } else {
                                rowstr[j] = edgecol ? ls_rs : ' ';
                        } /* if */
                } /* for j */
                dt_draw(rowstr, // should I've passed a pointer to this instead?
                        row + i, col, w);
        } /* for i */

        return 1; // returns true

} /* fn dt_box */

int dt_edit(char *s, int slen, int row, int col, int flen, int *start,
            int *offset, int *insert, int ctype) {

        int key;        // holds the key the user types inside our loop
        int i;          // loop variable
        int pos;        // ?cursor position on an absolute basis (ie. considering the value of col)
        int moves;

        char* olds = malloc((slen + 1) * sizeof(char));
        (void) strcpy(olds, s);

        //;;;mark

        dt_draw(s, row, col, flen);
        pos = col + MIN(strlen(s), flen);

        do {
                key = dt_getkey();

                //cerr << "\n" << key << "\n" << KEY_ENTER << "\n";

                switch (key) {

                case LEFT:
                        if (pos > col) {
                                pos--;
                                dt_cursor(row, pos);
                        } // FIXME_P3 else beep (here and also in case RIGHT and case BS)?
                        break;

                case RIGHT:
                        // strlen seems to return a size_t, but that's ok I think
                        if (pos < col + strlen(s)) {
                                pos++;
                                dt_cursor(row, pos);
                        }
                        break;

                case BS:        /* complete */
                        if (pos > col) {
                                moves = strlen(s) - (pos - col) + 1;
                                i = pos - col;
                                while (moves > 0) {
                                        s[i - 1] = s[i];
                                        i++;
                                        moves--;
                                }

                                dt_cursor(row, col);

                                for (i = 0; s[i] != '\0' && i < flen; i++) {
                                        dt_putchar(s[i]);
                                }
                                for (; i <= flen; i++) {
                                        dt_putchar(' ');
                                }
                                pos--;
                                dt_cursor(row, pos);
                        }
                        break;

                case DEL:
                        if (pos < col + strlen(s)) {
                                pos++;
                                dt_cursor(row, pos);

                                assert(pos > col);

                                // backspace code:

                                moves = strlen(s) - (pos - col) + 1;
                                i = pos - col;
                                while (moves > 0) {
                                        s[i - 1] = s[i];
                                        i++;
                                        moves--;
                                }

                                dt_cursor(row, col);

                                for (i = 0; s[i] != '\0' && i < flen; i++) {
                                        dt_putchar(s[i]);
                                }
                                for (; i <= flen; i++) {
                                        dt_putchar(' ');
                                }
                                pos--;
                                dt_cursor(row, pos);
                        }
                        break;

                case HOME:
                        dt_cursor(row, col);
                        pos = col;
                        break;

                case END:
                        dt_cursor(row, col + strlen(s));
                        pos = col + strlen(s);
                        break;

                case ENTER: // all of these fall through
                case TAB:
                case UP:
                case DOWN:
                case PGUP:
                case PGDN:
                case F1:
                case F2:
                case F3:
                case F4:
                case F5:
                case F6:
                case F7:
                case F8:
                case F9:
                case F10:
                        return key;
                        // This statement is never reached:
                        break;

                case ESC:
                        // Do nothing now. Note: the while loop is about to end.
                        break;

                // if a letter is typed:
                default:
                        if (pos <= col + strlen(s)) {
#ifndef OVERTYPE
                                // start at end of string
                                int end = strlen(s);
                                if (end + 1 < slen + 1) { // + 1 represents space for null char
                                        // (pos - col) means our position in the field.
                                        moves = end - (pos - col);
                                        i = end;
                                        while (moves > 0) {
                                                s[i] = s[i - 1];
                                                i--;
                                                moves--;
                                        }
#endif
                                        s[pos - col] = key;             // insert the new letter
                                        pos++;                          // move pos to match
                                        dt_draw(s, row, col, flen);     // redraw field
                                        dt_cursor(row, pos);            // set cursor to $pos
                                        // FIXME this is overtype mode
                                        // FIXMEp1 what happens when we hit end of buffer?
                                        //  should our caller've passed us a string with padding?
                                        //  download assignment 2 and see how we're used.
                                }
                        }
                        break;
                }
        } while (key != ESC);

        // This point is reached when the user pressed ESC to exit.
        // Revert s to what it was before.

        (void) strcpy(s, olds);
        free(olds);

        // ;;;mark

        return key;
}