/**
 * 
 */
package de.lazyzero.NazeWiiConf.utils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Set;
import java.util.TreeMap;
import java.util.logging.Level;

import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNLogEntry;
import org.tmatesoft.svn.core.SVNLogEntryPath;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.SVNProperties;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.wc.SVNWCUtil;

import de.lazyzero.NazeWiiConf.NazeWiiConf;
import de.lazyzero.NazeWiiConf.gui.widget.StateBar;

/**
 * @author Christian Moll
 * 
 */
public class SVN {
	private String url;
	private String file;
	private String name = "anonymous";
	private String password = "anonymous";
	private long startRevision = 0;
	private long endRevision = -1;// HEAD (the latest) revision
	private SVNRepository repository;
	private NazeWiiLogger logger = NazeWiiConf.getInstance().getLogger();
	private TreeMap<Long, Firmware> firmwares;
	
	private String stateOpenRepositorySuccessfull;
	private String stateNumbersFirmware;
	private String stateFetchRepositorySuccessfull;
	private String stateOpenRepositoryFailed;
	private String stateFetchRevisionFailed;
	private String stateFetchChangelogFailed;
	private String stateFirmwareNotFound;
	private String stateFirmwareDownloadFailed;
	private String stateFirmwareStartDownload;
	private String stateFirmwareFinishedDownload;
	private String stateFirmwareSaveFailed;
	private String stateFirmwareCRCfailed;

	public SVN(String url, String file) throws Exception {
		this.url = url;
		this.file = file;
		

		firmwares = new TreeMap<Long, Firmware>();
		DAVRepositoryFactory.setup();

		long time = System.currentTimeMillis();
		
		openRepository();
		logger.log(Level.INFO, "openRepostiory [ms]: " + (System.currentTimeMillis() - time));
		StateBar.setState(Icons.INFO, stateOpenRepositorySuccessfull + " [ms]: " + (System.currentTimeMillis() - time));
		fetchRevisions();
		logger.log(Level.INFO, "Number of firmwares in svn: " + firmwares.size());
		StateBar.setState(Icons.INFO, firmwares.size() + ": " + stateNumbersFirmware);
		logger.log(Level.INFO, "fetchRevisions [ms]: " + (System.currentTimeMillis() - time));
		StateBar.setState(Icons.INFO, stateFetchRepositorySuccessfull + " [ms]: " + (System.currentTimeMillis() - time));
		
	}

	private void openRepository() throws Exception {
		repository = null;
		try {
			repository = SVNRepositoryFactory.create(SVNURL
					.parseURIEncoded(this.url));
		} catch (SVNException svne) {
			logger.log(Level.WARNING,
					"error while creating an SVNRepository for the location '"
							+ this.url + "': " + svne.getMessage(), true);
//			StateBar.setState(Icons.ERROR, stateOpenRepositoryFailed);
			throw new Exception("Can't access svn!!!");
		}
		ISVNAuthenticationManager authManager = SVNWCUtil
				.createDefaultAuthenticationManager(name, password);
		repository.setAuthenticationManager(authManager);

	}

	@SuppressWarnings("unchecked")
	private void fetchRevisions() throws Exception {
		try {
			endRevision = repository.getLatestRevision();
		} catch (SVNException svne) {
			logger.log(Level.WARNING,
					"error while fetching the latest repository revision: "
							+ svne.getMessage(), true);
//			StateBar.setState(Icons.ERROR, stateFetchRevisionFailed);
			throw new Exception("Can't fetch latest revision!!!");
		}

		Collection<SVNLogEntry> logEntries = null;
		try {
			logEntries = repository.log(new String[] { "" }, null,
					startRevision, endRevision, true, true);

		} catch (SVNException svne) {
			logger.log(Level.WARNING,
					"error while collecting log information for '" + url
							+ "': " + svne.getMessage(), true);
//			StateBar.setState(Icons.ERROR, stateFetchChangelogFailed);
			throw new Exception("Can't access svn log!!!");
		}

		for (Iterator<SVNLogEntry> entries = logEntries.iterator(); entries
				.hasNext();) {
			SVNLogEntry logEntry = entries.next();

			if (logEntry.getChangedPaths().size() > 0) {
				Set<String> changedPathsSet = logEntry.getChangedPaths()
						.keySet();
				if (changedPathsSet.contains(this.file)) {
					SVNLogEntryPath entryPath = (SVNLogEntryPath) logEntry
							.getChangedPaths().get(file);
					Firmware firmware = new Firmware(logEntry.getRevision(),
							logEntry.getAuthor(), logEntry.getDate(),
							logEntry.getMessage(), entryPath.getPath());
					firmwares.put(logEntry.getRevision(), firmware);
				}
			}
		}
	}

	public File getFirmware(long revision) {
		SVNProperties fileProperties = new SVNProperties();
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		File svnFile = new File(file);

		try {
			SVNNodeKind nodeKind = repository.checkPath(svnFile.getPath(), -1);

			if (nodeKind == SVNNodeKind.NONE) {
				logger.log(Level.WARNING, "There is no entry at '" + url + "'.");
				StateBar.setState(Icons.ERROR, stateFirmwareNotFound);
				return null;
			} else if (nodeKind == SVNNodeKind.DIR) {
				logger.log(Level.WARNING, "The entry at '" + url
						+ "' is a directory while a file was expected.");
				StateBar.setState(Icons.ERROR, stateFirmwareNotFound);
				return null;
			}
			
			StateBar.setState(Icons.INFO, stateFirmwareStartDownload);
			repository.getFile(svnFile.getPath(), revision, fileProperties,
					baos);
			
		} catch (SVNException svne) {
			logger.log(Level.WARNING,
					"error while fetching the file contents and properties: "
							+ svne.getMessage());
			StateBar.setState(Icons.ERROR, stateFirmwareDownloadFailed);
			return null;
		}
		
		File saveFile = new File(Utils.getTempFolder()+ "/" +"baseflight_r"+revision+".hex");
		try {
			FileOutputStream fos = new FileOutputStream(saveFile);
			baos.writeTo(fos);
			fos.close();
			logger.log(Level.INFO, "File saved: " + saveFile);
		} catch (IOException ioe) {
			ioe.printStackTrace();
			StateBar.setState(Icons.ERROR, stateFirmwareSaveFailed);
		}
		
		String svnCRC = fileProperties.getStringValue("svn:entry:checksum");
		String fileCRC = MD5.getMD5(saveFile);
		if (fileCRC.equals(svnCRC)) {
			StateBar.setState(Icons.INFO, stateFirmwareFinishedDownload);
			return saveFile;
		} else {
			StateBar.setState(Icons.ERROR, stateFirmwareCRCfailed);
			return null;
		}
	}

	public TreeMap<Long, Firmware> getFirmwares() {
		return firmwares;
	}

	
}
