var CollectionMaker = new (new Class({
	
	Implements: Events,
	
	initialize: function(container) {
		this.loaded = false;
		this.container = container;
		this.items = new Hash({});
		window.addEvent('DataStorageReady', function() {
			this.load();
			this.updateStatus();
		}.bind(this));
	},
	
	add: function(url, label) {
		if (this.contains(url)) return;
		this.items.set(url, {
			label: label,
			attachments: {},
			order: this.items.getLength()
		});
		this.saveState();
	},
	
	addAttachment: function(url, attachmentUrl, label) {
		if (!this.contains(url)) return false;
		var item = this.items.get(url);
		Hash.set(item.attachments, attachmentUrl, label);
		this.saveState();
	},
	
	hasAttachment: function(url, attachmentUrl) {
		if (!this.contains(url)) return false;
		var item = this.items.get(url);
		return Hash.has(item.attachments, attachmentUrl);
	},
	
	removeAttachment: function(url, attachmentUrl) {
		if (!this.contains(url)) return false;
		var item = this.items.get(url);
		Hash.erase(item.attachments, attachmentUrl);
		this.saveState();
	},
	
	handleAttachment: function() {
		var parts = this.value.split('|', 2);
		if (this.checked) CollectionMaker.addAttachment(this.getParent('.collection-item').get('data-url'), parts[0], parts[1]);
		else CollectionMaker.removeAttachment(this.getParent('.collection-item').get('data-url'), parts[0]);
	},
	
	clear: function() {
		this.items = new Hash({});
		this.saveState();
		this.render();
	},
	
	contains: function(url) {
		return this.items.has(url);
	},
	
	createItem: function(url) {
		var dl = new Element('dl', {
			'class': 'collection-item',
			'data-url': url
		});
		var dt = new Element('dt');
		dl.adopt(dt);
		var dd = new Element('dd');
		dl.adopt(dd);
		var controls = new Element('span', {
			'class': 'controls'
		});
		dt.adopt(controls);
		var moveTop = new Element('a', {
			href: '#',
			html: '<img src="/files/precastfountain/gfx/icon_movetotop.gif" alt="top" title="Move to top" />',
			events: {
				click: this.moveFirst
			}
		});
		var moveUp = new Element('a', {
			href: '#',
			html: '<img src="/files/precastfountain/gfx/icon_moveup.gif" alt="up" title="Move up" />',
			events: { click: this.moveUp }
		});
		var moveDown = new Element('a', {
			href: '#',
			html: '<img src="/files/precastfountain/gfx/icon_movedown.gif" alt="down" title="Move down" />',
			events: { click: this.moveDown }
		});
		var moveBottom = new Element('a', {
			href: '#',
			html: '<img src="/files/precastfountain/gfx/icon_movetobottom.gif" alt="bottom" title="Move to bottom" />',
			events: { click: this.moveLast }
		});
		var remove = new Element('a', {
			href: '#',
			html: '<img src="/files/precastfountain/gfx/icon_remove.gif" alt="remove" title="Remove" />',
			events: { click: this.removeItem }
		});
		controls.adopt(moveTop);
		controls.adopt(moveUp);
		controls.adopt(moveDown);
		controls.adopt(moveBottom);
		controls.adopt(remove);
		dt.appendText(this.items.get(url).label + ' ');
		var loader = new Element('img', {
			src: '/files/precastfountain/img/ajax-loader.gif',
			alt: 'loading',
			title: 'Please wait..',
			'class': 'loading'
		});
		dt.adopt(loader);
		new Request({
			url: url,
			onSuccess: function(response) {
				var col3 = response.match(/<!--COL_3_BEGIN-->([\s\S]*)<!--COL_3_END-->/)[1];
				var col4 = response.match(/<!--COL_4_BEGIN-->([\s\S]*)<!--COL_4_END-->/)[1];
				dd.set('html', col3 + col4);
				dd.getElements('a').each(function(link) {
					if (!link.target && !/\.\w+$/i.test(link.href)) return;
					var input = new Element('input', {
						type: 'checkbox',
						'class': 'link-include',
						value: link.href + '|' + link.get('text').trim(),
						events: {
							click: CollectionMaker.handleAttachment
						}
					})
					input.inject(link, 'before');
					if (CollectionMaker.hasAttachment(url, link.href)) {
						input.checked = true;
						input.setAttribute('checked', 'checked');
					}
				});
				loader.dispose();
			}
		}).get();
		return dl;
	},
	
	getSortedKeys: function() {
		var sorted = this.items.getKeys();
		sorted.sort(function(a, b) {
			return this.items[a].order - this.items[b].order;
		}.bind(this));
		return sorted;
	},
	
	load: function() {
		var items = DataStorage.get('CMaker');
		if (!items) return;
		this.items = new Hash(items);
		this.loaded = true;
		this.fireEvent('load');
	},
	
	moveDown: function(e) {
		if (e) e.stop();
		var item = this.getParent('.collection-item');
		var swap = item.getNext('.collection-item');
		if (!swap) return;
		item.inject(swap, 'after').highlight('#CBD7DF');
		CollectionMaker.update();
	},
	
	moveFirst: function(e) {
		if (e) e.stop();
		var item = this.getParent('.collection-item');
		item.inject(item.getParent(), 'top').highlight('#CBD7DF');
		CollectionMaker.update();
	},
	
	moveLast: function(e) {
		if (e) e.stop();
		var item = this.getParent('.collection-item');
		item.inject(item.getParent(), 'bottom').highlight('#CBD7DF');
		CollectionMaker.update();
	},
	
	moveUp: function(e) {
		if (e) e.stop();
		var item = this.getParent('.collection-item');
		var swap = item.getPrevious('.collection-item');
		if (!swap) return;
		item.inject(swap, 'before').highlight('#CBD7DF');
		CollectionMaker.update();
	},
	
	onLoad: function(callback) {
		if (this.loaded) callback();
		else this.addEvent('load', callback);
	},
	
	remove: function(url) {
		this.items.erase(url);
		this.update();
	},
	
	removeItem: function(e) {
		if (e) e.stop();
		this.getParent('.collection-item')
			.fade('out')
			.get('tween')
			.chain(function() {
				var url = this.element.get('data-url');
				this.element.dispose();
				CollectionMaker.remove(url);
			});
	},
	
	render: function() {
		if (!this.container) return;
		this.container.empty();
		var sorted = this.getSortedKeys();
		if (sorted.length < 1) {
			this.container.adopt(new Element('p', {
				text: 'You have not selected any pages yet.'
			}));
		}
		sorted.each(function(item) {
			this.container.appendChild(this.createItem(item));
		}, this);
	},
	
	createList: function(container) {
		var sorted = this.getSortedKeys();
		if (sorted.length < 1) {
			container.adopt(new Element('p', {
				text: 'You have not selected any pages yet.'
			}));
		}
		sorted.each(function(item) {
			var content = new Element('div', {
				'class': 'content'
			});
			var rightBox = new Element('div', {
				'class': 'rightbox'
			});
			content.adopt(rightBox);
			rightBox.adopt(
				new Element('div', {
					'class': 'pageheading'
				}).adopt(
					new Element('h2', { text: this.items[item].label }),
					new Element('div', { 'class': 'clearer' })
				),
				new Element('div', {
					'class': 'crumbtrail',
					'text': item
				}));
			container.appendChild(content);
		}, this);
		return container;
	},
	
	createPrintView: function(container, async) {
		if (!$defined(async)) async = true;
		var sorted = this.getSortedKeys();
		if (sorted.length < 1) {
			container.adopt(new Element('p', {
				text: 'You have not selected any pages yet.'
			}));
		}
		sorted.each(function(item) {
			var content = new Element('div', {
				'class': 'content'
			});
			var rightBox = new Element('div', {
				'class': 'rightbox'
			});
			content.adopt(rightBox);
			var loader = new Element('img', {
				src: '/files/precastfountain/img/ajax-loader.gif',
				alt: 'loading',
				title: 'Please wait..',
				'class': 'loading'
			});
			rightBox.adopt(
				new Element('div', {
					'class': 'pageheading'
				}).adopt(
					new Element('h2', { text: this.items[item].label + ' ' }).adopt(loader),
					new Element('div', { 'class': 'clearer' })
				));
			new Request({
				url: item,
				async: async,
				onSuccess: function(response) {
					var html = response.match(/<!--CONTENT_BEGIN-->([\s\S]*)<!--CONTENT_END-->/)[1].replace(/<noscript>([\s\S]*?)<\/noscript>/g, function($0, $1) {
						return $1;
					});
					loader.dispose();
					rightBox.innerHTML += html;
				}
			}).get();
			container.appendChild(content);
		}, this);
		return container;
	},
	
	createPrintHTML: function() {
		var sorted = this.getSortedKeys();
		if (sorted.length < 1) {
			return 'You have not selected any pages yet.';
		}
		var html = '';
		sorted.each(function(item) {
			html += [
				'<table width="100%" border="0">',
					'<tr bgcolor="#E8E8E7">',
						'<td>',
							'<b>',
								this.items[item].label,
							'</b>',
						'</td>',
					'</tr>',
				'</table>',
				'<br /><br />'
			].join('');
			new Request({
				url: item,
				async: false,
				onSuccess: function(response) {
					var content = response.replace(/<noscript>([\s\S]*?)<\/noscript>/g, function($0, $1) {
						return $1;
					}).replace(/<script>([\s\S]*?)<\/script>/g, function() {
						return '';
					}).replace(/<noembed>([\s\S]*?)<\/noembed>/g, function() {
						return '';
					});
					var cols = content.match(/<!--COL_(\d)_BEGIN-->([\s\S]*)<!--COL_\1_END-->/g);
					html += [
						'<table width="100%" border="0">',
							'<tr bgcolor="#CBD7DF">',
								'<td width="350">',
									cols[0],
								'</td>',
								'<td>',
									'<br />',
									cols[1].replace(/<h3>(.*?)<\/h3>/g, function($0, $1) {
										return '<strong>' + $1 + '</strong>';
									}),
								'</td>',
							'</tr>',
							'<tr>',
								'<td width="350">',
									'<br />',
									cols[2],
								'</td>',
								'<td>',
									'<br />',
									cols[3],
								'</td>',
							'</tr>',
						'</table>',
						'<br /><br />',
						'<!-- PAGE BREAK -->'
					].join('');
				}
			}).get();
		}, this);
		return html;
	},
	
	createPDF: function() {
		var form = new Element('form', {
			method: 'post',
			action: '/en/fountain_collection_maker/pdf?cm_do_pdf=1'
		});
		var html = new Element('input', {
			name: 'collection_html',
			type: 'hidden',
			value: this.createPrintHTML()
		});
		form.adopt(html);
		$(document.body).adopt(form);
		form.submit();
		form.dispose();
	},
	
	createEmailBody: function() {
		var sorted = this.getSortedKeys();
		var parts = [];
		sorted.each(function(item) {
			var data = this.items[item];
			parts.push(data.label + ':\n' + item + '\n');
			if (Hash.getLength(data.attachments)) {
				parts.push('\nAttachments:\n');
			}
			Hash.each(data.attachments, function(val, key) {
				parts.push(val + '\n' + key.replace(/#.*$/, '') + '\n');
			});
			parts.push('\n');
		}, this);
		return parts.join('');
	},
	
	saveState: function() {
		DataStorage.set('CMaker', this.items);
		DataStorage.save();
	},
	
	setContainer: function(container) {
		this.container = container;
	},
	
	update: function() {
		if (this.container) this.container
			.getElements('.collection-item')
			.each(function(el, i) {
				var url = el.get('data-url');
				this.items[url].order = i;
			}, this);
		this.saveState();
	},
	
	updateStatus: function() {
		var length = this.items.getLength();
		$$('#page-cart-info').set('text', length + (length == 1 ? ' item' : ' items'));
		$$('#page-cart-checkbox').each(function(el) {
			el.checked = CollectionMaker.contains(location.href);
		});
	}
	
}));

var AbstractStorage = new Class({
	
	initialize: function(id) {
		this.id = id;
		this.data = {};
	},

	clear: function() {
		this.data = {};
	},
				
	get: function(key) {
		return this.data[key];
	},
	
	load: function() {
		throw new Error('not implemented');
	},
	
	save: function() {
		throw new Error('not implemented');
	},

	set: function(key, val) {
		this.data[key] = val;
	}
			
});

var CookieStorage = new Class({

	Extends: AbstractStorage,
	
	load: function() {
		this.data = JSON.decode(Cookie.read(this.id)) || {};
		window.fireEvent('DataStorageReady');
	},
	
	save: function() {
		Cookie.write(this.id, JSON.encode(this.data), {
			path: '/'
		});
	}
	
});

var GlobalStorage = new Class({

	Extends: AbstractStorage,
	
	load: function() {
		// no, it's not supposed to be used this way.
		var serialized = window.globalStorage[this.id].storedData;
		if (serialized && serialized.value) serialized = serialized.value;
		this.data = JSON.decode(serialized) || {};
		window.fireEvent('DataStorageReady');
	},
	
	save: function() {
		window.globalStorage[this.id].storedData = JSON.encode(this.data);
	}
	
});

var UserDataStorage = new Class({
	
	Extends: AbstractStorage,
	
	initialize: function(id) {
		this.id = id;
		this.data = {};
		this.storage = null;
		this.createStorage();
	},
	
	createStorage: function() {
		var container = new IFrame({
			id: 'IEDataStorageBridge',
			src: '/datastorage',
			styles: {
				width: 1,
				height: 1,
				visibility: 'hidden'
			},
			events: {
				load: function(doc) {
					this.storage = container.contentWindow.document.getElementById('DataStorage');
					this.load();
				}.bind(this)
			}
		});
		window.addEvent('domready', function() {
			$(document.body).adopt(container);
		});
	},
	
	load: function() {
		if (!this.storage) return;
		this.storage.load(this.id);
		this.data = JSON.decode(this.storage.getAttribute('storedData')) || {};
		window.fireEvent('DataStorageReady');
		return this;
	},
	
	save: function() {
		if (!this.storage) {
			alert('DataStorage is not ready yet. Please wait for a few more seconds and try again.');
			return;
		}
		this.storage.setAttribute('storedData', JSON.encode(this.data));
		this.storage.save(this.id);
		return this;
	}
	
});

window.DataStorage = (function() {
	
	var key = location.host;
	if (window.globalStorage) return new GlobalStorage(key);
	else if (Browser.Engine.trident) return new UserDataStorage(key);
	else return window.DataStorage = new CookieStorage(key);

})();

window.DataStorage.load();

window.addEvent('domready', function() {

	var changed = function() {
		if (this.checked) CollectionMaker.add(location.href, document.getElement('h2').get('text'));
		else CollectionMaker.remove(location.href);
		CollectionMaker.updateStatus();
	};
	
	$$('#page-cart-checkbox').addEvents({
		change: changed,
		click: changed
	});
	
	CollectionMaker.updateStatus();
	
	CollectionMaker.setContainer($('pagecart-items'));
	
	CollectionMaker.onLoad(function() {
		CollectionMaker.render();
	});
	
});

// LZW-compress a string
function lzw_encode(s) {
    var dict = {};
    var data = (s + "").split("");
    var out = [];
    var currChar;
    var phrase = data[0];
    var code = 256;
    for (var i=1; i<data.length; i++) {
        currChar=data[i];
        if (dict[phrase + currChar] != null) {
            phrase += currChar;
        }
        else {
            out.push(phrase.length > 1 ? dict[phrase] : phrase.charCodeAt(0));
            dict[phrase + currChar] = code;
            code++;
            phrase=currChar;
        }
    }
    out.push(phrase.length > 1 ? dict[phrase] : phrase.charCodeAt(0));
    for (var i=0; i<out.length; i++) {
        out[i] = String.fromCharCode(out[i]);
    }
    return out.join("");
}

// Decompress an LZW-encoded string
function lzw_decode(s) {
    var dict = {};
    var data = (s + "").split("");
    var currChar = data[0];
    var oldPhrase = currChar;
    var out = [currChar];
    var code = 256;
    var phrase;
    for (var i=1; i<data.length; i++) {
        var currCode = data[i].charCodeAt(0);
        if (currCode < 256) {
            phrase = data[i];
        }
        else {
           phrase = dict[currCode] ? dict[currCode] : (oldPhrase + currChar);
        }
        out.push(phrase);
        currChar = phrase.charAt(0);
        dict[code] = oldPhrase + currChar;
        code++;
        oldPhrase = phrase;
    }
    return out.join("");
}