package com.pmease.quickbuild.plugin.scm.svn;

import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.List;

import org.apache.commons.lang.Validate;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import com.pmease.quickbuild.Context;
import com.pmease.quickbuild.QuickbuildException;
import com.pmease.quickbuild.bootstrap.Bootstrap;
import com.pmease.quickbuild.execution.Commandline;
import com.pmease.quickbuild.execution.Commandline.ExecuteResult;
import com.pmease.quickbuild.execution.LineConsumer;
import com.pmease.quickbuild.repositorysupport.SourceViewSupport;
import com.pmease.quickbuild.util.Constants;
import com.pmease.quickbuild.util.FileUtils;

@SuppressWarnings("serial")
public class SvnSourceViewSupport extends SourceViewSupport<SvnRevision> {
	
	public SvnSourceViewSupport(SvnRepository repository) {
		super(repository);
	}
	
	public SvnRepository getRepository() {
		return (SvnRepository) super.getRepository();
	}
	
	@SuppressWarnings("unchecked")
	public String mapToExternalAware(String repositoryPath) {
		String fileUrl = null;
		if (getRepository().getRuntime().getCustomData() != null) {
			List<ExternalInfo> externals = (List<ExternalInfo>) getRepository().getRuntime().getCustomData();
			String rootPath = getRepository().getNormalizedUrls().getSecond().substring(getRepository().getNormalizedUrls().getFirst().length());
			String relativePath = repositoryPath.substring(rootPath.length());
			
			String relativeToExternal = null;
			for (ExternalInfo external: externals) {
				String temp = FileUtils.getRelativePath(relativePath, external.getCheckoutPath());
				if (temp != null && (relativeToExternal == null || temp.length()<relativeToExternal.length())) {
					relativeToExternal = temp;
					fileUrl = external.getUrl() + relativeToExternal;
				}
			}
		}
		
		if (fileUrl == null)
			fileUrl = getRepository().getNormalizedUrls().getFirst() + repositoryPath;
		return fileUrl;
	}
	
	@SuppressWarnings("unchecked")
	private List<String> readFile(String repositoryPath, SvnRevision revision) {
		String fileUrl = null;
		SvnRevision externalRevision = null;
		if (getRepository().getRuntime().getCustomData() != null) {
			List<ExternalInfo> externals = (List<ExternalInfo>) getRepository().getRuntime().getCustomData();
			String rootPath = getRepository().getNormalizedUrls().getSecond().substring(getRepository().getNormalizedUrls().getFirst().length());
			String relativePath = repositoryPath.substring(rootPath.length());
			
			String relativeToExternal = null;
			for (ExternalInfo external: externals) {
				String temp = FileUtils.getRelativePath(relativePath, external.getCheckoutPath());
				if (temp != null && (relativeToExternal == null || temp.length()<relativeToExternal.length())) {
					relativeToExternal = temp;
					fileUrl = external.getUrl() + relativeToExternal;
					externalRevision = new SvnRevision(external.getRevision());
				}
			}
		}
		
		if (revision == null)
			revision = externalRevision;
		if (revision == null)
			revision = getRepository().getRevision();
		
		if (fileUrl == null)
			fileUrl = getRepository().getNormalizedUrls().getFirst() + repositoryPath;
		
        Commandline cmdline = getRepository().buildSvnCmd().addArgValue("info")
        		.addArgValue(fileUrl);

        getRepository().addAuthenticationSwitches(cmdline, 
        		getRepository().getUserName(), getRepository().getPassword());
        cmdline.addArgValue("--non-interactive");
		if (getRepository().isSupportTrustServerCert())
			cmdline.addArgValue("--trust-server-cert");
        
        cmdline.addArgLine("--xml -r " + revision);
        final StringBuffer buffer = new StringBuffer();

        final boolean[] fileExists = new boolean[] {true};
        ExecuteResult result = cmdline.execute(new LineConsumer(Constants.UTF8) {

			@Override
			public void consume(String line) {
            	Context.getLogger().debug(line);
                buffer.append(line);
                buffer.append("\n");
			}
        	
        }, new LineConsumer(Constants.UTF8) {

			@Override
			public void consume(String line) {
				Context.getLogger().warn(line);
				if (line.contains("Unable to find repository location for"))
					fileExists[0] = false;
			}
        	
        });
        if (!fileExists[0])
        	return null;
        
        result.checkReturnCode();

        SAXReader reader = new SAXReader();
        try {
            Document doc = reader.read(new StringReader(buffer.toString()));
            Element entryElement = doc.getRootElement().element("entry");
            if (entryElement == null) {
                throw new QuickbuildException("Can not find entry element for information " +
                		"of url '" + fileUrl + "'.");
            }
            
            Attribute kindAttrib = entryElement.attribute("kind");
            if (kindAttrib == null) {
                throw new QuickbuildException("Can not find kind attribute inside the " +
                		"entry element.");
            }
            if (!kindAttrib.getValue().equals("file"))
            	return null;
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        }
        
        File tempFile = FileUtils.createTempFile("svnsourceview");
        try {
	        cmdline.clearArgs().addArgValue("export").addArgValue(fileUrl)
	        		.addArgValue(tempFile.getAbsolutePath())
	        		.addArgValue("--non-interactive");
    		if (getRepository().isSupportTrustServerCert())
    			cmdline.addArgValue("--trust-server-cert");
	        
	        getRepository().addLogLevelSwitches(cmdline);
	        getRepository().addAuthenticationSwitches(cmdline, 
	        		getRepository().getUserName(), getRepository().getPassword());
	    	cmdline.addArgLine("--force -r " + revision);
	    	
	    	cmdline.execute(Bootstrap.getTempDir(), new LineConsumer.DebugLogger(), 
	    			new LineConsumer.WarnLogger()).checkReturnCode();
	        return FileUtils.readFileAsLines(tempFile);
        } finally {
        	FileUtils.deleteFile(tempFile);
        }
	}

	public List<String> readSourceByRevision(String repositoryPath, SvnRevision revision) {
		return readFile(repositoryPath, revision);
	}
	
	public List<String> readSourceByEdition(String repositoryPath, String edition) {
		return readFile(repositoryPath, new SvnRevision(edition));
	}

	public String getRepositoryPath(String checkoutPath) {
		String relativePath;
		try {
			File checkoutFile = new File(Context.getConfiguration().getWorkspaceDir(), 
					checkoutPath);
			String checkoutFileCanonicalPath = checkoutFile.getCanonicalPath();
			String destDirCanonicalPath = getRepository().getDestDir().getCanonicalPath();
			Context.getLogger().debug("Calculating repository path...");
			Context.getLogger().debug("Path to checkout file: " + checkoutFileCanonicalPath);
			Context.getLogger().debug("Path to repository destination directory: " + 
					destDirCanonicalPath);
			relativePath = FileUtils.getRelativePath(checkoutFile.getCanonicalPath(), 
					getRepository().getDestDir().getCanonicalPath());
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		if (relativePath == null || relativePath.length() == 0)
			return null;
		Validate.notNull(getRepository().getNormalizedUrls());
		return getRepository().getNormalizedUrls().getSecond()
				.substring(getRepository().getNormalizedUrls().getFirst().length()) + relativePath;
	}

}
