var g_steps_per_second = 128
var g_step_size = 1.0 / g_steps_per_second

function dprint(s) {
	document.write(s)
}

function htmlquote(s) {
	return String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
}

function break_time(t) {
	var min, sec, ms

	ms = Math.floor(t * 1000.0)
	sec = Math.floor(ms / 1000)
	min = Math.floor(sec / 60)

	ms -= sec * 1000
	sec -= min * 60

	return { min: min, sec: sec, ms: ms }
}

function get_var(s) {
	var i

	i = s.indexOf("=")

	if (i >= 0)
		return { name: s.substring(0, i), value: s.substring(i + 1) }

	return null
}

function left_fill_string(s, pad, n) {

	n -= s.length

	while (n > 0) {
		if (n & 1)
			s = pad + s

		n >>= 1
		pad += pad
	}

	return s
}

function time_to_string(t) {
	var s

	t = break_time(t)

	s = left_fill_string(t.min.toString(), " ", 3) + ":"
	s += left_fill_string(t.sec.toString(), "0", 2) + "."
	s += left_fill_string(t.ms.toString(), "0", 3)

	return s
}

function position_to_lap(raceinfo, p) {
	if (p < raceinfo.firstlap)
		return 0

	return 1 + Math.floor((p - raceinfo.firstlap) / raceinfo.normallap);
}

function lap_to_timeidx(raceinfo, lap) {
	if (lap == 0)
		return 0

	return raceinfo.firstlap + raceinfo.normallap * (lap - 1) - 1
}

function wrap_timing_position(raceinfo, i) {
	if (i < raceinfo.firstlap)
		return i

	return raceinfo.firstlap + (i - raceinfo.firstlap) % raceinfo.normallap
}

function get_section_time(times, i0, i1) {
	var i, t0, t1

	for (i = i0 < 0 ? 0 : i0; i <= i1; i++)
		if (!(i in times))
			return -1

	t0 = (i0 < 0) ? 0 : times[i0]
	t1 = times[i1]

	return t1 - t0;
}

function get_average_section_time(raceinfo, times, i0, i1) {
	var i, n, t, s, p, fll, nll

	p = times.length
	fll = raceinfo.firstlap
	nll = raceinfo.normallap

	if (i0 < fll)
		return get_section_time(times, i0, i1);

	t = 0
	n = 0
	i = wrap_timing_position(raceinfo, i0)

	for (i = wrap_timing_position(raceinfo, i0); i + (i1 - i0) < p; i += nll) {
		s = get_section_time(times, i, i + (i1 - i0));

		if (s >= 0) {
			t += s
			n++
		}
	}

	if (n == 0)
		return -1

	return Math.floor(t / n)
}

function get_baseline_section_time(raceinfo, index, i0, i1) {
	var i, s, t, n, times

        t = 0;
        n = 0;

        for (i in raceinfo.players) {
		times = raceinfo.players[i].times

		s = get_average_section_time(raceinfo, times, i0, i1)

		if (s >= 0) {
			t += s
			n++
		}
	}

	if (n == 0)
		return g_steps_per_second

        return Math.floor(t / n)
}

function get_cutting_penalty(raceinfo, index) {
	var times, i, n, r, p, t, t1, t0

	times = raceinfo.players[index].times
	n = times.length
	p = 0
	r = 0
	t0 = 0

	for (i = 0; i < n; i++) {
		if (!(i in times))
			r++
		else {
			t1 = times[i]
			if (r > 0) {
				t = get_baseline_section_time(raceinfo, index, i - r - 1, i)

				if (t + g_steps_per_second > t1 - t0)
					p += t + g_steps_per_second - (t1 - t0)

				r = 0
			}
			t0 = t1
		}
	}

	return p
}

function get_lap_time(raceinfo, times, lap) {
	var lastlap, lapstart, lapend

	lastlap = position_to_lap(times.length);

	if (lap < 0)
		lap = lastlap;

	if (lap < 1 || lap > lastlap)
		return 0

	lapstart = lap_to_timeidx(raceinfo, lap - 1)
	lapend = lap_to_timeidx(raceinfo, lap)

	if (!(lapstart in times && lapend in times))
		return 0

	if (lap == 1)
		lapstart = raceinfo.starttime;
	else
		lapstart = times[lapstart];
	lapend = times[lapend];

	return (lapend - lapstart)
}

function lap_no_cuts(raceinfo, times, lap) {
	var i, lapstart, lapend

	lapstart = lap_to_timeidx(raceinfo, lap - 1)
	lapend = lap_to_timeidx(raceinfo, lap)

	for (i = lapstart; i <= lapend; i++)
		if (!(i in times))
			return 0

	return 1
}

function best_lap(raceinfo, times, nocuts) {
	var i, j, n, best, t0, t1;

	n = position_to_lap(raceinfo, times.length)

	if (n < 2)
		return 0

	best = 2;

	for (i = 3; i <= n; i++)  {
		t0 = get_lap_time(raceinfo, times, best)
		t1 = get_lap_time(raceinfo, times, i)

		if (nocuts) {
			if (!lap_no_cuts(raceinfo, times, i))
				continue
			if (!lap_no_cuts(raceinfo, times, best))
				t0 = 0.0
		}

		if (t0 == 0.0 || (t1 != 0.0 && t1 < t0))
			best = i
	}

	if (nocuts && !lap_no_cuts(raceinfo, times, best))
		return 0

	return best
}

function first_checkered_time(raceinfo) {
	var i, p, t, l, btime, blaps

	btime = 0
	blaps = 0

	for (i in raceinfo.players) {
		p = raceinfo.players[i]

		l = position_to_lap(raceinfo, p.times.length)

		if (l < raceinfo.laps || l < blaps)
			continue

		t = p.times[lap_to_timeidx(raceinfo, l - raceinfo.laps)]

		if (t < raceinfo.time + raceinfo.starttime)
			continue

		t = p.times[lap_to_timeidx(raceinfo, l)]

		if (l > blaps || t < btime) {
			btime = t
			blaps = l
		}
	}

	return btime
}

function compare_position_and_time(a, b) {
	if (a.position == b.position)
		return a.time - b.time
	return -(a.position - b.position)
}

function update_running_order(raceinfo) {
	var i, n, order, p, pos, time, penalty, bl

	n = 0
	order = []

	raceinfo.firstcheckeredtime = first_checkered_time(raceinfo)

	for (i in raceinfo.players) {
		p = raceinfo.players[i]

		pos = p.times.length

		pos = lap_to_timeidx(raceinfo, position_to_lap(raceinfo, pos)) + 1

		if (pos <= 1)
			time = raceinfo.starttime
		else
			time = p.times[pos - 1]

		penalty = get_cutting_penalty(raceinfo, i)

		bl = best_lap(raceinfo, p.times, false)

		if (bl >= 2) {
			bl = get_lap_time(raceinfo, p.times, bl)

			while (penalty > bl && pos > raceinfo.firstlap) {
				penalty -= bl
				pos -= raceinfo.normallap
			}
		}

		time += penalty

		order[n] = { index: i, position: pos, time: time }
		n++
	}

	order.sort(compare_position_and_time)

	raceinfo.order = order

	for (i in raceinfo.order)
		raceinfo.players[raceinfo.order[i].index].order = i
}

function compare_first_element(a, b) {
	return a[0] - b[0]
}

function count_laps(raceinfo) {
	var laps, i, l

	laps = 0

	for (i in raceinfo.players) {
		l = position_to_lap(raceinfo, raceinfo.players[i].times.length)
		if (l > laps)
			laps = l
	}

	return laps
}

function count_players(raceinfo) {
	var i, n

	n = 0

	for (i in raceinfo.players)
		n++

	return n
}

function make_lap_chart(raceinfo) {
	var i, l, laps, a, p, n, s

	laps = count_laps(raceinfo)

	if (laps == 0)
		return

	n = count_players(raceinfo)

	a = []

	for (l = 0; l < laps; l++) {
		a[l] = []
		for (i in raceinfo.players) {
			p = lap_to_timeidx(raceinfo, l + 1)
			if (p in raceinfo.players[i].times)
				a[l].push([raceinfo.players[i].times[p], i])
		}

		a[l].sort(compare_first_element)
	}

	dprint("<H3>Lap Chart</H3>\n")

	dprint("<TABLE BORDER=1 FRAME=BORDER RULES=ALL>\n")

	dprint("<TR><TH>&nbsp;</TH>")

	for (l = 0; l < laps; l++)
		dprint("<TH>L" + (l + 1).toString() + "</TH>")

	dprint("</TR>\n")

	for (i = 0; i < n; i++) {
		dprint("<TR><TH>P" + (i + 1).toString() + "</TH>")
		for (l = 0; l < laps; l++) {
			if (i in a[l])
				dprint("<TD>" + raceinfo.players[a[l][i][1]].number + "</TD>")
			else
				dprint("<TD></TD>")
		}
		dprint("</TR>\n")
	}
	dprint("</TABLE>\n")
}

function require_and_quote_string(obj, s, def) {
	if (!(s in obj))
		obj[s] = def

	obj[s] = htmlquote(obj[s])
}

function sanitize_raceinfo(raceinfo) {
	var i, p, requiredints

	if (!("time" in raceinfo))
		raceinfo.time = 0

	requiredints = ["firstlap", "normallap", "laps", "starttime", "time", "date"]

	for (i in requiredints) {
		i = requiredints[i]

		if (!(i in raceinfo))
			throw "Missing required variable \"" + i + "\""

		requiredints[i] = parseInt(requiredints[i])

		if (isNaN(requiredints[i]))
			throw "\""  + i + "\" is not a valid integer"
	}

	require_and_quote_string(raceinfo, "dir", "Unknown")
	require_and_quote_string(raceinfo, "longname", raceinfo.dir)

	for (p in raceinfo.players) {
		p = raceinfo.players[p]

		require_and_quote_string(p, "name", "Mystery Rider")
		require_and_quote_string(p, "bike", "???")
		require_and_quote_string(p, "number", "???")
	}
}

function date_to_string(secs) {
	var d = new Date(secs * 1000)

	return d.toLocaleString()
}

function how_long_ago(secs) {
	var d, u, units, unitlabels, ago

	d = new Date()

	secs = d.valueOf() * 0.001 - secs

	if (secs < 0.0) {
		secs = -secs
		ago = " from now"
	} else
		ago = " ago"


	unitlabels = [ "second", "minute", "hour", "day" ]
	units = []
	units[0] = Math.floor(secs)
	units[1] = Math.floor(units[0] / 60.0)
	units[2] = Math.floor(units[1] / 60.0)
	units[3] = Math.floor(units[2] / 24.0)

	for (u = 3; u >= 0; u--) {
		if (units[u] > 1)
			return units[u].toString() + " " + unitlabels[u] + "s" + ago
		if (units[u] == 1)
			return "1" + unitlabels[u] + ago
	}

	return "less than 1 second" + ago
}

function print_results(raceinfo) {
	var s, i, j, p, l, t

	dprint("<P>")
	dprint(raceinfo.longname + ", ")
	if (raceinfo.time >= 60)
		dprint((raceinfo.time / 60).toFixed(0) + " minutes, ")
	dprint(raceinfo.laps.toString() + " laps, ")
	dprint(date_to_string(raceinfo.date) + " (" + how_long_ago(raceinfo.date) + ")</P>\n")
	dprint("<TABLE BORDER=1 FRAME=BORDER RULES=ALL>\n")
	dprint("<TR><TH>Pos.</TH><TH>No.</TH><TH>Name</TH><TH>Bike</TH><TH>Laps</TH>")
	dprint("<TH>Total Time</TH><TH>Best Lap</TH><TH>Best Time</TH></TR>\n")

	for (i in raceinfo.order) {
		i = parseInt(i)
		j = raceinfo.order[i].index
		p = raceinfo.players[j]
		s = "<TR>"
		s += "<TD CLASS=\"pos\">" + (i + 1).toString() + "</TD>"
		s += "<TD CLASS=\"no\">" + left_fill_string(p.number, " ", 4) + "</TD>"
		s += "<TD CLASS=\"name\">" + p.name + "</TD>\n"
		s += "<TD CLASS=\"bike\">" + left_fill_string(p.bike, " ", 12) + "</TD>"
		l = position_to_lap(raceinfo, raceinfo.order[i].position)
		s += "<TD CLASS=\"laps\">" + left_fill_string(l.toString(), " ", 4) + "</TD>"
		s += "<TD CLASS=\"totaltime\">" + time_to_string((raceinfo.order[i].time - raceinfo.starttime) * g_step_size) + "</TD>"
		t = best_lap(raceinfo, p.times, true)
		s += "<TD CLASS=\"bestlap\">" + left_fill_string(t.toString(), " ", 4) + "</TD>"
		s += "<TD CLASS=\"besttime\">" + time_to_string(get_lap_time(raceinfo, p.times, t) * g_step_size) + "</TD>"
		/* s += "<TD>" + left_fill_string(p.uid, " ", 6) + "</TD>" */
		s += "</TR>"
		dprint(s)
	}

	dprint("</TABLE>\n")
}

function print_full_results(raceinfo) {
	try {
		dprint("<H3>" + raceinfo.server + "</H3>\n")

		update_running_order(raceinfo)

		print_results(raceinfo)
		make_lap_chart(raceinfo)
		dprint("<P><A HREF=\"mxsimulator:" + raceinfo.server + "\">Connect to this server</A></P>\n")
	} catch (e) {
		dprint(e)
	}
}
