function List(list_id, name, use_positions, readonly, public, on_mark_read)
{
	var m_id            = list_id;
	var m_name          = name;
	var m_use_positions = use_positions || 0;
	var m_readonly      = readonly || 0;
	var m_public        = public || 0;
	var m_on_mark_read  = on_mark_read || 0;
	var m_books         = null;

	this.id = function()
	{
		return m_id;
	};

	this.name = function(name)
	{
		if (typeof name !== 'undefined')
			m_name = name;
		return m_name;
	};

	this.use_positions = function(use_positions)
	{
		if (typeof use_positions === 'boolean')
			m_use_positions = use_positions;
		return m_use_positions;
	};

	this.readonly = function(readonly)
	{
		if (typeof readonly === 'boolean')
			m_readonly = readonly;
		return m_readonly;
	};

	this.public = function(public)
	{
		if (typeof public === 'boolean')
			m_public = public;
		return m_public;
	};

	this.on_mark_read = function(on_mark_read)
	{
		if (typeof on_mark_read !== 'undefined')
			m_on_mark_read = on_mark_read;
		return m_on_mark_read;
	}

	this.length = function()
	{
		return m_books.length;
	}

	this.add_book = function(book)
	{
		if (m_books == null)
			m_books = new Hash();

		m_books.setItem(book.record_id, book);
	};

	this.remove_book = function(record_id)
	{
		// If list doesn't use positions, simply remove the record from its array
		if (this.use_positions() != 1) {
			m_books.removeItem(record_id);
		} else {
			// Otherwise, figure out what position the book was at and update all later books
			var book     = m_books.getItem(record_id);
			var position = book.position;

			// Remove target book
			m_books.removeItem(record_id);

			var books = this.get_books().slice(book.position);

			for (var i in books) {
				books[i].position--;
			}
		}
	}

	this.move_book = function(record_id, new_pos, dnd)
	{
		// Don't allow moves on read-only or non-position-based lists
		if (this.readonly() == 1 || this.use_positions() != 1)
			return;

		// Get record
		var book = m_books.getItem(record_id);

		// Are we actually moving this? If not, don't bother making a request.
		if (book.position == new_pos) {
			// Call moved_item to remove the spinner (Todo #2176)
			if (!dnd)
				moved_item(record_id, book.position, book.position, dnd || 0);
			return;
		}

		// Submit Ajax request with the move
		var ajax = new pgengler.Ajax(base_url, move_book_aux);

		// Make the Ajax request
		ajax.post({
			act: 'ajax_move_to',
			record: record_id,
			list: m_id,
			pos: new_pos,
			dnd: dnd ? dnd : 0
		});
	};

	function move_book_aux(response)
	{
		if (!response)
			return;

		var root         = response.getElementsByTagName('move')[0];
		var record_id    = parseInt(root.getAttribute('record'));
		var new_position = parseInt(root.getElementsByTagName('position')[0].firstChild.nodeValue, 10);
		var old_position = parseInt(root.getElementsByTagName('oldposition')[0].firstChild.nodeValue, 10);
		var dragged      = parseInt(root.getAttribute('dnd'), 10);

		if (old_position == new_position) {
			// Don't bother doing anything if the item hasn't moved
			if (!dragged)
				clear_move_box();
			return;
		}

		var book = m_books.getItem(record_id);

		if (!book)
			return;

		// Update position information for Book
		book.position = new_position;

		if (new_position < old_position) {
			var books = curr_list.get_books().slice(new_position - 1, old_position - 1);

			for (var i = 0; i < books.length; i++) {
				if (books[i].record_id != record_id)
					books[i].position++;
			}
		} else {
			var books = curr_list.get_books().slice(old_position - 1, new_position - 1);

			for (var i = 0; i < books.length; i++) {
				if (books[i].record_id != record_id)
					books[i].position--;
			}
		}

		// Update the list display
		moved_item(record_id, old_position, new_position, dragged);
	}

	this.get_book = function(record_id)
	{
		return m_books ? m_books.getItem(record_id) : null;
	};

	this.get_books = function()
	{
		return m_books ? m_books.items.slice(0).sort(m_use_positions ? sort_positions : sort_names) : null;
	};

	function sort_positions(a, b)
	{
		return (a.position - b.position);
	}

	function sort_names(a, b)
	{
		if (a.title < b.title)
			return -1;
		else if (a.title == b.title)
			return 0;
		return 1;
	}
}

List.from_xml = function(xml)
{
	// Don't call parseInt on 'id' field because it won't be numeric for 'virtual' lists
	var list_id       = xml.getAttribute('id');

	var use_positions = parseInt(xml.getAttribute('use_positions'));
	var readonly      = parseInt(xml.getAttribute('readonly'));
	var public        = parseInt(xml.getAttribute('public'));
	var on_mark_read  = parseInt(xml.getAttribute('on_mark_read'));
	var name          = xml.getElementsByTagName('name')[0].firstChild.nodeValue.trim();

	return new List(list_id, name, use_positions, readonly, public, on_mark_read);
}

function Lists()
{
	var m_lists = [];

	this.add_list = function(list)
	{
		m_lists.push(list);
	}

	this.update_list = function(list)
	{
		// First check if list is already present; if so, update with passed List object
		for (var i = 0; i < m_lists.length; i++) {
			if (m_lists[i].id() == list.id()) {
				m_lists[i] = list;
				return;
			}
		}

		// If we made it here, the list was not found; add it
		this.add_list(list);
	}

	this.get_list = function(list_id)
	{
		for (var i = 0; i < m_lists.length; i++)
			if (m_lists[i].id() == list_id)
				return m_lists[i];
		return null;
	}

	this.get_lists = function()
	{
		m_lists.sort(this.sort_name);
		return m_lists;
	}

	this.get_readonly_lists = function()
	{
		var lists = [];

		for (var i = 0; i < m_lists.length; i++)
			if (m_lists[i].readonly() == 1)
				lists.push(m_lists[i]);
		return lists.sort(this.sort_name);
	}

	this.get_editable_lists = function()
	{
		var lists = [];

		for (var i = 0; i < m_lists.length; i++)
			if (m_lists[i].readonly() == 0)
				lists.push(m_lists[i]);
		return lists.sort(this.sort_name);
	}

	this.get_public_lists = function()
	{
		var lists = [];

		for (var i = 0; i < m_lists.length; i++)
			if (m_lists.public() == 1)
				lists.push(m_lists[i]);
		return lists.sort(this.sort_name);
	}

	this.sort_name = function(a, b)
	{
		if (a.name() < b.name())
			return -1;
		else if (a.name() == b.name())
			return 0;
		else
			return 1;
	}

	this.count = function()
	{
		return m_lists.length;
	}
}

