package ca.carleton.gcrc.couch.app;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import ca.carleton.gcrc.couch.app.impl.DbRestoreListenerNull;
import ca.carleton.gcrc.couch.app.impl.DocumentFile;
import ca.carleton.gcrc.couch.client.CouchDb;
import ca.carleton.gcrc.couch.fsentry.FSEntry;
import ca.carleton.gcrc.couch.fsentry.FSEntryFile;

public class DbRestoreProcess {

	private CouchDb couchDb = null;
	private File dumpDir = null;
	private DbRestoreListener listener = new DbRestoreListenerNull();
	private boolean allDocs = false;
	private Set<String> docIds = new HashSet<String>();

	public DbRestoreProcess(CouchDb couchDb, File dumpDir) throws Exception {
		this.couchDb = couchDb;
		this.dumpDir = dumpDir;
	}

	public DbRestoreListener getListener() {
		return listener;
	}

	public void setListener(DbRestoreListener listener) {
		this.listener = listener;
	}

	public CouchDb getCouchDb() {
		return couchDb;
	}

	public File getDumpDir() {
		return dumpDir;
	}

	public boolean isAllDocs() {
		return allDocs;
	}

	public void setAllDocs(boolean allDocs) {
		this.allDocs = allDocs;
	}

	public Collection<String> getDocIds() {
		return docIds;
	}

	public void addDocId(String docId) {
		docIds.add(docId);
	}

	public void restore() throws Exception {
		if( null == couchDb ) {
			throw new Exception("On database restore, a database must be specified.");
		}
		if( null == dumpDir ) {
			throw new Exception("On database restore, a target directory must be specified.");
		}
		if( false == dumpDir.exists() ){
			throw new Exception("On database restore, the target directory must exist.");
		}

		DocumentUpdateProcess updateProcess = new DocumentUpdateProcess(couchDb);
		updateProcess.setListener(listener);
		
		Map<String,FSEntry> documentsToRestore = computeDocIds();
		
		// Sort so that design documents are at the end
		List<String> docIds = new ArrayList<String>( documentsToRestore.keySet() );
		Collections.sort(docIds, new Comparator<String>(){

			@Override
			public int compare(String docId1, String docId2) {
				boolean doc1IsDesign = docId1.startsWith("_design/");
				boolean doc2IsDesign = docId2.startsWith("_design/");
				
				if( doc1IsDesign && doc2IsDesign ) {
					return 0;
				}

				if( doc1IsDesign ) {
					return 1;
				}

				if( doc2IsDesign ) {
					return -1;
				}

				return 0;
			}
			
		});
		
		listener.reportDocumentIds(docIds);

		// Perform document updates
		for(String docId : docIds){
			FSEntry entry = documentsToRestore.get(docId);
			
			Document doc = null;
			try {
				doc = DocumentFile.createDocument(entry);
				
			} catch (Exception e) {
				throw new Exception("Unable to load document from dump: "+docId);
			}
			
			try {
				updateProcess.update(doc);
				
			} catch (Exception e) {
				throw new Exception("Unable to update document to database: "+docId);
			}
		}
		
		listener.endRestore();
	}

	private Map<String, FSEntry> computeDocIds() throws Exception {
		HashMap<String, FSEntry> map = new HashMap<String, FSEntry>();
		
		FSEntryFile topDir = new FSEntryFile(dumpDir);
		for(FSEntry entry : topDir.getChildren()) {
			try {
				Document doc = DocumentFile.createDocument(entry);
				String docId = doc.getId();

				boolean shouldBeIncluded = false;
				if( allDocs ) {
					shouldBeIncluded = true;
				} else if( docIds.contains(docId) ) {
					shouldBeIncluded = true;
				}

				if( shouldBeIncluded ) {
					if( map.containsKey(docId) ){
						throw new Exception("Document id ("+docId+") already found in: "+map.get(docId).getName());
					}
					
					map.put(docId, entry);
				}
				
			} catch(Exception e) {
				throw new Exception("Error loading document at: "+entry.getName());
			}
			
		}
		
		return map;
	}
}

@@ -2885,32 +2885,35 @@
 om dump: %22+docId
+, e
 );%0A%09%09%09%7D%0A%09%09%09%0A%09%09%09t
@@ -3047,24 +3047,27 @@
 ase: %22+docId
+, e
 );%0A%09%09%09%7D%0A%09%09%7D%0A

Add better error handling on DbRestore process.