/* For free support for VSIDE, please visit www.vsdsp-forum.com */

// Starting point template for creating VSOS3 libraries and device drivers.
// This will create a <projectname>.DL3 file, which you can copy to 
// your VS1005 Developer Board's system disk's SYS subdirectory.

// If init(), main() or fini() are not needed, remove them from the solution.
// There's no need to have any unneeded functions in the library.

#include <vo_stdio.h>
#include <volink.h>     // Linker directives like DLLENTRY
#include <apploader.h>  // RunLibraryFunction etc
#include <string.h>
#include <strings.h>
#include <timers.h>
#include <sysmemory.h>
#include <consolestate.h>
#include <ctype.h>
#include <kernel.h>



int utf16charwidth(register u_int16 c) {
  /* The limit values are based on an empirical test. If there is a
     definitive answer to which characters are supposed to be double
     wide on a terminal, that information would be appreciated. */
  if ((c >= 0x3000 && c < 0xd800) || (c >= 0xf900 && c < 0xfc00)) {
    return 2;
  }
  return 1;
}

int utf16strwidth(register u_int16 *c) {
  int res = 0;
  while (*c) {
    res += utf16charwidth(*c++);
  }
  return res;
}



#define ONE_LINE_LEN 256
s_int16 oneLine[ONE_LINE_LEN];

s_int16 GetCoordinates(register s_int16 *x, register s_int16 *y) {
  u_int32 startTime;
  s_int16 *p = oneLine;

  memset(oneLine, 0, sizeof(oneLine));
  printf("\033[6n");
  startTime = ReadTimeCount();

  while (1) {
    if (ioctl(stdin, IOCTL_TEST, NULL) > 0) {
      if ((*p++ = fgetc(stdin)) == 'R') {
	break;
      }
      if (p >= oneLine+ONE_LINE_LEN) {
	return -1;
      }
    } else {
      /* Be multitasking friendly */ 
      Delay(1);
    }
    /* 100 ms timeout */
    if (ReadTimeCount() - startTime > TICKS_PER_SEC/10) {
      return -1;
    }
  } /* while(1) (with break) */

  if (oneLine[0] != '\033' || oneLine[1] != '[' || !(p=strchr(oneLine, ';'))) {
    return -1;
  }
  *y = atoi(oneLine+2);
  *x = atoi(p+1);

  return 0;
}




u_int16 hexBuf[2][16];

/* Returns number of lines it has printed: either 1 or 0. */
int PrintHexBuf(register u_int16 line, register u_int16 n) {
  int i;
  static int consecutiveSameLines = 0;
  if (!line || memcmp(hexBuf[0], hexBuf[1], sizeof(hexBuf[0]))) {
    printf("%08lx  ", (s_int32)line*16);
    for (i=0; i<16; i++) {
      printf("%s", (i==8) ? "  " : " ");
      if (i<n) {
	printf("%02x", hexBuf[0][i]);
      } else {
	printf("  ");
      }
    }
    printf("  |");
    for (i=0; i<n; i++) {
      printf("%c", isprint(hexBuf[0][i]) ? hexBuf[0][i] : '.');
    }
    printf("|\n");
    memcpy(hexBuf[1], hexBuf[0], sizeof(hexBuf[0]));
    consecutiveSameLines = 0;
  } else {
    if (++consecutiveSameLines == 1) {
      printf("*\n");
    } else {
      /* Nothing printed, nothing shown. */
      return 0;
    }
  }
  return 1;
}




ioresult main(char *parameters) {
  FILE *fp = NULL;
  int i, rows, columns;
  u_int16 c;
  char *p = parameters;
  int retVal = S_ERROR;
  int nextIsRows = 0, nextIsColumns = 0;
  int invertBS[3] = {0}; /* DEL = \[[3~, C-H = 0x08, Backspace = 0x7F */
  char *fileName = NULL;
  int x,y;
  int nParam;
  int line;
  int hexMode = 0;
  int quietMode = 0;
  int simpleMode = 0;
  
  nParam = RunProgram("ParamSpl", parameters);

  for (i=0; i<nParam; i++) {
    if (nextIsRows > 0) {
      nextIsRows = -1;
      rows = atoi(p);
    } else if (nextIsColumns > 0) {
      nextIsColumns = -1;
      columns = atoi(p);
    } else if (!strcmp(p, "-h")) {
      printf("Usage: More [-c col] [-r rows] [-x|+x] [-h] fileName\n"
	     "-c col\tForce number of columns\n"
	     "-r rows\tForce number of rows\n"
	     "-t/+t\tSimple \"type\" mode (no formatting)\n"
	     "-x/+x\tHex mode on / off\n"
	     "-q/+q\tQuiet mode on / off\n"
	     "-h\tShow this help\n");
      goto finally_ok;
    } else if (!strcmp(p, "-r")) {
      nextIsRows = 1;
    } else if (!strcmp(p, "-c")) {
      nextIsColumns = 1;
    } else if (!strcmp(p, "-t")) {
      simpleMode = 1;
    } else if (!strcmp(p, "+t")) {
      simpleMode = 0;
    } else if (!strcmp(p, "-x")) {
      hexMode = 1;
    } else if (!strcmp(p, "+x")) {
      hexMode = 0;
    } else if (!strcmp(p, "-q")) {
      quietMode = 1;
    } else if (!strcmp(p, "+q")) {
      quietMode = 0;
    } else if (!fileName) {
      fileName = p;
    } else {
      printf("E: Unknown parameter \"%s\"\n", p);
      goto finally;
    }
    p += strlen(p)+1;
  }

  if (!fileName) {
    printf("E: No file name provided!\n", fileName);
    goto finally;
  }

  fp = fopen(fileName, "r");
  if (!fp) {
    printf("E: Cannot open \"%s\"!\n", fileName);
    goto finally;
  }

  if (simpleMode || quietMode) {
    rows = columns = 999;
  } else {
    if (!nextIsRows && !nextIsColumns) {
      /* Determine terminal size */
      printf("\033[999;999H");
      GetCoordinates(&columns, &rows);
      printf("\033[999;1H");
    }

    if (columns < 20 || (hexMode && columns < 79)) {
      printf("E: Too narrow screen!\n");
      goto finally;
    }

    printf("\033[2J\033[f\033[1;1H");
  }

#if 0
  GetCoordinates(&x, &y);
  printf("coord %d %d\n", x, y);
  printf("Size %dx%d\n", columns, rows);
#endif

  x=1; y=1;
  line = hexMode ? 0 : 1;

  if (quietMode) {
    char *buf = malloc(2048);
    if (!buf) {
      printf("E: Out of memory!\n");
      goto finally;
    } else {
      while (fread(buf, sizeof(*buf), 2048, fp) == 2048 &&
	     !(appFlags & APP_FLAG_QUIT));
      free(buf);
    }
    goto finally_ok;
  }
  
  while ((int)(c = fgetc(fp)) != EOF && !(appFlags & APP_FLAG_QUIT)) {
    static u_int16 utf8Bytes = 0;
    static u_int32 utf8Res = 0;

    if (hexMode) {
      hexBuf[0][x-1] = c;
      if (++x >= 17) {
	y += PrintHexBuf(line, 16);
	line++;
	x = 1;
      }
      c = '\0';
    } else {
      if (utf8Bytes) {
	if ((c&0xC0) != 0x80) {
	  /* Broken code, cancel and print incorrectly. */
	  utf8Bytes = 0;
	  utf8Res = c;
	} else {
	  utf8Res |= (u_int32)(c&0x3f) << (6 * --utf8Bytes);
	}
	/* UTF8 code finished, now return what we've collected. */
	if (!utf8Bytes) {
#if 0
	  printf("\n%08lx\n", utf8Res);
#endif
	  c = (u_int16)utf8Res;
	}
      } else if ((c & 0xC0) == 0xC0) {
	utf8Bytes = 1;
	utf8Res = c;
	while (((c<<=1) & 0xC0) == 0xC0) {
	  utf8Bytes++;
	}
	utf8Res = (u_int32)(utf8Res&(0x3f>>utf8Bytes)) << (6*utf8Bytes);
	if (utf8Bytes > 3) utf8Res = 0xFFFF; /* Too big value, can't be encoded */
	/* UTF8 code not finished, no character to return. */
      }

      if (!utf8Bytes) {
	if (utf8Res >= 0x10000) {
	  /* 20-bit surrogate pair presents value between 0x10000-0x10FFFF */
	  putchar(0xd800|((u_int16)(utf8Res>>10)-0x40));
	  putchar(0xdc00|(c&0x3ff));
	} else {
	  putchar(c);
	}
	if (!simpleMode) {
	  if (c >= 0x20 && c != 0x7f) {
	    int width = utf16charwidth(c);
	    if (utf8Res > 0x10000) width = 2; /* We had a surrogate pair. */
	    x += width;
	    if (x > columns) {
	      y++;
	      x = width;
	    }
	  } else if (c == '\n') {
	    y++;
	    x = 0;
	  } else if (c == '\r') {
	    x = 0;
	  } else {
	    GetCoordinates(&x, &y);
	  }
	}
	utf8Res = 0;
      }
    }

    if (!simpleMode && y >= rows) {
      int cc;
      printf("\033[7m--MORE-- (%d/%ld%%)\033[27m",
	     line, fp->fileSize ? 100L*(fp->pos)/fp->fileSize : 100L);
      cc = fgetc(stdin);
      printf("\r\033[0K");
      if (cc == '\n') {
	/* Print one line more. */
	printf("\n\033[1A");
	y--;
      } else if (cc == 'q') {
	goto finally_ok;
      } else {
	/*      Clear, UpLeft,To 1;1 */
	printf("\033[2J\033[f\033[1;1H");
	y = 1;
      }
      x = 1;
    }
    if (c == '\n') {
      line++;
    }
  } /* while (!eof && !QUIT) */

  if (appFlags & APP_FLAG_QUIT) {
    printf("^C\n");
  } else if (hexMode) {
    if (x > 1) {
      PrintHexBuf(line, x-1);
    }
    printf("%08lx\n", (s_int32)line*16+x-1);
  }

 finally_ok:
  retVal = S_OK;
 finally:
  if (fp) {
    fclose(fp);
    fp = NULL;
  }

  return retVal;
}
