initComplete = false;
var lineSplitRegex = new RegExp("\s*[\r\n]+\s*", "m");
var onePixelImg = "http://i.cdn.turner.com/nascar/.e/img/2.0/global/util/pixel.gif"
var flagImgBase = "http://i.cdn.turner.com/nascar/.e/img/2.0/sect/races/apps/flags/";
var flagImgMap = {
	"green": "green.png",
	"yellow": "yellow.png",
	"white": "white.png",
	"checkered": "checkered.png",
	"black": "black.png",
	"red": "red.png"
};
var seriesId = "B";
var seriesDirMap = {
	"C": "cup",
	"B": "bg",
	"T": "ct",
	"U": ""
};
var normalDriverColor = "black";
var fastestCurrentLapColor = "#003399";
var trackImgPattern = "http://i.cdn.turner.com/nascar/.element/img/2.0/sect/races/tracks/shape/__trackAbbreviation__.png";
var driverLinkPattern = "/drivers/dps/__driverId__/__seriesDir__/index.html";
var registrationMap = {};
var protoTBody;
var registrationTBody;
var tableWrapper;
var leaderboardTable;
var dataLoadingDiv;
var lapByLapDiv;
var currentEventId;
var eventType;
var seriesId;
var appletDataAvailable = false;
var dataReloadFrequency = 10000;
var fastestCurrentLapRegId = null;
var xhrInProgress = false;
var ingestorApplet;
var intervalId = null;
var ELEMENT_NODE_TYPE = 1;

function setupLeaderboard() {
	protoTBody = el("driverRowProtos");
	registrationTBody = el("cnnTableBody");
	tableWrapper = el("tableWrapper");
	leaderboardTable = el("cnnDataTable");
	dataLoadingDiv = el("dataLoading");
	ingestorApplet = el("ingestor");
	currentEventId = null;
	loadEventData();
}

function unloadLeaderboard() {
	// force-stop the applet
	ingestorApplet.stopStream();
}

function loadEventData() {
	if (eventDataFileURL) {
		if (!xhrInProgress) { // skip this if we get asked to do it while another request is doing its thing
			var req = newXHR();
			if (req != null) {
				req.onreadystatechange = function () {
					if (req.readyState == 4) {
						if (req.status == 200) {
							initializeEvent(req.responseText);
							xhrInProgress = false;
						} else {
							alert("Error fetching init: " + req.status + " " + req.statusText);
						}
					}
				};
				xhrInProgress = true;
				req.open("GET", eventDataFileURL, true);
				req.send("");
			} else {
				alert("req is null");
			}
		}
	} else {
		alert("eventDataFileURL unknown");
	}
}

function reloadEventData() {
	if (!appletDataAvailable) {
		loadEventData();
	}
}

function extReceiveInfo(code, info) {
	
}

function extReceiveWarning(code, warning) {
	
}

function extReceiveData(length, data) {
	if (initComplete) processDataPayload(data + "");
}

function extDataAvailable(isAvailable) {
	if (isAvailable instanceof Object) {
		appletDataAvailable = isAvailable.booleanValue();
	} else {
		appletDataAvailable = isAvailable;
	}
}

function initializeEvent(data) {
	processDataPayload(data);
	if (intervalId == null) intervalId = window.setInterval("reloadEventData();", dataReloadFrequency);
}

function processDataPayload(data) {
	var lines = data.split(lineSplitRegex);
	var registrationFound = false;
	for (var i=0; i < lines.length; i++) {
		var line = lines[i];
		if (line.match(/^E\|/)) {
			processEventData(line);
		} else if (line.match(/^R\|/)) {
			registrationFound = true;
			processRegistrationData(line);
		}
	}
	if (registrationFound && !initComplete) {
		dataLoadingDiv.style.display = "none";
		tableWrapper.style.display = "block";
		initComplete = true;
		if (ingestorApplet && ingestorApplet.startStream) ingestorApplet.startStream();
	}		
}

function resetEvent() {
	// set all fields to defaults
	el('raceName').innerHTML = "";
	el('location').innerHTML = "Loading...";
	el('flagImg').setAttribute('src', onePixelImg);
	dashedFields = ['lapsCompleted', 'totalLaps', 'elapsedTime', 'cautions', 'cautionLaps', 'leaders', 'leadChanges',
		'carsStarted', 'carsRunning', 'carsOnLeadLap', 'averageEventSpeed', 'fullLocation', 'trackType', 'trackLength',
		'trackShape', 'trackTemp', 'airTemp', 'windDirection', 'windSpeed'/*, 'weatherForecast'*/];
	for (var i = 0; i < dashedFields.length; i++) {
		el(dashedFields[i]).innerHTML = "--";
	}
	el('trackImg').setAttribute('src', onePixelImg);
	// hide the leaderboardTable
	tableWrapper.style.display = "none";
	// show the loading div
	dataLoadingDiv.style.display = "";
	// remove all registration TBODYs from the table
	while (leaderboardTable.tBodies.length > 1) {
		// this should never happen because it should always be last in the list, but just to be sure
		if (leaderboardTable.tBodies[0] == protoTBody) break;
		leaderboardTable.removeChild(leaderboardTable.tBodies[0]);
	}
	// remove all regs from map
	registrationMap = {};
	initComplete = false;
}

function processEventData(data) {
	var values = data.split("|");
	// id, raceId, type, league, totalLaps, lapsCompleted, lapsLeft, trackLength, raceName, sponsor, location
	// 1,  2,      3,    4,      5,         6,             7,        8,           9,        10,       11
	// flag, startTime, elapsedTime, averageSpeed, carsRunning, carsOnLeadLap, leaders, leadChanges
	// 12,   13,        14,          15,           16,          17,            18,      19,
	// numberLapsUnderCaution, numberCautions, airTemperature, trackTemperature, windSpeed, windDirection
	// 20,                     21,             22,             23,               24,        25,
	// weather, forecast, trackAbbreviation, trackShape, trackShortName, trackType, carsStarted
	// 26,      27,       28,                29,         30,             31,        32
	if (currentEventId != values[1]) {
		if (currentEventId != null) resetEvent();
		currentEventId = values[1];
	}
	eventType = values[3];
	seriesId = values[4];
	el('raceName').innerHTML = convertNullToDefault(values[9]);
	el('location').innerHTML = convertNullToDefault(values[30]);
	if (flagImgMap[values[12]]) {
		el('flagImg').setAttribute('src', flagImgBase + flagImgMap[values[12]]);
		convertPNG(el('flagImg'));
	}
	el('lapsCompleted').innerHTML = convertNullToDefault(values[6]);
	el('totalLaps').innerHTML = convertNullToDefault(values[5]);
	el('elapsedTime').innerHTML = convertNullToDefault(values[14]);
	el('cautions').innerHTML = convertNullToDefault(values[21]);
	el('cautionLaps').innerHTML = convertNullToDefault(values[20]);
	el('leaders').innerHTML = convertNullToDefault(values[18]);
	el('leadChanges').innerHTML = convertNullToDefault(values[19]);
	el('carsStarted').innerHTML = convertNullToDefault(values[32]);
	el('carsRunning').innerHTML = convertNullToDefault(values[16]);
	el('carsOnLeadLap').innerHTML = convertNullToDefault(values[17]);
	el('averageEventSpeed').innerHTML = convertNullToDefault(values[15]);
	el('trackImg').setAttribute('src', trackImgPattern.replace("__trackAbbreviation__", values[28]));
	convertPNG(el('trackImg'));
	el('fullLocation').innerHTML = convertNullToDefault(values[11]);
	el('trackType').innerHTML = convertNullToDefault(values[31]);
	if (!isNull(values[8])) el('trackLength').innerHTML = values[8] + " Mile" + (values[8] != 1 ? "s" : "");
	el('trackShape').innerHTML = convertNullToDefault(values[29]);
	el('trackTemp').innerHTML = convertNullToDefault(values[23]);
	el('airTemp').innerHTML = convertNullToDefault(values[22]);
	el('windDirection').innerHTML = convertNullToDefault(values[25]);
	el('windSpeed').innerHTML = convertNullToDefault(values[24]);
	//el('weatherForecast').innerHTML = convertNullToDefault(values[27]);
}

function processRegistrationData(data) {
	var values = data.split("|");
	// id, carNumber, startingPosition, currentPosition, isPositionOfficial, timeBehind, lapsBehind,
	// 1,  2,         3,                4,               5,                  6,          7,
	// firstName, lastName, currentLap, currentLapTime, lapsLed, fastestLap, seasonPoints, racePoints,
	// 8,         9,        10,         11,             12,      13,         14,           15,
	// status, extendedStatus, manufacturer, sponsor, bestPosition, worstPosition, averagePosition,
	// 16,     17,             18,           19,      20,           21,            22,
	// fastestSpeed, slowestSpeed, averageSpeed, fastestLapTime, slowestLapTime, averageLapTime
	// 23,           24,           25,           26,             27,             28
	// currentSpeed, driverId, isFastestCurrentLap, isInChase
	// 29,           30,       31,                  32
	var r, newReg = false;
	// if the registration has an entry in the registration map
	var regId = values[1];
	if (registrationMap[regId]) {
		r = registrationMap[regId];
	} else {
		// create a new TR for the regular row and the details row from the prototype rows
		var newTBody = protoTBody.cloneNode(true);
		newTBody.setAttribute("id", "driverTBody" + regId);
		// modify the ids of all the children
		replaceStringInIds(newTBody, "Proto", regId);
		var normalRow = newTBody.getElementsByTagName("tr")[0];
		normalRow.style.display = "";
		// insert the new tbody as the last one (before the prototypes)
		leaderboardTable.insertBefore(newTBody, protoTBody);
		// create a registration object
		r = new Registration(regId, newTBody);
		newReg = true;
		registrationMap[regId] = r;
	}
	r.setPosition(values[4]);
	r.setCarNumber(values[2]);
	r.setStartingPosition(values[3]);
	r.setPositionOfficial("true"); // ignoring this from the feed for now
	r.setDriverName(values[8] + " " + values[9]);
	r.setCurrentLap(values[10]);
	r.setCurrentSpeed(values[29]);
	r.setCurrentTime(values[11]);
	r.setBehindLeader((values[7] && values[7] > 0) ? values[7] + " Lap" + (values[7] > 1 ? "s" : "") : values[6]);
	r.setLapsLed(values[12]);
	r.setPoints((values[14].match(/^\d+/) ? parseInt(values[14]) : 0) + (values[15].match(/^\d+/) ? parseInt(values[15]) : 0));
	r.setStatus(values[16]);
	r.setExtendedStatus(values[17]);
	r.setMake(values[18]);
	r.setSponsor(values[19]);
	r.setLowPosition(values[20]);
	r.setHighPosition(values[21]);
	r.setBestSpeed(values[23]);
	r.setAverageSpeed(values[25]);
	r.setBestTime(values[26]);
	r.setAverageTime(values[28]);
	r.setDriverId(values[30]);
	r.setFastestCurrentLap(values[31] == "1" ? true : false);
	r.setInChase(values[32] == "1" ? true : false);
}

// registration object holds info about the car/driver
function Registration(id, tBody) {
	this._id = id;
	var _position, _carNumber, _driverName, _currentLap, _currentSpeed, _currentTime, _behindLeader, _lapsLed, _points;
	var _sponsor, _make, _status, _extendedStatus, _startingPosition, _highPosition, _lowPosition, _bestSpeed, _averageSpeed;
	var _bestTime, _averageTime, _pitStops, _lastPitLap, _lastPitTime, _lastPitEntryPosition, _lastPitExitPosition, _isPositionOfficial;
	var _driverId, _isInChase;
	
	// find elements for each of our fields
	this._tBody = tBody;
	this._positionElem = el("pos" + id); this._driverLinkElem = null;
	this._currentLapElem = el("lap" + id); this._currentSpeedElem = el("speed" + id); this._currentTimeElem = el("time" + id);
	this._timeBehindElem = el("timeBehind" + id); this._lapsLedElem = el("lapsLed" + id); this._pointsElem = el("points" + id);
	this._carNumberElem = el("carNumber" + id); this._driverNameLinkElem = el("driverNameLink" + id);
	
	this.getTBody = function() {
		return this._tBody;
	}
	
	this.getId = function() {
		return this._id;
	}
	
	this.getPosition = function() {
		return this._position;
	}
	this.setPosition = function(position) {
		// don't set anything for an invalid position
		if (position.match(/^\s*\d+\s*$/)) {
			var oldPosition = this._position;
			if (position != oldPosition) {
				this._position = position;
				this._positionElem.innerHTML = this._position;
				// if our tBody element has a parent
				if (this._tBody.parentNode) leaderboardTable.removeChild(this._tBody);

				// this assumes there will be a TBODY (prototypes) in the table regardless
				var tBodies = leaderboardTable.tBodies;
				var newNextSibling = protoTBody;
				for (var i = 0; i < tBodies.length; i++) {
					if (tBodies[i] == protoTBody) {
						break;
					}
					var matches = tBodies[i].getAttribute("id").match(/^driverTBody(\d+)$/);
					if (matches && matches.length > 1) {
						var driver = registrationMap[matches[1]];
						if (driver && parseInt(driver.getPosition()) >= parseInt(position)) {
							newNextSibling = tBodies[i];
							break;
						}
					}
				}
				leaderboardTable.insertBefore(this._tBody, newNextSibling);
				// restripe the table
				for (var i = 0; i < tBodies.length; i++) {
					var tbodyTRs = tBodies[i].getElementsByTagName("tr")[0];
					var tbodyTDs = tbodyTRs.getElementsByTagName('td');
					for (var j = 0; j < tbodyTDs.length; j++) {
						tbodyTDs[j].style.backgroundColor = i % 2 == 0 ? "#efefef" : "#ffffff";
					}
				}
			}
			if (position - 0 == 1) { // implicit numeric conversion
				// set everyone else's positionOfficial to false
				for (var i = 1; i < leaderboardTable.tBodies.length; i++) {
					if (leaderboardTable.tBodies[i] == protoTBody) {
						break;
					}
					var matches = leaderboardTable.tBodies[i].getAttribute("id").match(/^driverTBody(\d+)$/);
					if (matches && matches.length > 1) {
						var driver = registrationMap[matches[1]];
						if (driver) {
							driver.setPositionOfficial("false");
						}
					}
				}
			}
		}
	}
	
	this.getCarNumber = function() {
		return this._carNumber;
	}
	this.setCarNumber = function(carNumber) {
		if (this._carNumber != convertNullToDefault(carNumber)) {
			this._carNumber = convertNullToDefault(carNumber);
			this._carNumberElem.innerHTML = this._carNumber;
		}
	}
	
	this.getDriverName = function() {
		return this._driverName;
	}
	this.setDriverName = function(driverName) {
		if (this._driverName != convertNullToDefault(driverName)) {
			this._driverName = convertNullToDefault(driverName);
			if (this._driverLinkElem != null) {
				this._driverLinkElem.innerHTML = this._driverName;
			} else {
				this._driverNameLinkElem.innerHTML = '<span class="driverName">' + this._driverName + '</span>';
			}
		}
	}
	
	this.getDriverId = function() {
		return this._driverId;
	}
	this.setDriverId = function(driverId) {
		if (this._driverId != driverId) {
			this._driverId = driverId;
			if (!isNull(this._driverId)) {
				if (this._driverLinkElem == null) {
					// create a driver link
					var driverLink = document.createElement("a");
					if (this._driverNameLinkElem.firstChild) {
						driverLink.innerHTML = this._driverNameLinkElem.firstChild.innerHTML;
						this._driverNameLinkElem.replaceChild(driverLink, this._driverNameLinkElem.firstChild);
					} else {
						this._driverNameLinkElem.appendChild(driverLink);
					}
					this._driverLinkElem = driverLink;
				}
				this._driverLinkElem.setAttribute("href", driverLinkPattern.replace("__driverId__", this._driverId).replace("__seriesDir__", seriesDirMap[seriesId]));
			}
		}
	}
	
	this.getCurrentLap = function() {
		return this._currentLap;
	}
	this.setCurrentLap = function(currentLap) {
		this._currentLap = convertNullToDefault(currentLap);
		this._currentLapElem.innerHTML = this._currentLap;
	}
	
	this.getCurrentSpeed = function() {
		return this._currentSpeed;
	}
	this.setCurrentSpeed = function(currentSpeed) {
		this._currentSpeed = convertNullToDefault(currentSpeed);
		this._currentSpeedElem.innerHTML = this._currentSpeed;
	}
	
	this.getCurrentTime = function() {
		return this._currentTime;
	}
	this.setCurrentTime = function(currentTime) {
		this._currentTime = convertNullToDefault(currentTime);
		this._currentTimeElem.innerHTML = this._currentTime;
	}
	
	this.getBehindLeader = function() {
		return this._behindLeader;
	}
	this.setBehindLeader = function(behindLeader) {
		this._behindLeader = convertNullToDefault(behindLeader);
		this._timeBehindElem.innerHTML = this._behindLeader;
	}
	
	this.getLapsLed = function() {
		return this._lapsLed;
	}
	this.setLapsLed = function(lapsLed) {
		this._lapsLed = convertNullToDefault(lapsLed);
		this._lapsLedElem.innerHTML = this._lapsLed;
	}
	
	this.getPoints = function() {
		return this._points;
	}
	this.setPoints = function(points) {
		this._points = convertNullToDefault(points);
		this._pointsElem.innerHTML = this._points;
	}
	
	this.getSponsor = function() {
		return this._sponsor;
	}
	this.setSponsor = function(sponsor) {
		this._sponsor = sponsor.toLowerCase() == "none" ? "" : convertNullToDefault(sponsor, "");
	}
	
	this.getMake = function() {
		return this._make;
	}
	this.setMake = function(make) {
		this._make = convertNullToDefault(make, "");
	}
	
	this.getStatus = function() {
		return this._status;
	}
	this.setStatus = function(status) {
		this._status = convertNullToDefault(status);
	}
	
	this.getExtendedStatus = function() {
		return this._extendedStatus;
	}
	this.setExtendedStatus = function(extendedStatus) {
		this._extendedStatus = convertNullToDefault(extendedStatus);
	}
	
	this.getStartingPosition = function() {
		return this._startingPosition;
	}
	this.setStartingPosition = function(startingPosition) {
		this._startingPosition = convertNullToDefault(startingPosition);
	}
	
	this.getHighPosition = function() {
		return this._highPosition;
	}
	this.setHighPosition = function(highPosition) {
		this._highPosition = convertNullToDefault(highPosition);
	}
	
	this.getLowPosition = function() {
		return this._lowPosition;
	}
	this.setLowPosition = function(lowPosition) {
		this._lowPosition = convertNullToDefault(lowPosition);
	}
	
	this.getBestSpeed = function() {
		return this._bestSpeed;
	}
	this.setBestSpeed = function(bestSpeed) {
		this._bestSpeed = convertNullToDefault(bestSpeed);
	}
	
	this.getAverageSpeed = function() {
		return this._averageSpeed;
	}
	this.setAverageSpeed = function(averageSpeed) {
		this._averageSpeed = convertNullToDefault(averageSpeed);
	}
	
	this.getBestTime = function() {
		return this._bestTime;
	}
	this.setBestTime = function(bestTime) {
		this._bestTime = convertNullToDefault(bestTime);
	}
	
	this.getAverageTime = function() {
		return this._averageTime;
	}
	this.setAverageTime = function(averageTime) {
		this._averageTime = convertNullToDefault(averageTime);
	}
	
	this.getPitStops = function() {
		return this._pitStops;
	}
	this.setPitStops = function(pitStops) {
		this._pitStops = convertNullToDefault(pitStops);
	}
	
	this.getLastPitLap = function() {
		return this._lastPitLap;
	}
	this.setLastPitLap = function(lastPitLap) {
		this._lastPitLap = convertNullToDefault(lastPitLap);
	}
	
	this.getLastPitTime = function() {
		return this._lastPitTime;
	}
	this.setLastPitTime = function(lastPitTime) {
		this._lastPitTime = convertNullToDefault(lastPitTime);
	}
	
	this.getLastPitEntryPosition = function() {
		return this._lastPitEntryPosition;
	}
	this.setLastPitEntryPosition = function(lastPitEntryPosition) {
		this._lastPitEntryPosition = convertNullToDefault(lastPitEntryPosition);
	}
	
	this.getLastPitExitPosition = function() {
		return this._lastPitExitPosition;
	}
	this.setLastPitExitPosition = function(lastPitExitPosition) {
		this._lastPitExitPosition = convertNullToDefault(lastPitExitPosition);
	}
	
	this.getPositionOfficial = function() {
		return this._isPositionOfficial;
	}
	this.setPositionOfficial = function(isPositionOfficial) {
		this._isPositionOfficial = isPositionOfficial == "true" ? true : false;
		var driverRow = this._tBody.getElementsByTagName("tr")[0];
		if (this._isPositionOfficial) {
			driverRow.style.color = normalDriverColor;
			if (this._driverLinkElem)
				this._driverLinkElem.style.color = normalDriverColor;
		} else {
			driverRow.style.color = "#999999";
			if  (this._driverLinkElem)
				this._driverLinkElem.style.color = "#999999";
		}
	}
	
	this.setFastestCurrentLap = function(isFastestCurrentLap) {
		var driverRow = this._tBody.getElementsByTagName("tr")[0];
		if (isFastestCurrentLap) {
			if (fastestCurrentLapRegId != null) {
				// set the old fastest driver to normal color
				var driver = registrationMap[fastestCurrentLapRegId];
				if (driver) driver.setFastestCurrentLap(false);
			}
			fastestCurrentLapRegId = this._id;
			driverRow.style.color = fastestCurrentLapColor;
			if (this._driverLinkElem)
				this._driverLinkElem.style.color = fastestCurrentLapColor;
		} else {
			driverRow.style.color = normalDriverColor;
			if (this._driverLinkElem)
				this._driverLinkElem.style.color = normalDriverColor;
		}
	}
	
	this.setInChase = function(isInChase) {
		if (isInChase != this._isInChase) {
			this._isInChase = isInChase;
		}
	}
}

