package cn.edu.seu.ise.dca.app.php.model;

import java.util.ArrayList;
import java.util.List;

import cn.edu.seu.ise.dca.util.ListUtil;


public class PHPFileChunk {
	/*original represents the old version of the PHP File*/
	private PHPFile original;
	/*revised represents the new version of the PHP File*/
	private PHPFile revised;
	
	public PHPFileChunk() {
	}
	
	public PHPFileChunk(PHPFile original, PHPFile revised) {
		this.original = original;
		this.revised = revised;
	}

	public PHPFile getOriginal() {
		return original;
	}

	public void setOriginal(PHPFile original) {
		this.original = original;
	}

	public PHPFile getRevised() {
		return revised;
	}

	public void setRevised(PHPFile revised) {
		this.revised = revised;
	}
	
	/**
	 * Return the code lines of original versions 
	 * @return
	 */
	public long sizeOfOriginalLines() {
		return this.original.getLines();
	}
	
	/**
	 * Return the code lines of revised versions
	 * @return
	 */
	public long sizeOfRevisedLines() {
		return this.revised.getLines();
	}
	
	/**
	 * Return the code lines of original versions (without comments and blank lines)
	 * @return
	 */
	public long sizeOfOriginalActLines() {
		return this.original.getActualLines();
	}
	
	/**
	 * Return the actual code lines of revised versions (without comments and blank lines)
	 * @return
	 */
	public long sizeOfRevisedActLines() {
		return this.revised.getActualLines();
	}
	
	/**
	 * Return the number of added classes
	 * @return
	 */
	public int sizeOfAddedClasses() {
		//return sizeOfUnionClasses() - original.sizeOfClasses();
		return this.getAddedClasses().size();
	}
	
	/**
	 * Return the number of deleted classes
	 * @return
	 */
	public int sizeOfDeletedClasses() {
		//return sizeOfUnionClasses() - revised.sizeOfClasses();
		return this.getDeletedClasses().size();
	}
	
	/**
	 * Return the number of added global functions
	 * @return
	 */
	public int sizeOfAddedGlobalFunctions() {
		//return this.sizeOfUnionGlobalFunctions() - this.original.sizeOfGlobalFunctions();
		return this.getAddedGlobalFunctions().size();
	}
	
	/**
	 * Return the number of deleted global functions
	 * @return
	 */
	public int sizeOfDeletedGlobalFunctions() {
		//return this.sizeOfUnionGlobalFunctions() - this.revised.sizeOfGlobalFunctions();
		return this.getDeletedGlobalFunctions().size();
	}
	
	/**
	 * Return the number of added member functions
	 * @return
	 */
	public int sizeOfAddedMemberFunctions() {
		//return this.sizeOfUnionMemberFunctions() - this.original.sizeOfMemberFunctions();
		return this.getAddedMemberFunctions().size();
	}
	
	/**
	 * Return the number of deleted member functions
	 * @return
	 */
	public int sizeOfDeletedMemberFunctions() {
		//return this.sizeOfUnionMemberFunctions() - this.revised.sizeOfMemberFunctions();
		return this.getDeletedMemberFunctions().size();
	}
	
	/**
	 * Return the number of all added functions (including global and member)
	 * @return
	 */
	public int sizeOfAddedFunctions() {
		return this.sizeOfAddedGlobalFunctions() + this.sizeOfAddedMemberFunctions();
	}
	
	/**
	 * Return the number of all deleted functions (including global and member)
	 * @return
	 */
	public int sizeOfDeletedFunctions() {
		return this.sizeOfDeletedGlobalFunctions() + this.sizeOfDeletedMemberFunctions();
	}
	
	/**
	 * Get the list of added classes
	 * @return
	 */
	public List<PHPClass> getAddedClasses() {
		//Get the remaining classes that are extracted from revised version 
		List<PHPClass> remainingClasses = getRemainingClasses(true);
		List<PHPClass> addedClasses = new ArrayList<PHPClass>(this.revised.getClasses());
		//Remove the elements that are in union list from revised version  
		for(PHPClass phpClass : remainingClasses) {
			int index = ListUtil.indexOf(phpClass, addedClasses);
			if(index != -1) {
				addedClasses.remove(index);
			}
		}
		return addedClasses;
	}
	
	/**
	 * Get the list of deleted classes
	 * @return
	 */
	public List<PHPClass> getDeletedClasses() {
		//Get the remaining classes that are extracted from original version 
		List<PHPClass> remainingClasses = getRemainingClasses(false);
		List<PHPClass> deletedClasses = new ArrayList<PHPClass>(this.original.getClasses());
		//Remove the elements that are in union list from revised version  
		for(PHPClass phpClass : remainingClasses) {
			int index = ListUtil.indexOf(phpClass, deletedClasses);
			if(index != -1) {
				deletedClasses.remove(index);
			}
		}
		return deletedClasses;
	}
	
	/**
	 * Get the list of added member functions
	 * @return
	 */
	public List<PHPFunction> getAddedMemberFunctions() {
		//Get the remaining classes that are extracted from revised version 
		List<PHPFunction> remainingFunctions = getRemainingMemberFunctions(true);
		List<PHPFunction> addedMemberFunctions = new ArrayList<PHPFunction>(this.revised.getMemberFunctions());
		//Remove the elements that are in union list from revised version  
		for(PHPFunction phpFunction : remainingFunctions) {
			int index = ListUtil.indexOf(phpFunction, addedMemberFunctions);
			if(index != -1) {
				addedMemberFunctions.remove(index);
			}
		}
		return addedMemberFunctions;
	}
	
	/**
	 * Get the list of deleted member functions
	 * @return
	 */
	public List<PHPFunction> getDeletedMemberFunctions() {
		//Get the remaining member functions that are extracted from original version 
		List<PHPFunction> remainingFunctions = getRemainingMemberFunctions(false);
		List<PHPFunction> deletedMemberFunctions = new ArrayList<PHPFunction>(this.original.getMemberFunctions());
		//Remove the elements that are in union list from revised version  
		for(PHPFunction phpFunction : remainingFunctions) {
			int index = ListUtil.indexOf(phpFunction, deletedMemberFunctions);
			if(index != -1) {
				deletedMemberFunctions.remove(index);
			}
		}
		return deletedMemberFunctions;
	}
	
	/**
	 * Get the list of added global functions
	 * @return
	 */
	public List<PHPFunction> getAddedGlobalFunctions() {
		//Get the remaining global functions that are extracted from original version 
		List<PHPFunction> remainingFunctions = getRemainingGlobalFunctions(true);
		List<PHPFunction> addedGlobalFunctions = new ArrayList<PHPFunction>(this.revised.getGlobalFunctions());
		//Remove the elements that are in union list from revised version  
		for(PHPFunction phpFunction : remainingFunctions) {
			int index = ListUtil.indexOf(phpFunction, addedGlobalFunctions);
			if(index != -1) {
				addedGlobalFunctions.remove(index);
			}
		}
		return addedGlobalFunctions;
	}
	
	/**
	 * Get the list of deleted global functions
	 * @return
	 */
	public List<PHPFunction> getDeletedGlobalFunctions() {
		//Get the remaining global functions that are extracted from original version 
		List<PHPFunction> remainingFunctions = getRemainingGlobalFunctions(false);
		List<PHPFunction> deletedGlobalFunctions = new ArrayList<PHPFunction>(this.original.getGlobalFunctions());
		//Remove the elements that are in union list from revised version  
		for(PHPFunction phpFunction : remainingFunctions) {
			int index = ListUtil.indexOf(phpFunction, deletedGlobalFunctions);
			if(index != -1) {
				deletedGlobalFunctions.remove(index);
			}
		}
		return deletedGlobalFunctions;
	}
	
	/**
	 * Return the list of classes that remains in both original and revised version
	 * @param fromRevised specifies whether the remaining class list is from revised version or original version
	 * @return
	 */
	public List<PHPClass> getRemainingClasses(boolean fromRevised) {
		List<PHPClass> allClasses = new ArrayList<PHPClass>();
		List<PHPClass> remainingClasses = new ArrayList<PHPClass>();
		if(fromRevised) {
			for(PHPClass phpClass : this.original.getClasses()) {
				allClasses.add(phpClass);
			}
			for(PHPClass phpClass : this.revised.getClasses()) {
				if(ListUtil.isInList(phpClass, allClasses)) {
					remainingClasses.add(phpClass);
				}
			}
		} else {
			for(PHPClass phpClass : this.revised.getClasses()) {
				allClasses.add(phpClass);
			}
			for(PHPClass phpClass : this.original.getClasses()) {
				if(ListUtil.isInList(phpClass, allClasses)) {
					remainingClasses.add(phpClass);
				}
			}
		}
		
		return remainingClasses;
	}

	/**
	 * Return the list of member functions that remain in classes of both original and revised version
	 * @param fromRevised specifies whether the remaining member function list is from revised version or original version
	 * @return
	 */
	public List<PHPFunction> getRemainingMemberFunctions(boolean fromRevised) {
		List<PHPFunction> allMemberFunctions = new ArrayList<PHPFunction>();
		List<PHPFunction> remainingMemberFunctions = new ArrayList<PHPFunction>();
		List<PHPClass> remainingFromOriginal = getRemainingClasses(false);
		List<PHPClass> remainingFromRevised = getRemainingClasses(true);
		if(fromRevised) {
			for(PHPClass phpClass : remainingFromOriginal) {
				for(PHPFunction phpFunction : phpClass.getFunctions()) {
					allMemberFunctions.add(phpFunction);
				}
			}
			for(PHPClass phpClass : remainingFromRevised) {
				for(PHPFunction phpFunction : phpClass.getFunctions()) {
					if(ListUtil.isInList(phpFunction, allMemberFunctions)) {
						remainingMemberFunctions.add(phpFunction);
					}
				}
			}
		} else {
			for(PHPClass phpClass : remainingFromRevised) {
				for(PHPFunction phpFunction : phpClass.getFunctions()) {
					allMemberFunctions.add(phpFunction);
				}
			}
			for(PHPClass phpClass : remainingFromOriginal) {
				for(PHPFunction phpFunction : phpClass.getFunctions()) {
					if(ListUtil.isInList(phpFunction, allMemberFunctions)) {
						remainingMemberFunctions.add(phpFunction);
					}
				}
			}
		}
		return remainingMemberFunctions;
	}

	/**
	 * Return the list of global functions that remain in classes of both original and revised version
	 * @param fromRevised specifies whether the remaining global function list is from revised version or original version
	 * @return
	 */
	public List<PHPFunction> getRemainingGlobalFunctions(boolean fromRevised) {
		List<PHPFunction> allGlobalFunctions = new ArrayList<PHPFunction>();
		List<PHPFunction> remainingGlobalFunctions = new ArrayList<PHPFunction>();
		if(fromRevised) {
			
			for(PHPFunction phpGlobalFunction : this.original.getGlobalFunctions()) {
				allGlobalFunctions.add(phpGlobalFunction);
			}
			for(PHPFunction phpGlobalFunction : this.revised.getGlobalFunctions()) {
				if(ListUtil.isInList(phpGlobalFunction, allGlobalFunctions)) {
					remainingGlobalFunctions.add(phpGlobalFunction);
				}
			}
		} else {
			for(PHPFunction phpGlobalFunction : this.revised.getGlobalFunctions()) {
				allGlobalFunctions.add(phpGlobalFunction);
			}
			for(PHPFunction phpGlobalFunction : this.original.getGlobalFunctions()) {
				if(ListUtil.isInList(phpGlobalFunction, allGlobalFunctions)) {
					remainingGlobalFunctions.add(phpGlobalFunction);
				}
			}
		}
		return remainingGlobalFunctions;
	}
}
