package cn.edu.seu.ise.dca.analysis.util;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Scanner;
import java.util.Set;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNLogEntry;
import org.tmatesoft.svn.core.SVNLogEntryPath;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.wc.SVNClientManager;
import org.tmatesoft.svn.core.wc.SVNRevision;

import cn.edu.seu.ise.dca.analysis.RepositoryFactory;
import cn.edu.seu.ise.dca.analysis.SVNCollector;
import cn.edu.seu.ise.dca.global.Global;
import cn.edu.seu.ise.dca.util.ExcelReader;
import cn.edu.seu.ise.dca.util.ListUtil;

public class LoCComparision {
	
	static Logger logger = Logger.getLogger(SVNCollector.class);
	
    @SuppressWarnings("rawtypes")
	public static void main(String[] args){
        DAVRepositoryFactory.setup();
        
    	//Set the project you want to test
		String project = "joomla";
		//Set the log properties
		PropertyConfigurator.configure( "log4j.properties" );
		//Get the properties defined for project settings
		String settings = "cn/edu/seu/ise/dca/properties/projects.properties";
		Properties properties = new Properties();
		InputStream in = ClassLoader.getSystemResourceAsStream(settings);  
		try {
			properties.load(in);
		} catch (IOException e) {
			e.printStackTrace();
		}  
		//Get the properties of the selected project
		String projectSettings = properties.getProperty(project);
		in = ClassLoader.getSystemResourceAsStream(projectSettings);
		try {
			properties.load(in);
		} catch (IOException e) {
			e.printStackTrace();
		}
		//Get the information of selected project
		String projectName = properties.getProperty("name");
		String url = properties.getProperty("url");
		String reUrl = properties.getProperty("reurl");
		//String schemaUrl = properties.getProperty("schema");
		String username = properties.getProperty("username");
		String password = properties.getProperty("password");
		String includes = properties.getProperty("includeURL");
		//String excludes = properties.getProperty("excludeURL");
		long startRevision = Long.parseLong(properties.getProperty("start"));
		long endRevision = Long.parseLong(properties.getProperty("end"));
		
		//Store all included urls in the List
		List<String> includedUrls = new ArrayList<String>();
		//Set the included path for the project
		if(includes.length() > 0) {
			String[] splitPaths = includes.split(",");
			if(splitPaths.length > 0) {
				for(String path : splitPaths) {
					path = path.trim();
					if(path != "") {
						if(path.startsWith("/")) {
							path = path.substring(1, path.length());
						}
						includedUrls.add(path.trim());
					}	
				}
			}
		}
		
		System.out.println(includedUrls);
		
		//Set the output path for checkout and file path
		Global.setOutputDir(projectName);
		
		//Set the list of selected revisions
		List<Long> selectedRevisions = new ArrayList<Long>();
		String filePath = Global.OUTPUT_DIR + projectName + "_selectedRevisions.xls";
		ExcelReader reader = new ExcelReader(filePath);
		for(int row = 0; row < reader.getRows(); row++) {
			selectedRevisions.add(Long.parseLong(reader. read(0, row)));
		}
		
		//Connect to the selected SVN repository
		SVNRepository repository = RepositoryFactory.getRepository(url, username, password);
		
		//Start to compare
		SVNURL svnurl = null;
		try {
			svnurl = SVNURL.parseURIEncoded(reUrl);
		} catch (SVNException e) {
			e.printStackTrace();
		}
		
		Collection logEntries = null;
		
		SVNClientManager manager = SVNClientManager.newInstance();
		ByteArrayOutputStream bao = new ByteArrayOutputStream();
		/*
		 * Set the paths that mean only those revisions at which they were
		 * changed
		 */
		try {
			logEntries = repository.log(new String[]{}, null, startRevision, endRevision, true, true);		
		} catch (SVNException e) {
			logger.error(e);
		}
		
		//Total changed lines
		long changedLines = 0;
		//Changed lines in selected revisions
		long selectedChangedLines = 0;
		
		List<Long> realChangedRevisions = new ArrayList<Long>();
		
		boolean isDiff = false;
		for (Iterator entries = logEntries.iterator(); entries.hasNext();) {
			SVNLogEntry logEntry = (SVNLogEntry) entries.next();
			long revId = logEntry.getRevision();
			//realChangedRevisions.add(revId);
			Set changedPathsSet = logEntry.getChangedPaths().keySet();
			Iterator changedPathsItor = changedPathsSet.iterator();
			while (changedPathsItor.hasNext()) {
				SVNLogEntryPath entryPath = (SVNLogEntryPath) logEntry.getChangedPaths().get(changedPathsItor.next());
				String path = entryPath.getPath();
				if(path.startsWith("/"))
					path = path.substring(1, path.length());
				if(ListUtil.isStartWith(path, includedUrls)) {
					isDiff = true;
					break;
				}
			}
			
			if(isDiff == true) {
				realChangedRevisions.add(revId);
				isDiff = false;
			} else {
				System.out.println(revId);
			}
		}
		logger.debug(projectName + ": the number of real revisions need to compare is " + realChangedRevisions.size());
		logger.debug(projectName + ": ----------------------------start to compare--------------------------");
		
		long manualSetting = 11179;
		for(int i=1; i<realChangedRevisions.size(); i++) {
			long revId = realChangedRevisions.get(i);
			if(revId > manualSetting) {
				logger.debug(projectName + ": start to compare revision " + (revId-1) + " and revision " + revId);
				SVNRevision oldRev = SVNRevision.create(revId-1);
				SVNRevision newRev = SVNRevision.create(revId);
				try {
					manager.getDiffClient().doDiff(svnurl, SVNRevision.UNDEFINED, oldRev, newRev, SVNDepth.UNKNOWN, true, bao);
				} catch (SVNException e) {
					logger.error("Connection Timeout when comparing revision " + (revId-1) + " and revision " + revId);
					i--;
					bao.reset();
					continue;
				}
				String content = bao.toString();
				
				long changed = getChangedLines(content, includedUrls, Global.POSTFIX);
				logger.debug("The changed code lines in revision " + revId + " is " + changed);
				changedLines += changed;
					
				if(ListUtil.isInList(revId, selectedRevisions)) {
					selectedChangedLines += changed;
				}
				
				logger.info("The total changed lines after revision " + revId + " is " + changedLines);
				logger.info("The total changed lines (selected) after revission "+ revId + " is " + selectedChangedLines);
				
				bao.reset();
			}
			
		}
				
		try {
			bao.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		System.out.println("---------------------------------------------------------");
		System.out.println("Total changed lines in all revisions: " + changedLines);
		System.out.println("Total changed lines in selected revisions: " + selectedChangedLines);
	
		


			
		/*for(long i=startRevision; i<endRevision; i++) {
			logger.info(projectName + ": start to compare revision " + i + " and revision " + (i+1));
			SVNRevision oldRev = SVNRevision.create(i);
			SVNRevision newRev = SVNRevision.create(i+1);
			try {
				manager.getDiffClient().doDiff(svnurl, SVNRevision.UNDEFINED, oldRev, newRev, SVNDepth.UNKNOWN, true, bao);
			} catch (SVNException e) {
				logger.debug("Connection Timeout when comparing revision " + i + " and revision " + (i+1));
				i --;
				bao.reset();
				continue;
			}
			String content = bao.toString();
				
			long changed = getChangedLines(content, includedUrls, Global.POSTFIX);
			logger.info("The changed code lines in revision " + (i+1) + " is " + changed);
			changedLines += changed;
				
			if(ListUtil.isInList(i+1, selectedRevisions)) {
				selectedChangedLines += changed;
			}
			logger.info("The total changed code lines currently is " + changedLines);
			logger.info("The total changed code lines in selected version is " + selectedChangedLines);
			
			bao.reset();
		}
		try {
			bao.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		System.out.println("---------------------------------------------------------");
		System.out.println("Total changed lines in all revisions: " + changedLines);
		System.out.println("Total changed lines in selected revisions: " + selectedChangedLines);*/
	
    }

	private static long getChangedLines(String content, List<String> prefixes, String[] postfixes) {
		long changedLines = 0;
		String path = null;
		String postfix = null;
		boolean flag = false;
		Scanner scanner = new Scanner(content);
		while (scanner.hasNextLine()) {
		  String line = scanner.nextLine().trim();
		  if(line.startsWith("Index")) {
			  String[] lineSeps = line.split(":");
			  if(lineSeps.length > 1) {
				  path = line.split(":")[1].trim();
				  if(path.contains(".")) {
					  int index = path.lastIndexOf(".");
					  postfix = path.substring(index+1, path.length());
				  }
				  
				  if (ListUtil.isStartWith(path, prefixes) && ListUtil.isInArray(postfix, postfixes)) {
					  flag = true;
					 
				  } else {
					  flag = false;
				  }
			  }  
		  }
		  
		  if(((line.startsWith("+") && !line.startsWith("+++")) || (line.startsWith("-") && !line.startsWith("---"))) && flag == true) {
			  changedLines ++;
		  }
		}
		return changedLines;
	}
}