/*	Copyright  (c)	Günter Woigk 2016 - 2016
					mailto:kio@little-bat.de

	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.

	Permission to use, copy, modify, distribute, and sell this software and
	its documentation for any purpose is hereby granted without fee, provided
	that the above copyright notice appear in all copies and that both that
	copyright notice and this permission notice appear in supporting
	documentation, and that the name of the copyright holder not be used
	in advertising or publicity pertaining to distribution of the software
	without specific, written prior permission.  The copyright holder makes no
	representations about the suitability of this software for any purpose.
	It is provided "as is" without express or implied warranty.

	THE COPYRIGHT HOLDER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
	INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
	EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY SPECIAL, INDIRECT OR
	CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
	DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
	TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
	PERFORMANCE OF THIS SOFTWARE.
*/

#define SAFE 3
#define LOG 1
#include "kio/kio.h"
#include "TTYObject.h"
#include "TTYPixmap.h"
#include "MainWindow.h"


static //helper
cstr maxmemstr()
{
	return max_mem >= 1 GB ? usingstr("%.1f GB",max_mem/(1.0 GB)) : usingstr("%u MB",max_mem/(1 MB));
}


// ===========================================================
//				Text Terminal Object
// ===========================================================


TTYObject::TTYObject(TTYPixmap* pixmap, int x, int y)
:
	Object(IsaTTYObject,x,y,pixmap->width(),pixmap->height()),
	pixmap(pixmap)
{}

TTYObject::TTYObject(int w, int h, QRgb paper, QRgb pen)
:
	Object(IsaTTYObject,w,h),
	pixmap(new TTYPixmap(w,h,paper,pen))
{}

TTYObject::TTYObject(int w, int h, QRgb paper)
:
	Object(IsaTTYObject,w,h),
	pixmap(new TTYPixmap(w,h,paper))
{}


/*	Reset geometry: ZSIZE=QSIZE, QPOS=0,0, ROT=0°
*/
void TTYObject::resetGeometry()
{
	zbox.setWidth(pixmap->width());
	zbox.setHeight(pixmap->height());
}


/*	paint object into the framebuffer:
*/
void TTYObject::paint_out(QPainter& p, int dx, int dy)
{
	p.drawPixmap(zbox.translated(dx,dy), pixmap);
	if(children.count()) Object::paint_out(p,dx,dy);
}



// ===========================================================
//				Print to Terminal
// ===========================================================


/*	Execute paint commands:
*/
int TTYObject::paintjob(Stream& stream, QPainter&) throw(uint)
{
	TTYPixmap* s = pixmap;

	uchar bu[256];	// scratch for i/o

	for(;;)
	{
		uint repeat = 1;
a:		uint byte = stream.readByte();

		switch(byte)
		{
		default:						// print ASCII, Latin-1 or graphics char
			if(repeat>1) memset(bu,byte,repeat);
			else { bu[0] = byte; repeat += stream.readAvail(bu+1, sizeof(bu)-1, [](uchar c){return c>=0x20;}); }
			s->drawText(bu,repeat);
			continue;

		case Reset:						// in plain transmission mode (TTY == GTerm) reset whole GTerm
			s->reset();					// else reset this widget only
			if(stream.transmission_mode == Stream::plain_transmission) throw Reset;	// full GTerm reset
			continue;

		case ClearScreen:
			s->clearScreen();
			continue;

		case GotoPosition:
			stream.readBytes(bu,2);
			s->gotoPosition(bu[0],bu[1]);	// row, col
			continue;

		case GotoColumn:
			s->gotoColumn(stream.readByte());
			continue;

		case GetCursorPosition:			// reported position may be outside screen
			bu[0] = GetCursorPosition;	// negative values are reported as big positive values and are ambiguous
			bu[1] = s->row;				// very large values are reported modulo 256
			bu[2] = s->col;				// be reasonable!
			stream.write(bu,3);
			continue;

		case PushCursorPosition:
			s->pushState();
			continue;

		case PopCursorPosition:
			s->popState();
			continue;

		case ShowCursor:
			s->showBlob();
			continue;

		case CursorLeft:
			s->cursorLeft(repeat);
			continue;

		case Tab:
			s->cursorTabulator(repeat);
			continue;

		case CursorDown:
			s->cursorDown(repeat);
			continue;

		case CursorUp:
			s->cursorUp(repeat);
			continue;

		case CursorRight:
			s->cursorRight(repeat);
			continue;

		case Return:
			s->cursorReturn();
			continue;

		case ClearToEndOfLine:
			s->clearRect(1,999);
			continue;

		case PrintBMP:		// print immediately following bitmap
			stream.readBytes(bu,s->char_height);
			s->drawBmp(bu,s->char_height,repeat);
			continue;

		case SetAttributes:
			s->setAttr(stream.readByte());		// attr
			continue;

		case XON:
			continue;

		case RepeatNextChar:
			repeat = stream.readByte();		// repeat count
			goto a;

		case XOFF:
			continue;

		case SetColors:			// <byte:which> <bytes:r> <bytes:g> <bytes:b>
			stream.readBytes(bu,4);
			byte = bu[0];		// which: 0=paper, 1=pen
			bu[0] = 0xff;		// will become alpha
			if(byte) s->setPen(peek4X(bu));			// argb
			else     s->setPaper(peek4X(bu));
			continue;

		case ScrollScreen:
			byte = stream.readByte() | 0x20;
			if(byte=='u') s->scrollScreenUp(+repeat); else
			if(byte=='d') s->scrollScreenUp(-repeat); else
			if(byte=='l') s->scrollScreenRight(-repeat); else
			if(byte=='r') s->scrollScreenRight(+repeat);
			else LogLine("0x15 'ScrollScreen': illegal direction");
			continue;

		case ClearWindow:
			stream.readBytes(bu,2);
			s->clearRect(bu[0],bu[1]);					// rows, cols
			continue;

		case CopyWindow:
			stream.readBytes(bu,4);
			s->copyRect(bu[0],bu[1],bu[2],bu[3]);		// destrow, destcol, rows, cols
			continue;

		case EchoChar:
			memset(bu,stream.readByte(),repeat);
			stream.write(bu,repeat);
			continue;

		case MoveWindow:
			// in 'identify' we advertise logical size == physical size,
			// so no horizontal shift of the logical window inside the physical window possible:
			// note: we could do s.th. with the qbox/zbox of the current object..
			throw byte; // "0x19: 'MoveWindow': not supported";

		case SetFramebufferSize:
			// we could resize the pixmap and adjust the qbox/zbox of the current object,
			// but the pixmap might be attached to another object too, and resizing the pixmap may come unexpected to that object.
			// the handling of the vertical size in the original MyTFT cannot be replicated. (40 or 42 rows only)
			throw byte; // "0x1A: 'SetFramebufferSize': not supported";

		case Identify:				// report dimensions of this TTY widget and capabilities of GTerm
			//-->	0x1E,
			//		"MyLCD,GTermQt,ssz=ROWS*COLS,fsz=ROWS*COLS,csz=12*8,
			//		gfx,rgb=888,mem=1G,str=240,fs,ptr,kbd\n"
			//
			//	 MyLCD 	= Kio's terminal
			//	 GTerm	= Name
			//	 ssz 	= screen size = ROWS*COLS			Textzeilen im Textmodus
			//	 fsz 	= framebuffer size = ROWS*COLS		Textzeilen im Textmodus
			//	 csz 	= character size = ROWS*COLS		Zeichengröße im Textmodus (Pixel)
			//	 gfx 	= Grafik-VDU vorhanden
			//	 rgb 	= Bits pro Kanal in True Color
			//	 mem 	= Verfügbarer Speicher für alle Pixelbuffer incl. Framebuffer (in Pixel)
			//	 str 	= Anzahl logischer Streams
			//	 fs  	= File System: Terminal can store files
			//	 ptr 	= Pointer device available, e.g. mouse
			//	 kbd 	= Keyboard available

			stream.write(usingstr(
				"\x1EMyLCD,GTermQt,ssz=%u*%u,fsz=%u*%u,csz=%u*%u,gfx,rgb=888,mem=%s,str=%u%s%s%s%s\n",
				s->rows,s->cols,s->rows,s->cols,s->char_height,s->char_width,
				maxmemstr(),
				MAX_STREAMS,
				enable_fs?",fs":"",
				enable_kbd?",kbd":"",
				enable_joy?",joy":"",
				enable_ptr?",ptr":""
				));
			continue;

		case Escape:
			byte = stream.readByte();
			if(byte==4)		//	set UDG: <byte:char>, <byte[char_height]> must follow
			{				//           <char> must be in range 0x80 .. 0x9F
				byte = stream.readByte();
				stream.readBytes(bu,pixmap->char_height);
				s->setUDG(byte,bu);
			}
			else handleEscape(byte);
			continue;

		case GetBmpOfCharMatrix:	throw byte; // "0x1C 'GetBmpOfCharMatrix': not implemented";
		case GetBmpAtCursor:		throw byte; // "0x1D 'GetBmpAtCursor': not implemented";
		case Controlcode_0x1F:		throw byte; // error
		}//switch command
	}
}








