/*-
 * Copyright (c) 1980, 1991 The Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * console.c: hacks to do various cursor movement/attribute things
 * -amol
 */
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <wincon.h>
#include <stdio.h>
#include "ntport.h"


// int to SHORT. caused by all the stupid functions that take WORDs
#pragma warning(disable:4244)

void ScrollBuf(HANDLE,CONSOLE_SCREEN_BUFFER_INFO*,int);
void NT_MoveToLineOrChar(int ,int ) ;
WORD get_attributes();


#define	FSHIN	16		/* Preferred desc for shell input */
#define	FSHOUT	17		/* Preferred desc for shell input */

#define FOREGROUND_BLACK (FOREGROUND_RED |FOREGROUND_GREEN | FOREGROUND_BLUE)
#define FOREGROUND_WHITE 0
#define BACKGROUND_BLACK (BACKGROUND_RED |BACKGROUND_GREEN | BACKGROUND_BLUE)
#define BACKGROUND_WHITE 0

static WORD wNormalAttributes;


static int nt_is_raw;
//
// The following are used to optimize some console routines. It avoids having
// to call GetConsoleScreenBufferInfo.
// Seems to have helped the speed a bit. -amol
//
HANDLE ghstdout;
HANDLE ghReverse;

//
// This function is called to set the values for above variables.
//
void redo_console(void) {

	CONSOLE_SCREEN_BUFFER_INFO scrbuf;
	HANDLE hTemp= GetStdHandle(STD_OUTPUT_HANDLE);
	WORD dbga;
	DWORD wrote;
	COORD origin = {0,0};

	if (!DuplicateHandle(GetCurrentProcess(),hTemp,GetCurrentProcess(),
				&ghstdout,0,TRUE,DUPLICATE_SAME_ACCESS) ) {
		;
	}

	if(!GetConsoleScreenBufferInfo(ghstdout, &scrbuf) ) {
		wNormalAttributes = FOREGROUND_BLACK | BACKGROUND_WHITE;
	}
	else
		wNormalAttributes = scrbuf.wAttributes;

	ghReverse = CreateConsoleScreenBuffer(GENERIC_READ|GENERIC_WRITE,
			FILE_SHARE_READ | FILE_SHARE_WRITE,
			NULL,
			CONSOLE_TEXTMODE_BUFFER,
			NULL);

	dbga = ((wNormalAttributes & 0x00f0) >> 4) |
		((wNormalAttributes & 0x000f) << 4) ;

	FillConsoleOutputAttribute(ghReverse,dbga,
			scrbuf.dwSize.X*scrbuf.dwSize.Y,
			origin,
			&wrote);
}
void nt_term_cleanup(void) {
	CloseHandle(ghstdout);
}
void nt_term_init() {

	DWORD dwmode;
	HANDLE hinput =GetStdHandle(STD_INPUT_HANDLE);

	if (!GetConsoleMode(hinput,&dwmode) ){
		;
	}
	if(!SetConsoleMode(hinput,dwmode | ENABLE_WINDOW_INPUT) ){
		return;
	}

	redo_console();

	return;
}
int do_nt_check_cooked_mode(void) {

	return !nt_is_raw;
}
void do_nt_raw_mode() {

	DWORD dwmode;
	HANDLE hinput =(HANDLE)_get_osfhandle(FSHIN);

	if (hinput == INVALID_HANDLE_VALUE)
		return;
	if (!GetConsoleMode(hinput,&dwmode) ){
		;
	}
	if(!SetConsoleMode(hinput,dwmode & (~(
						ENABLE_LINE_INPUT |ENABLE_ECHO_INPUT 
						| ENABLE_PROCESSED_INPUT)| ENABLE_WINDOW_INPUT )
				) ){
		return;
	}
	nt_is_raw = 1;
	return;
}
void do_nt_cooked_mode() {

	DWORD dwmode;
	HANDLE hinput =(HANDLE)_get_osfhandle(FSHIN);

	if (hinput == INVALID_HANDLE_VALUE)
		return;
	if (!GetConsoleMode(hinput,&dwmode) ){
		;
	}
	if(!SetConsoleMode(hinput,dwmode | ( (
						ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT |
						ENABLE_PROCESSED_INPUT) )
				) ){
	}
	nt_is_raw = 0;
	return;
}
//
// this function is a bit ugly, but I don't know how to do it better
// -amol
//
int nt_ClearEOL( void) {

	CONSOLE_SCREEN_BUFFER_INFO scrbuf;
	HANDLE hStdout =ghstdout ;
	DWORD numwrote;
	char errbuf[128];/*FIXME: uninitialized*/
	int num=0;
	COORD savepos;


	if (hStdout == INVALID_HANDLE_VALUE){
		ExitProcess(0xFFFF);
	}
	if(!GetConsoleScreenBufferInfo(hStdout, &scrbuf) ) {
		return 0 ;
	}
	num =2048;

	savepos = scrbuf.dwCursorPosition;
	if (!FillConsoleOutputCharacter(hStdout,' ',num,scrbuf.dwCursorPosition,
				&numwrote) ){
		dprintf("error from FillCons %s",errbuf);
	}
	else if (!FillConsoleOutputAttribute(hStdout,scrbuf.wAttributes, num,
				scrbuf.dwCursorPosition,&numwrote)) {
		dprintf("error from FillConsAttr %s",errbuf);
	}
	return 0;
}
void nt_move_next_tab(void) {

	CONSOLE_SCREEN_BUFFER_INFO scrbuf;
	HANDLE hStdout = ghstdout;
	int where;

	if(!GetConsoleScreenBufferInfo(hStdout, &scrbuf) ) {
		;
	}
	where = 8 - (scrbuf.dwCursorPosition.X+1)%8;
	scrbuf.dwCursorPosition.X += where;
	if (!SetConsoleCursorPosition(hStdout, scrbuf.dwCursorPosition) ) {
		;
	}

}
void NT_VisibleBell(void) {

	if(ghReverse != INVALID_HANDLE_VALUE) {
		SetConsoleActiveScreenBuffer(ghReverse);
		Sleep(100);
		SetConsoleActiveScreenBuffer(ghstdout);

	}

}
void NT_WrapHorizontal(void) {
	SMALL_RECT wnd;
	CONSOLE_SCREEN_BUFFER_INFO scrbuf;


	if (ghstdout == INVALID_HANDLE_VALUE){
		return;
	}
	if(!GetConsoleScreenBufferInfo(ghstdout, &scrbuf) ) {
		return;
	}
	//absolute movement
	wnd.Left =  0;//scrbuf.srWindow.Left ;
	wnd.Right = scrbuf.srWindow.Right- scrbuf.srWindow.Left + 1;
	wnd.Top =  scrbuf.srWindow.Top;
	wnd.Bottom = scrbuf.srWindow.Bottom;

	SetConsoleWindowInfo(ghstdout,TRUE,&wnd);
}
void ScrollBufHorizontal(HANDLE hOut, CONSOLE_SCREEN_BUFFER_INFO *scrbuf,
		int where) {
	SMALL_RECT wnd;
	int diff;
	CHAR_INFO chr;


	//absolute movement
	wnd.Left = (where - scrbuf->srWindow.Right) + scrbuf->srWindow.Left ;
	wnd.Right = where;
	wnd.Top =  scrbuf->srWindow.Top;
	wnd.Bottom = scrbuf->srWindow.Bottom;

	//diff = scrbuf->srWindow.Right - where;
	//dprintf("\tdiff1 %d\n",diff);

	diff = scrbuf->dwSize.X - where -1;

	if (diff < 0) { //would scroll past console buffer

		chr.Char.AsciiChar = ' ';
		chr.Attributes = scrbuf->wAttributes;

		scrbuf->dwCursorPosition.Y  = scrbuf->srWindow.Top ;
		scrbuf->dwCursorPosition.X = scrbuf->srWindow.Right+ diff;

		dprintf("scroll diff %d\n",diff);
		if (!ScrollConsoleScreenBuffer(hOut,&(scrbuf->srWindow),
					NULL,
					scrbuf->dwCursorPosition,&chr)) 
			;

		return;
	}

	SetConsoleWindowInfo(hOut,TRUE,&wnd);
}
// relative movement of "where".  line is 1 if we want to move to a line,
// or 0 if the movement is horizontal
void NT_MoveToLineOrChar(int where,int line) {

	CONSOLE_SCREEN_BUFFER_INFO scrbuf;
	HANDLE hStdout = ghstdout;


	if (hStdout == INVALID_HANDLE_VALUE){
		return;
	}
	if(!GetConsoleScreenBufferInfo(hStdout, &scrbuf) ) {
		return;
	}

	if (line){
		if ( ((scrbuf.dwCursorPosition.Y+where)> (scrbuf.srWindow.Bottom-1))
				&&( where >0)){
			ScrollBuf(hStdout,&scrbuf,where);
			scrbuf.dwCursorPosition.Y += where;
		}
		else
			scrbuf.dwCursorPosition.Y += where;
	}
	else{
		if ( (where> (scrbuf.srWindow.Right)) &&( where >0)){
			ScrollBufHorizontal(hStdout,&scrbuf,where);
		}
		scrbuf.dwCursorPosition.X = where;
	}
	if (scrbuf.dwCursorPosition.X < 0 || scrbuf.dwCursorPosition.Y <0)
		return;
	if (!SetConsoleCursorPosition(hStdout, scrbuf.dwCursorPosition) ) {
		return;
	}

}
void ScrollBuf(HANDLE hOut, CONSOLE_SCREEN_BUFFER_INFO *scrbuf,int where) {
	SMALL_RECT wnd;
	int diff;
	CHAR_INFO chr;
	COORD newpos;


	wnd.Left = 0;
	wnd.Right = 0;
	wnd.Top =  where;
	wnd.Bottom = where;

	//dwSize is not 0-based, so add 1 to proposed location
	diff = scrbuf->srWindow.Bottom + where + 1; 

	diff = scrbuf->dwSize.Y - diff;

	if (diff < 0) { //would scroll past console buffer

		chr.Char.AsciiChar = ' ';
		chr.Attributes = scrbuf->wAttributes;

		newpos.Y  = scrbuf->srWindow.Top + diff;
		newpos.X = scrbuf->srWindow.Left;

		dprintf("scroll diff %d\n",diff);
		if (!ScrollConsoleScreenBuffer(hOut,&(scrbuf->srWindow),
					NULL,
					newpos,&chr)) 
			;

		// need this to be in sync with tcsh
		scrbuf->dwCursorPosition.Y += diff; 
		return;
	}

	SetConsoleWindowInfo(hOut,FALSE,&wnd);
}
BOOL ConsolePageUpOrDown(BOOL Up) {

	HANDLE hStdout = ghstdout;
	CONSOLE_SCREEN_BUFFER_INFO scrbuf;
	SMALL_RECT srect;
	short diff;

	if(!GetConsoleScreenBufferInfo(hStdout, &scrbuf) ) {
		return FALSE;
	}
	diff = scrbuf.srWindow.Bottom -scrbuf.srWindow.Top+1 ;


	if (Up)
		diff = -diff;

	if ((scrbuf.srWindow.Top + diff > 0) &&
			(scrbuf.srWindow.Bottom + diff < scrbuf.dwSize.Y)) { 
		srect.Top = diff;  
		srect.Bottom = diff;
		srect.Left = 0;    
		srect.Right = 0;  

		if (! SetConsoleWindowInfo( hStdout, FALSE, &srect)) {
			return FALSE;
		}
	} 

	return TRUE;
}
int nt_getsize(int * lins, int * cols, int *visiblecols) {
	CONSOLE_SCREEN_BUFFER_INFO scrbuf;
	HANDLE hStdout = ghstdout;

	if(!GetConsoleScreenBufferInfo(hStdout, &scrbuf) ) {
		;
	}
	*lins = scrbuf.srWindow.Bottom -scrbuf.srWindow.Top+1 ;

	if(visiblecols)
		*visiblecols = scrbuf.srWindow.Right -scrbuf.srWindow.Left +1;

	*cols = scrbuf.dwSize.X;
	return 1;
}
void nt_set_size(int lins, int cols) {
	SMALL_RECT srect;
	CONSOLE_SCREEN_BUFFER_INFO scrbuf;
	int expand;

	/* The screen buffer visible window is specified as co-ordinates
	 * not size. Therefore, it must be zero-based
	 */
	cols--;
	lins--;

	srect.Left = srect.Top = 0;
	srect.Right = cols;
	srect.Bottom = lins;

	if(!GetConsoleScreenBufferInfo(ghstdout, &scrbuf) ) 
		return;

	expand = 0;
	if (scrbuf.dwSize.X < cols){
		expand = 1;
		scrbuf.dwSize.X = cols+1;
	}
	if (scrbuf.dwSize.Y < lins){
		expand = 1;
		scrbuf.dwSize.Y = lins+1;
	}

	if (expand && !SetConsoleScreenBufferSize(ghstdout,scrbuf.dwSize))
		return;

	if(!SetConsoleWindowInfo(ghstdout,TRUE,&srect)){
		int err;
		err=GetLastError();
		dprintf("error %d\n",err);
	}
}
void NT_ClearEOD(void) {
	CONSOLE_SCREEN_BUFFER_INFO scrbuf;
	DWORD numwrote;
	COORD origin;
	int ht,wt;
	HANDLE hStdout = ghstdout;//GetStdHandle(STD_OUTPUT_HANDLE);

	if (hStdout == INVALID_HANDLE_VALUE){
		return ;
	}
	if(!GetConsoleScreenBufferInfo(hStdout, &scrbuf) ) {
		return ;
	}
	origin = scrbuf.dwCursorPosition;
	ht = scrbuf.dwSize.Y - origin.Y;
	wt = scrbuf.dwSize.X - origin.X;
	if(!FillConsoleOutputCharacter(hStdout,' ',ht*wt,origin,&numwrote) ) {
		return ;
	}
	if (!FillConsoleOutputAttribute(hStdout,scrbuf.wAttributes, ht*wt,
				scrbuf.dwCursorPosition,&numwrote)) {
		return;
	}
	return;
}
void NT_ClearScreen(void) {
	CONSOLE_SCREEN_BUFFER_INFO scrbuf;
	DWORD numwrote;
	COORD origin={0,0};
	HANDLE hStdout = ghstdout;//GetStdHandle(STD_OUTPUT_HANDLE);

	if (hStdout == INVALID_HANDLE_VALUE){
		;
	}
	if(!GetConsoleScreenBufferInfo(hStdout, &scrbuf) ) {
		;
	}
	origin.X = scrbuf.srWindow.Left;
	origin.Y = scrbuf.srWindow.Top;
	if(!FillConsoleOutputCharacter(hStdout,' ',scrbuf.dwSize.X*scrbuf.dwSize.Y,
				origin,&numwrote) ) {
		;
	}
	if (!FillConsoleOutputAttribute(hStdout,scrbuf.wAttributes,
				scrbuf.dwSize.X*scrbuf.dwSize.Y,origin,&numwrote)) {
		;
	}
	if (!SetConsoleCursorPosition(hStdout, origin) ) { // home cursor
		;
	}
	return;
}
void NT_ClearScreen_WholeBuffer(void) {
	CONSOLE_SCREEN_BUFFER_INFO scrbuf;
	DWORD numwrote;
	COORD origin={0,0};
	HANDLE hStdout = ghstdout;

	if (hStdout == INVALID_HANDLE_VALUE){
		;
	}
	if(!GetConsoleScreenBufferInfo(hStdout, &scrbuf) ) {
		;
	}
	if(!FillConsoleOutputCharacter(hStdout,' ',scrbuf.dwSize.X*scrbuf.dwSize.Y,
				origin,&numwrote) ) {
		;
	}
	if (!FillConsoleOutputAttribute(hStdout,scrbuf.wAttributes,
				scrbuf.dwSize.X*scrbuf.dwSize.Y,origin,&numwrote)) {
		;
	}
	if (!SetConsoleCursorPosition(hStdout, origin) ) { // home cursor
		;
	}
	return;
}

#ifndef COLOR_LS_F
void set_cons_attr(char *attr2) {
	char cp[3];
	USHORT attr;
	HANDLE outhandle = (HANDLE)_get_osfhandle(FSHOUT);
	static WORD old_attribs;
	CONSOLE_SCREEN_BUFFER_INFO scrbuf;

	if (!old_attribs) {
		if(!GetConsoleScreenBufferInfo(outhandle, &scrbuf) ) {
			return;
		}
		old_attribs = scrbuf.wAttributes;
	}
	cp[0] = (unsigned char)(attr2[0]);
	cp[1] = (unsigned char)(attr2[1]);
	cp[2] = 0;
	if (cp[0] != 'g' || cp[1] != 'g')
		attr = (USHORT)strtol(cp,NULL,16);
	else{
		attr = old_attribs;
		old_attribs=0;
	}

	SetConsoleTextAttribute(outhandle, attr );
}
#endif /* !COLOR_LS_F */


/*
   color escape sequences (ISO 6429, aixterm)
   - nayuta
 */


WORD get_attributes() {
	CONSOLE_SCREEN_BUFFER_INFO scrbuf;
	if (!GetConsoleScreenBufferInfo(ghstdout, &scrbuf))
		return 0x70; // ERROR: return white background, black text
	return scrbuf.wAttributes;
}


#ifndef COMMON_LVB_REVERSE_VIDEO
#define COMMON_LVB_REVERSE_VIDEO   0x4000
#define COMMON_LVB_UNDERSCORE      0x8000
#endif


void set_attributes(const unsigned char *color) {

	static const int colors[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
	WORD wAttributes;
	const char *t;

	if (color[0] == '\x1b' && color[1] == '[')
		color += 2;

	if (!('0' <= color[0] && color[0] <= '9')) {
		SetConsoleTextAttribute(ghstdout, wNormalAttributes);
		return;
	}

	wAttributes = get_attributes();
	t = (char*)color;

	while (t) {
		int n = atoi(t);

		if ((t = strchr(t, ';')) != NULL)
			t++;

		if      (n == 0)				// Normal (default)
			wAttributes = wNormalAttributes;
		else if (n == 1)				// Bold
			wAttributes |= FOREGROUND_INTENSITY;
		else if (n == 4)				// Underlined
			wAttributes |= COMMON_LVB_UNDERSCORE;
		else if (n == 5)				// Blink (appears as BACKGROUND_INTENSITY)
			wAttributes |= BACKGROUND_INTENSITY;
		else if (n == 7)				// Inverse
			wAttributes |= COMMON_LVB_REVERSE_VIDEO;
		else if (n == 21)				// Not bold
			wAttributes &= ~FOREGROUND_INTENSITY;
		else if (n == 24)				// Not underlined
			wAttributes &= ~COMMON_LVB_UNDERSCORE;
		else if (n == 25)				// Steady (not blinking)
			wAttributes &= ~BACKGROUND_INTENSITY;
		else if (n == 27)				// Positive (not inverse)
			wAttributes &= ~COMMON_LVB_REVERSE_VIDEO;
		else if (30 <= n && n <= 37)	// Set foreground color
			wAttributes = (wAttributes & ~0x0007) | colors[n - 30];
		else if (n == 39)				// Set foreground color to default
			wAttributes = (wAttributes & ~0x0007) | (wNormalAttributes & 0x0007);
		else if (40 <= n && n <= 47)	// Set background color
			wAttributes = (wAttributes & ~0x0070) | (colors[n - 40] << 4);
		else if (n == 49)				// Set background color to default
			wAttributes = (wAttributes & ~0x0070) | (wNormalAttributes & 0x0070);
		else if (90 <= n && n <= 97)	// Set foreground color (bright)
			wAttributes = (wAttributes & ~0x0007) | colors[n - 90]
				| FOREGROUND_INTENSITY;
		else if (100 <= n && n <= 107)	// Set background color (bright)
			wAttributes = (wAttributes & ~0x0070) | (colors[n - 100] << 4)
				| BACKGROUND_INTENSITY;
		else							// (default)
			wAttributes = wNormalAttributes;
	}

	// Though Windows' console supports COMMON_LVB_REVERSE_VIDEO,
	// it seems to be buggy.  So we must simulate it.
	if (wAttributes & COMMON_LVB_REVERSE_VIDEO)
		wAttributes = (wAttributes & COMMON_LVB_UNDERSCORE)
			| ((wAttributes & 0x00f0) >> 4) | ((wAttributes & 0x000f) << 4);
	SetConsoleTextAttribute(ghstdout, wAttributes);
}
void StartHighlight(void)
{
}
void StopHighlight(void)
{
}
