// VD.js -- a viewdata terminal emulator

// constructor
function VD(wd, ht, scr_id)
{
	var r;
	var c;
	var scr = document.getElementById(scr_id);
	this.wd_ = wd;
	this.ht_ = ht;
	this.text_ = new Array(ht);
	for (r = 0; r < ht; ++r) {
		this.text_[r] = new Array(wd);
	}
	this.scr_ = scr;
	this.cursor_vis_ = true;
        this.getch_isr_ = undefined;
	this.grab_events_ = false;
	this.key_buf_ = [];
	this.esc_state_ = 0;
	this.col_ = 0;
	this.row_ = 0;
	// Internal debug setting.
	this.clear();
	//this.refresh();
}

VD.A_REVERSE = 2;
VD.A_BLINK = 4;

VD.browser_ie_ = (navigator.appName.indexOf("Microsoft") != -1);
VD.browser_opera_ = (navigator.appName.indexOf("Opera") != -1);
// class variables
VD.the_vt_ = undefined;

// object methods

VD.prototype.html_colours_ = function(at_fg, at_bg, at_mode)
{
	var co0, co1;
	if (at_mode & VD.A_REVERSE) {
		co0 = 'ff';
		co1 = '00';
	} else {
		co0 = '00';
		co1 = 'ff';
	}

	return {
		f: '#' + (at_fg & 1 ? co1 : co0) +
			 (at_fg & 2 ? co1 : co0) +
			 (at_fg & 4 ? co1 : co0),
		b: '#' + (at_bg & 1 ? co1 : co0) +
			 (at_bg & 2 ? co1 : co0) +
			 (at_bg & 4 ? co1 : co0)
	    };
}


VD.prototype.clear = function()
{
	this.row_ = this.col_ = 0;
	for (r = 0; r < this.ht_; ++r) {
		for (c = 0; c < this.wd_; ++c) {
			this.text_[r][c] = ' ';
		}
	}
}

VD.prototype.curs_set = function(vis, grab, eventist)
{
	if (vis !== undefined)
		this.cursor_vis_ = (vis > 0);
}

VD.prototype.getch = function(isr)
{
//        this.refresh();
        this.getch_isr_ = isr;
        setTimeout(VD.go_getch_, 0);
}

VD.go_getch_ = function VD_go_getch()
{
        var vt = VD.the_vt_;
        if (vt === undefined)
                return;
        var isr = vt.getch_isr_;
        vt.getch_isr_ = undefined;
        if (isr === undefined)
                return;
        var ch = vt.key_buf_.shift();
        if (ch === undefined) {
                vt.getch_isr_ = isr;
                return;
        }
        isr(ch, vt);
}

VD.prototype.move = function(r, c)
{
	if (r < 0)
		r = 0;
	else if (r >= this.ht_)
		r = this.ht_ - 1;
	if (c < 0)
		c = 0;
	else if (c >= this.wd_)
		c = this.wd_ - 1;
	this.row_ = r;
	this.col_ = c;
}


VD.prototype.refresh = function()
{
	var r, c, stuff = "", start_tag = "", end_tag = "", ch,
	    pair, cr, cc, ht, wd, cv, n_fg, n_bg, n_mode, n_type, a_fg=-1, a_bg=-1, a_mode=-1;
	ht = this.ht_;
	wd = this.wd_;
	cr = this.row_;
	cc = this.col_;
	cv = this.cursor_vis_;
//	var innerHTML = this.scr_.innerHTML;
	if (cc >= wd)
		cc = wd - 1;
	for (r = 0; r < ht; ++r) {
		if (r > 0) {
			stuff += '\n';
		}
		n_fg = 7;
		n_bg = 0;
		n_mode = 0;
		n_type = 0;
		for (c = 0; c < wd; ++c) {
			if (cv && r == cr && c == cc) {
				// Draw the cursor here.
				n_mode |= VD.A_REVERSE;
			} else {
				n_mode &= ~VD.A_REVERSE;
			}

			ch = this.text_[r][c];
//console.log('CH: %i (%s) [%i,%i]',ch.charCodeAt(0),ch,r,c);
			if (ch.charCodeAt(0) >= 129 && ch.charCodeAt(0) <= 135) {
				n_fg = ch.charCodeAt(0)-128;
				n_type &= ~2;
				n_type &= ~16;
			}
			if (ch.charCodeAt(0) >= 145 && ch.charCodeAt(0) <= 151) {
				n_fg = ch.charCodeAt(0)-144;
				n_type |= 2;
				n_type &= ~16;
			}
			if (ch == '\x88')
				n_mode |= VD.A_BLINK;
			if (ch == '\x89')
				n_mode &= ~VD.A_BLINK;
			if (ch == '\x8c')
				n_type &= ~8;
			if (ch == '\x8d')
				n_type |= 8;
			if (ch == '\x98')
				n_type |= 16;
			if (ch == '\x99')
				n_type &= ~1;
			if (ch == '\x9a')
				n_type |= 1;
			if (ch == '\x9c')
				n_bg = 0;
			if (ch == '\x9d')
				n_bg = n_fg;
			if (ch == '\x9e')
				n_type |= 32;
			if (ch == '\x9f')
				n_type &= ~32;

			// If the attributes changed, make a new span.
			if (n_mode != a_mode || n_fg != a_fg || n_bg != a_bg) {
				stuff += end_tag;
				pair = this.html_colours_(n_fg, n_bg, n_mode);
				stuff += '<span style="color:' + pair.f + ';background-color:' + pair.b + ';">';
				// + (n_mode & VD.A_BLINK ? ' class="blink"' : '') + '>';
				end_tag = "</span>";
				a_fg = n_fg;
				a_bg = n_bg;
				a_mode = n_mode;
			}

			if (ch.charCodeAt(0) >= 128)
				ch = ' ';

			if (n_type & 2) {
				if ( ch.charCodeAt(0) >= 0x20 && ch.charCodeAt(0) <= 0x3f) {
					ch = '&#x' + (ch.charCodeAt(0)+0xe1e0+((n_type & 1)*0xc0)).toString(16) +";";
				} else if ( ch.charCodeAt(0) >= 0x60 && ch.charCodeAt(0) <= 0x7f) {
					ch = '&#x' + (ch.charCodeAt(0)+0xe1c0+((n_type & 1)*0xc0)).toString(16) +";";
				}
			}
			
			switch (ch) {
			    case '&':
				stuff += '&amp;'; break;
			    case '<':
				stuff += '&lt;'; break;
			    case '>':
				stuff += '&gt;'; break;
			    case ' ':
				// stuff += '&nbsp;'; break;
				stuff += '\xa0'; break;
			    case '\x7e':
			    case '~':
			    	stuff += '\xf7'; break;
			    case '\x7f':
			    	stuff += "&#xe23f;"; break
			    case '_':
			    	stuff += '#'; break
			    case '{':
			    	stuff += '\xbc'; break
			    case '\\':
			    	stuff += '\xbd'; break
			    case '}':
			    	stuff += '\xbe'; break
			    case '#':
			    	stuff += '\xa3'; break
			    default:
				stuff += ch;
			}
		}
	}
	stuff += end_tag
	//this.scr_.innerHTML = "<b>" + stuff + "</b>\n";
	this.scr_.innerHTML = stuff + "\n";
}


VD.prototype.write = function(stuff)
{
	var ch, i;
	for (i = 0; i < stuff.length; ++i) {
		ch = stuff.charCodeAt(i);
		if (this.esc_state_ && ch > 31)
			ch = (ch % 32) + 128;
		this.esc_state_ = 0;
		switch (ch) {
		case 8:
			if (this.col_ != 0) {
				--this.col_;
			} else {
				this.col_ = this.wd_-1;
				if (this.row_ == 0) {
					this.row_ = this.ht_-1;
				} else {
					--this.row_;
				}
			}
			break;
		case 9:
			if (this.col_ >= this.wd_) {
				this.col_ = 0;
				if (this.row_ == this.ht_-1) {
			    		this.row_ = 0;
				} else {
					++this.row_;
				}
			} else {
				++this.col_;
			}
			break;
		case 10:
			if (this.row_ >= this.ht_-1) {
	    			this.row_ = 0;
			} else {
				++this.row_;
			}
			break;
		case 11:
			if (this.row_ == 0) {
	    			this.row_ = this.ht_-1;
			} else {
				--this.row_;
			}
			break;
		case 12:
			this.clear();
			this.move(0, 0);
			break;
		case 13:
			this.col_ = 0;
			break;
		case 17:
			this.curs_set(1);
			break;
		case 20:
			this.curs_set(0);
			break;
		case 27:
			this.esc_state_ = 1;
			break;
		case 30:
			this.move(0, 0);
			break;
		default:
			if (ch > 31) {
				this.text_[this.row_][this.col_] = String.fromCharCode(ch);
				if (this.col_ >= this.wd_-1) {
					this.col_ = 0;
					if (this.row_ >= this.ht_-1) {
				    		this.row_ = 0;
					} else {
						++this.row_;
					}
				} else {
					++this.col_;
				}
			}
		}
	}
	this.refresh();
}
