var EXPORTED_SYMBOLS = ['PictureDAO'];
var Application = Components.classes["@mozilla.org/fuel/application;1"]
                  .getService(Components.interfaces.fuelIApplication);

var DATABASE_FILE = "livemargins_grabpic.sqlite";
var PICTURES_TBL_NAME = "tbl_pictures";
var PICGROUP_TBL_NAME = "tbl_picgroups";
var METADATA_TBL_NAME = "tbl_metadata";

var PICTURES_TABLE_SCHEMA = 
		"CREATE TABLE " + PICTURES_TBL_NAME + "(" +
		"  id INTEGER PRIMARY KEY AUTOINCREMENT," +
		"  uri VARCHAR(512)," +
		"  weburi VARCHAR(512), " +
		"  group_id INTEGER)";
var PICTURES_GROUP_SCHEMA = 
		"CREATE TABLE " + PICGROUP_TBL_NAME + "(" +
		"  id INTEGER PRIMARY KEY AUTOINCREMENT, " + 
		"  title VARCHAR(256))";
var METADATA_SCHEMA = 
		"CREATE TABLE " + METADATA_TBL_NAME + "(" +
		"  id INTEGER PRIMARY KEY AUTOINCREMENT, " + 
		"  fname VARCHAR(64), " + 
		"  fvalue VARCHAR(256) )";

var dirService = Components.classes["@mozilla.org/file/directory_service;1"]
				.getService(Components.interfaces.nsIProperties);
var storageService = Components.classes["@mozilla.org/storage/service;1"]
				.getService(Components.interfaces.mozIStorageService);

// sqlite's id starts with 1 but not 0
var DEFAULT_GROUP_ID = 1;
var CURR_DATA_VERSION = "1";

var PictureDAO = {
	
	dbConnection: null,
	firstRun: false,
	worker: null,
	OS: null,
	
	isFFVersionGET4: function() {
		return typeof Application.getExtensions !== 'undefined';
	},
	
	
	init: function() {
		// Returns "WINNT" on Windows Vista, XP, 2000, and NT systems;  
		// "Linux" on GNU/Linux; and "Darwin" on Mac OS X.  
		this.OS = Components.classes["@mozilla.org/xre/app-info;1"]
						.getService(Components.interfaces.nsIXULRuntime).OS;  
						
		if (!this.isFFVersionGET4()) {
			this.worker = Components.classes["@mozilla.org/thread-manager;1"].getService().newThread(0);
		}
		
		this.connectDatabase();
		
		// import old data or make data version upgrade
		if (this.firstRun) {
			this.importJuiceData();
		}
		
		var dataVersion = this.getMetadata('data_version');
		if (dataVersion === null) dataVersion = "0";
		if (dataVersion !== CURR_DATA_VERSION) {
			var upgradeMethod = "upgradeData" + dataVersion + "To" + CURR_DATA_VERSION;
			if (upgradeMethod in this) {
				this[upgradeMethod].apply(this, null);
			}
		}
	},
	
	dispose: function() {
		if (this.dbConnection) {
			try {
				this.dbConnection.close();
			} catch (e) {}
		}
	},
	
	getGroups: function() {
		if (!this.dbConnection) return [];
		
		var groups = [];
		
		var selSql = "SELECT id, title FROM " + PICGROUP_TBL_NAME + " ORDER BY id";
		var selStatement = this.createStatement(this.dbConnection, selSql);
		try {
			while (selStatement.executeStep()) {
				var id = selStatement.getInt32(0);
				var title = selStatement.getUTF8String(1);
				groups.push({
					id: id,
					title: title
				});
			}				
		} finally {
			selStatement.reset();
		}
		
		return groups;
	},
	
	getPictures:function() {
		if (!this.dbConnection) return [];
		
		var pictures = [];
		var selSql = "SELECT id, uri, weburi, group_id FROM " + PICTURES_TBL_NAME + " ORDER BY id";
		var selStatement = this.createStatement(this.dbConnection, selSql);
		try {
			while (selStatement.executeStep()) {
				var id = selStatement.getInt32(0);
				var uri = selStatement.getUTF8String(1);
				var webUri = selStatement.getUTF8String(2);
				var groupId = selStatement.getInt32(3);
				
				pictures.push({
					id: id,
					uri: uri,
					weburi: webUri,
					groupId: groupId
				});
			}
		} finally {
			selStatement.reset();
		}
		
		return pictures;
	},
	
	// group dao
	addGroup: function(title) {
		if (!this.dbConnection) return;
		
		var insertSql = "INSERT INTO " + PICGROUP_TBL_NAME + " (title) VALUES (?1)";
		var insertStatement = this.createStatement(this.dbConnection, insertSql);
		insertStatement.bindUTF8StringParameter(0, title);
		insertStatement.execute();
		insertStatement.finalize();
		
		// return the group id
 		return this.dbConnection.lastInsertRowID;
	},
	
	updateGroup: function(id, title) {
		if (!this.dbConnection) return;
		
		var updateSql = "UPDATE " + PICGROUP_TBL_NAME + " SET title=?1 WHERE id == ?2";
		var updateStatement = this.createStatement(this.dbConnection, updateSql);
		updateStatement.bindUTF8StringParameter(0, title);
		updateStatement.bindInt32Parameter(1, id);
		updateStatement.execute();
		updateStatement.finalize();
	},
	
	removeGroup: function(id) {
		if (!this.dbConnection) return;
		
		var deleteSql = "DELETE FROM " + PICGROUP_TBL_NAME + " WHERE id == ?1";
		var deleteStatement = this.createStatement(this.dbConnection, deleteSql);
		deleteStatement.bindInt32Parameter(0, id);
		deleteStatement.execute();
		deleteStatement.finalize();
	},
	
	// picture dao
	addPicture: function(uri, weburi, gid) {
		if (!this.dbConnection) return;
		
		var insertSql = "INSERT INTO " + PICTURES_TBL_NAME + " (uri, weburi, group_id) VALUES (?1, ?2, ?3)";
		var insertStatement = this.createStatement(this.dbConnection, insertSql);
		insertStatement.bindUTF8StringParameter(0, uri);
		insertStatement.bindUTF8StringParameter(1, weburi);
		insertStatement.bindInt32Parameter(2, gid);
			
		insertStatement.execute();
		insertStatement.finalize();
		
		// return picture id
		return this.dbConnection.lastInsertRowID;
	},
	
	removePicture: function(id, uri, clearCache) {
		if (!this.dbConnection) return;
		
		var sql = "DELETE FROM " + PICTURES_TBL_NAME + " WHERE id == ?1";
		var statement = this.createStatement(this.dbConnection, sql);
		statement.bindInt32Parameter(0, id);
		statement.execute();
		statement.finalize();
		
		if (!clearCache) return;
		
		var count = 0;
		sql = "SELECT count(uri) FROM " + PICTURES_TBL_NAME + " WHERE uri==?1";
		statement = this.createStatement(this.dbConnection, sql);
		statement.bindUTF8StringParameter(0, uri);
		try {
			if (statement.executeStep()) {
				count = statement.getInt32(0);
			}
		} finally {
			statement.reset();
		}
		
		if (count == 0) {
			this.removeCache(uri);
			this.removeThumbnail(uri);
		}
	},
	
	// metadata dao
	setMetadata: function(fname, fvalue) {
		var update = (this.getMetadata(fname) !== null);

		if (update) {
			var sql = "UPDATE " + METADATA_TBL_NAME + " SET fvalue=?1 WHERE fname == ?2";
			var statement = this.createStatement(this.dbConnection, sql);
			statement.bindUTF8StringParameter(0, fvalue);
			statement.bindUTF8StringParameter(1, fname);
			statement.execute();
			statement.finalize();
		} else {
			var sql = "INSERT INTO " + METADATA_TBL_NAME + " (fname, fvalue) VALUES (?1, ?2)";
			var statement = this.createStatement(this.dbConnection, sql);
			statement.bindUTF8StringParameter(0, fname);
			statement.bindUTF8StringParameter(1, fvalue);
			statement.execute();
			statement.finalize();
		}
	},
	
	getMetadata: function(fname) {
		var sql = "SELECT fvalue FROM " + METADATA_TBL_NAME + " WHERE fname == ?1";
		var statement = this.createStatement(this.dbConnection, sql);
		statement.bindUTF8StringParameter(0, fname);
		
		try {
			if (statement.executeStep()) {
				return statement.getUTF8String(0);
			}
		} finally {
			statement.reset();
		}
		
		return null;
	},
	
	get mainThread() {
		var main = Components.classes["@mozilla.org/thread-manager;1"].getService().mainThread;
		return (main);
	},
	
	taskWrapper: function(task) {
		var wrapper = {
			run: function() {
				task.run();
			},
			
			QueryInterface: function(iid) {
				if (iid.equals(Components.interfaces.nsIRunnable) ||
			        	iid.equals(Components.interfaces.nsISupports)) {
					return this;
				}
				throw Components.results.NS_ERROR_NO_INTERFACE;
			},
		};
		
		return wrapper;
	},
	
	connectDatabase: function(){
		log('connect to database');
		if (!this.dbConnection) {
			var file = dirService.get("ProfD", Components.interfaces.nsIFile);
			file.append(DATABASE_FILE);
			
			var self = this;
			function _initPictureTable() {
				log("init or repair pictures table");
				self.firstRun = true;
				self.dbConnection.executeSimpleSQL(PICTURES_TABLE_SCHEMA);					
			}
			
			function _initPictureGroup() {
				log("init or repair pictures group table");
				self.firstRun = true;
				self.dbConnection.executeSimpleSQL(PICTURES_GROUP_SCHEMA);
				self.addGroup("__default__");
			}
			
			function _initMetadataTable() {
				log("init or repair metadata table");
				self.dbConnection.executeSimpleSQL(METADATA_SCHEMA);
				self.setMetadata("data_version", "0");
			}
			
			function _initTables() {
				_initPictureTable();
				_initPictureGroup();
				_initMetadataTable();
			}
			
			try {
				this.dbConnection = storageService.openDatabase(file);
				if (file.fileSize == 0) {
					_initTables();
				} else {
					if (!this.dbConnection.tableExists(PICTURES_TBL_NAME)) {
						_initPictureTable();
					} 
					if (!this.dbConnection.tableExists(PICGROUP_TBL_NAME)) {
						_initPictureGroup();
					}
					if (!this.dbConnection.tableExists(METADATA_TBL_NAME)) {
						_initMetadataTable();
					}
				}
			} catch(e) {
				Components.utils.reportError("GrabPic database appears to have been corrupted - resetting it." + 
							this.dbConnection.lastErrorString);
				log(e);
				if (file.exists()) {
					file.remove(false);
				}
				_initTables();
	 		}
		}
	},
	
	createStatement: function(conn, sql) {
		if (!conn) return;
		
		try {
			var statement = conn.createStatement(sql);
			return statement;
		} catch(e) {
			throw new Error(conn.lastErrorString);
		}
	},
	
	getLocalCache: function(uri) {
		var file = this.getCacheFolder();
		file.append(this.md5(uri));
		if (file.exists() && !file.isDirectory()) {
			var ioService = Components.classes["@mozilla.org/network/io-service;1"].
						getService(Components.interfaces.nsIIOService);
			return ioService.newFileURI(file).spec;
		} else {
			this.cachePicture(uri, true);
			return "";
		}
	},
	
	cachePicture: function(uri, background) {
		if (!this.localExists(uri)) {
			log("Miss Cache: " + uri);
			var self = this;
			var cacheTask = {
				pictureUri: uri,
				parent: self,
				
				run: function() {
					log("CacheTask - " + this.pictureUri);
					var cacheFolder = this.parent.getCacheFolder();
					var path = this.parent.internalSavePicture(this.pictureUri, cacheFolder, true);
					log('CacheDone - ' + this.pictureUri + " - " + path);
				},
			};
			
			if (background) {
				if (Components.interfaces.nsIWorkerFactory) {
					var workerFactory = Components.classes["@mozilla.org/threads/workerfactory;1"]
											.createInstance(Components.interfaces.nsIWorkerFactory);
					var worker = workerFactory.newChromeWorker("resource://livemargins/apps/gallery/picsaver.js");
					var self = this;
					worker.onmessage = function(event) {
						self.internalSavePicture(event.data.uri, event.data.folder, event.data.md5name);
					};
					
					worker.postMessage({
						symbol: 'components',
						components: Components
					});

					worker.postMessage({
						symbol: 'cachepic',
						pictureUri: uri
					});
				} else {
					var thread = this.isFFVersionGET4() ? this.mainThread : this.worker;
					thread.dispatch(this.taskWrapper(cacheTask), thread.DISPATCH_NORMAL);
				}
			} else {
				cacheTask.run();
			}
		} else {
			log("Hit Cache: " + uri);
		}
	},
	
	localExists: function(uri) {
		var file = this.getCacheFolder();
		file.append(this.md5(uri));
		return file.exists() && !file.isDirectory();
	},

	removeCache: function(uri) {
		if (this.localExists(uri)) {
			var file = this.getCacheFolder();
			file.append(this.md5(uri));
			file.remove(true);
			
			log('Cache Removed: ' + uri);
		}
	},
	
	get filePath () {
		log("OS: " + this.OS);
		if (this.OS == "Darwin" || this.OS == "Linux") {
			return "LiveMargins_Mac_Linux";
		} else {
			return "LiveMargins";
		}
	},
	
	
	getCacheFolder: function() {
		var cacheFolder = dirService.get('UChrm', Components.interfaces.nsIFile);
		cacheFolder.append(this.filePath);
		cacheFolder.append("files");
		if (!cacheFolder.exists() || !cacheFolder.isDirectory()) {
			cacheFolder.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0766);
		}
		return cacheFolder;
	},
	
	// thumbnail
	getThumbnailFolder: function() {
		var tFolder = dirService.get('UChrm', Components.interfaces.nsIFile);
		tFolder.append(this.filePath);
		tFolder.append('thumbnails');
		if (!tFolder.exists() || !tFolder.isDirectory()) {
			tFolder.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0766);
		}
		return tFolder;
	},
	
	getThumbnail: function(uri) {
		var file = this.getThumbnailFolder();
		file.append(this.md5(uri) + ".png");
		if (file.exists() && !file.isDirectory()) {
			var ioService = Components.classes["@mozilla.org/network/io-service;1"].
						getService(Components.interfaces.nsIIOService);
			return ioService.newFileURI(file).spec;
		}
		
		return null;
 	},
	
	createThumbnail: function(uri, data) {
		log('create thumbnail for ' + uri);
		var tfolder = this.getThumbnailFolder();
		this.internalSaveData(data, tfolder, uri);
	},
	
	removeThumbnail: function(uri) {
		log('remove thumbnail for ' + uri);
		var file = this.getThumbnailFolder();
		file.append(this.md5(uri) + ".png");
		if (file.exists() && !file.isDirectory()) {
			file.remove(true);
		}
	},
	
	// save picture
	saveGroup: function(group, folder){
		if (Components.interfaces.nsIWorkerFactory) {
			folder= encodeURI(folder.path);
			var workerFactory = Components.classes["@mozilla.org/threads/workerfactory;1"]
				.createInstance(Components.interfaces.nsIWorkerFactory);
			var worker = workerFactory.newChromeWorker("resource://livemargins/apps/gallery/picsaver.js");
			var self = this;
			worker.onmessage = function(event) {
				self.internalSavePicture(event.data.uri, event.data.folder, event.data.md5name);
			};
			
			worker.postMessage({
				symbol: 'components',
				components: Components
			});

			worker.postMessage({
				symbol: 'savegroup',
				group: group,
				folder: folder

			});
			
			// LOG("2 after encodeURI is "+ folder);
			return;
		}
		var self = this;
		
		var saveGroupTask = {
			group: group, 
			folder: folder,
			parent: self, 
			
			run: function() {
				log("SaveGroupTask - " + this.group.title);
				self.internalSaveGroup(this.group, this.folder);
				this.folder.launch();
			},
		};
		
		var thread = this.isFFVersionGET4() ? this.mainThread : this.worker;
		thread.dispatch(this.taskWrapper(saveGroupTask), thread.DISPATCH_NORMAL);
	},
	
	savePictures: function(groups, folder) {
		// create ChromeWorker to run thread.
		// nsIWorkerFactory for Fx4 - Fx7
		if (Components.interfaces.nsIWorkerFactory) {
			folder= encodeURI(folder.path);
			var workerFactory = Components.classes["@mozilla.org/threads/workerfactory;1"]
						.createInstance(Components.interfaces.nsIWorkerFactory);
			var worker = workerFactory.newChromeWorker("resource://livemargins/apps/gallery/picsaver.js");
			var self = this;
			worker.onmessage = function(event) {
				self.internalSavePicture(event.data.uri, event.data.folder, event.data.md5name);
			};
			
			worker.postMessage({
				symbol: 'components',
				components: Components
			});

			worker.postMessage({
				symbol: 'savepictures',
				groups: groups,
				folder: folder
			});
			return;
		}
		
		var self = this;
		var saveTask = {
			groups: groups,
			folder: folder,
			parent: self,
			
			run: function() {
				log("SavePicturesTask");
				for (var i = 0; i < this.groups.length; i += 1) {
					self.internalSaveGroup(this.groups[i], this.folder);
				}
				this.folder.launch();
			},
		};
		// separate thread for pre Fx4, main thread for Fx8+
		var thread = this.isFFVersionGET4() ? this.mainThread : this.worker;
		thread.dispatch(this.taskWrapper(saveTask), thread.DISPATCH_NORMAL);
	},
	
	internalSaveGroup: function(group, folder) {
		var groupName = "";
		if (group.id == DEFAULT_GROUP_ID) {
			groupName = "Default";
		} else {
			groupName = group.title;
		}

		try {
			var groupFolder = Components.classes["@mozilla.org/file/local;1"].
						createInstance(Components.interfaces.nsILocalFile);
			groupFolder.initWithFile(folder);
			groupFolder.append(groupName);
			if (!groupFolder.exists() || !groupFolder.isDirectory()) {
				groupFolder.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755);
			}
			for (var i = 0; i < group.pictures.length; i += 1) {
				var picture = group.pictures[i];
				if (this.localExists(picture.uri)) {
					this.internalCopyCache(picture.uri, groupFolder, false);
				} else {
					this.internalSavePicture(picture.uri, groupFolder, false);					
				}
			}
		} catch (e) {
			log(e);
		}
	},
	
	// copy the cache picture to folder
	internalCopyCache: function(uri, folder) {
		log("Copy cache file to folder: " + uri);
		var localfile = this.getCacheFolder();
		localfile.append(this.md5(uri));
		
		var filename = uri;
		filename = filename.substring(filename.lastIndexOf('/') + 1);
		if (filename.lastIndexOf('.') < filename.length - 5){
			filename += ".jpg";
		}			
		filename = new Date().getTime() + "_" + encodeURIComponent(filename);
		if (filename) {
			localfile.copyTo(folder, filename);
		}
	},
	
	internalSaveData: function(data, folder, uri) {
		var ioService = Components.classes["@mozilla.org/network/io-service;1"].
					getService(Components.interfaces.nsIIOService);
		var picURI = ioService.newURI(data, "UTF8", null);
		var filename = this.md5(uri) + ".png";
		
		var picFile = Components.classes["@mozilla.org/file/local;1"].
					createInstance(Components.interfaces.nsILocalFile);
		picFile.initWithFile(folder);
		picFile.append(filename);
		
		// create a persist
		var persist = Components.classes["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"].
					createInstance(Components.interfaces.nsIWebBrowserPersist);

		const nsIWBP = Components.interfaces.nsIWebBrowserPersist;
		persist.persistFlags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES | nsIWBP.PERSIST_FLAGS_FROM_CACHE;	
		persist.saveURI(picURI, null, null, null, "", picFile);
		return picFile.path;
	},
	
	// Returns cache file's local path name like "/Users/xxx/Application...."
	internalSavePicture: function(uri, folder, md5name) {
		var ioService = Components.classes["@mozilla.org/network/io-service;1"].
					getService(Components.interfaces.nsIIOService);
		// if folder is an string, init a local file with the string as a path.			
		if (typeof folder == "string") {
			folderFile = Components.classes["@mozilla.org/file/local;1"]
                .createInstance(Components.interfaces.nsILocalFile);
            folderFile.initWithPath(folder);
            folder = folderFile;
		}
		
		var picURI = ioService.newURI(uri, null, null);
		
		var filename = uri;
		if (md5name) {
			filename = this.md5(uri);
		} else {
			filename = filename.substring(filename.lastIndexOf('/') + 1);
			if (filename.lastIndexOf('.') < filename.length - 5){
				filename += ".jpg";
			}			
			filename = new Date().getTime() + "_" + encodeURIComponent(filename);
		}
		if (filename) {
			var picFile = Components.classes["@mozilla.org/file/local;1"].
						createInstance(Components.interfaces.nsILocalFile);
			picFile.initWithFile(folder);
			picFile.append(filename);
			
			// create a persist
			var persist = Components.classes["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"].
						createInstance(Components.interfaces.nsIWebBrowserPersist);
	
			const nsIWBP = Components.interfaces.nsIWebBrowserPersist;
			persist.persistFlags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES | nsIWBP.PERSIST_FLAGS_FROM_CACHE;
			try {
				persist.saveURI(picURI, null, null, null, "", picFile);
			} catch(e) {
				log("error when save uri");
			}
			
			return picFile.path;
		}
		
		return "";
	},
	
	captureWindow: function(window, uri) {
		var content = window.content;
		var desth = content.innerHeight + content.scrollMaxY;
		var destw = content.innerWidth + content.scrollMaxX;
		if (desth > 16384) desth = 16384;
		
		var canvas = content.document.createElement("canvas");
		var ctx = canvas.getContext("2d");
		canvas.height = desth;
		canvas.width = destw;
		ctx.drawWindow(content, 0, 0, destw, desth, "rgb(255,255,255)");
		
		var data = canvas.toDataURL("image/png", "");
		
		var pageFolder = dirService.get("UChrm", Components.interfaces.nsIFile);
		pageFolder.append("LiveMargins");
		pageFolder.append("WebPages");
		if (!pageFolder.exists() || !pageFolder.isDirectory()) {
			pageFolder.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755);
		}
		
		var path = this.internalSaveData(data, pageFolder, uri);
		var file = Components.classes["@mozilla.org/file/local;1"].
						createInstance(Components.interfaces.nsILocalFile);
		file.initWithPath(path);
		var ioService = Components.classes["@mozilla.org/network/io-service;1"].
						getService(Components.interfaces.nsIIOService);
		return ioService.newFileURI(file).spec;
	},
	
	// upgrade data method
	// "upgradeData" + dataVersion + "To" + CURR_DATA_VERSION;
	upgradeData0To1: function() {
		log("upgrading data from version0 to version1");
		
		// copy juice local files to livemargins cache directory
		var srcDir = dirService.get('UChrm', Components.interfaces.nsIFile);
		srcDir.append('linkool');
		srcDir.append('juice');
		srcDir.append('dlfiles');
		
		var destDir = this.getCacheFolder();
		
		if (srcDir.exists() && srcDir.isDirectory()) {
			var entries = srcDir.directoryEntries;
			while (entries.hasMoreElements()) {
				var entry = entries.getNext().QueryInterface(Components.interfaces.nsIFile);
				if (entry.exists() && !entry.isDirectory()) {
					try {
						entry.copyTo(destDir, entry.leafName);						
					} catch(e) {}
					log("Copied juice file: " + entry.leafName);
				}
			}
		}
		
		this.setMetadata('data_version', "1");
		
		// download all the pictures
		var pictures = this.getPictures();
		for (var i = 0; i < pictures.length; i += 1) {
			this.cachePicture(pictures[i].uri, true);
		}
	},
	
	importJuiceData: function() {
		var file = dirService.get('UChrm', Components.interfaces.nsIFile);
		file.append('linkool');
		file.append('juice');
		file.append('juicedata_en.sqlite');
		
		if (file.exists()) {
			log("need to import juice data, found it!");
			
			var juiceDb = null;
			try {
				juiceDb = storageService.openDatabase(file);
			} catch(e) {
				juiceDb = null;
				Components.utils.reportError("Cannot open juice database: " + 
						juiceDb.lastErrorString);
			}
			
			if (juiceDb) {
				// fd_imageURL|fd_categoryName|fd_lastModify|fd_jsonValue|fd_index|fd_id
				var pictures = {};
				var sql = "SELECT fd_imageURL, fd_jsonValue FROM tb_albumHistory";
				var statement = this.createStatement(juiceDb, sql);
				try {
					while (statement.executeStep()) {
						var itemKey = statement.getUTF8String(0);
						var jsonValue = statement.getUTF8String(1);
	
						var picObject = JSON.parse(decodeURIComponent(jsonValue));
						picObject.url = picObject.url.substring('localfile:'.length);
						pictures[itemKey] = picObject;
					}
				} finally {
					statement.reset();
				}
	
				
				var groups = {};
				sql = "SELECT fd_tagType, fd_tagName, fd_itemKey FROM tb_rs_tagAndItem";
				statement = this.createStatement(juiceDb, sql);
				try {
					while (statement.executeStep()) {
						var tagType = statement.getUTF8String(0);
						var tagName = statement.getUTF8String(1);
						var itemKey = statement.getUTF8String(2);
	
						if (tagType !== "album") continue;
	
						if (!(tagName in groups)) {
							log('found new group in juicedb ' + tagName)
							groups[tagName] = {
								title: tagName,
								pictures: []
							}
						}
						if (itemKey in pictures) {
							groups[tagName].pictures.push(pictures[itemKey]);
						}
					}
				} finally {
					statement.reset();
				}
				
				for (var groupName in groups) {
					var groupId = DEFAULT_GROUP_ID;
					if (groupName !== "default") {
						groupId = this.addGroup(groupName);
					}
					
					for (var i = 0; i < groups[groupName].pictures.length; i += 1) {
						var picture = groups[groupName].pictures[i];
						this.addPicture(picture.url, picture.windowURL, groupId);
					}
				}
				
				log('Done importing juice data!');
				try {
					juiceDb.close();					
				} catch (e) {
					log(e);
				}
			}
		}
	},
	
	md5 :function(s) {
		var str = s;
		var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);

		// we use UTF-8 here, you can choose other encodings.
		converter.charset = "UTF-8";
		// result is an out parameter,
		// result.value will contain the array length
		var result = {};
		// data is an array of bytes
		var data = converter.convertToByteArray(str, result);
		var ch = Components.classes["@mozilla.org/security/hash;1"].createInstance(Components.interfaces.nsICryptoHash);
		ch.init(ch.MD5);
		ch.update(data, data.length);
		var hash = ch.finish(false);

		// return the two-digit hexadecimal code for a byte
		var toHexString = function(charCode) {
			return ("0" + charCode.toString(16)).slice(-2);
		}
		// convert the binary hash data to a hex string.
		var s = [];//[for (var i=0;i<hash.length;i++) toHexString(hash.charCodeAt(i))].join("");
		for(var i=0;i<hash.length;i++) {
			var result = toHexString(hash.charCodeAt(i));
			s.push(result);
		}
		s = s.join("");
		return s;
	},
	
};

function logObject(obj) {
	var x = [];
	for (var key in obj) {
		if (typeof obj[key] === 'function') {
			x.push(key + ": function");
		} else {
			x.push([key, obj[key]].join(' = '));			
		}
	}
	log(x.join('\r\n'));
}

Components.utils["import"]("resource://livemargins/utils.jsm");
var _logger = new utils.logger("lm4.picdao");
function log(msg) {
	_logger.trace(msg);
}
