/* You may find the license in the LICENSE file */

const EXPORTED_SYMBOLS = [
	"UrlManager"
];

const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const Cu = Components.utils;
const module = Cu.import;
const Exception = Components.Exception;

module("resource://dta/utils.jsm");

const DTA = {};
module("resource://dta/api.jsm", DTA);
const IOService = DTA.IOService;

const Limits = {};
module("resource://dta/support/serverlimits.jsm", Limits);

function compareFn(a, b) {
	const rv = b.preference - a.preference;
	return rv ? rv : (Math.floor(Math.random() * 3) - 1);
}

function UrlManager(urls) {
	this.initByArray(urls);
}
UrlManager.prototype = {
	initByArray: function um_initByArray(urls) {
		this._urls = [];
		for each (let u in urls) {
			if (u instanceof DTA.URL || (u.url && u.url instanceof Ci.nsIURI)) {
				this.add(u);
			}
			else if (u instanceof Ci.nsIURI) {
				this.add(new DTA.URL(u));
			}
			else {
				this.add(
					new DTA.URL(
						IOService.newURI(u.url,	u.charset, null),
						u.preference
					)
				);
			}
		}
		this._urls.sort(compareFn);
		this._usable = this._urls[0].usable;
		this.eHost = Limits.getEffectiveHost(this._urls[0].url);
		this._makeGood();	
	},
	add: function um_add(url) {
		if (!url instanceof DTA.URL) {
			throw new Exception(url + " is not an DTA.URL");
		}
		if (!this._urls.some(function(ref) ref.url.spec == url.url.spec)) {
			this._urls.push(url);
		}
	},
	_rotate: function um_rotate() {
		this.good.push(this.good.shift());
	},
	_makeGood: function um_makeGood() {
		this.good = this._urls.filter(function(u) !('bad' in u));
		if (!this.good.length) {
			// all marked bad; actually a bug
			Cu.reportError("UM: all marked bad");
			for each (let u in this._urls) {
				delete u.bad;
			}
			this.good = this._urls.map(function(e) e);
		}
	},
	getURL: function um_getURL(idx) {
		let rv = this.good[0];
		this._rotate();
		return rv;
	},
	get url() {
		return this._urls[0].url;
	},
	get usable() {
		return this._urls[0].usable;
	},
	get length() {
		return this._urls.length;
	},
	get all() {
		for each (let i in this._urls) {
			yield i;
		}
	},
	replace: function(url, newurl) {
		this._urls = this._urls.map(function(u) u.url.spec == url.url.spec ? newurl : u);
		this._makeGood();
	},
	markBad: function um_markBad(url) {
		if (this.good.length == 1) {
			// cannot mark the last url bad :p
			return false;
		}
		for each (let u in this._urls) {
			if (u != url) {
				continue;
			}
			u.bad = true;
			// lower the preference
			u.preference = 0;
			break;
		}
		this._makeGood();
		return true;
	},
	serialize: function um_serialize() {
		let rv = [];
		for each (let url in this._urls) {
			rv.push(url.serialize());
		}
		return rv;
	},
	toString: function() {
		return this._urls.reduce(function(v, u) v + u.preference + " " + u.url + "\n");
	},
	// clone ;)
	toArray: function() this._urls.map(function(e) e)
};