/**
 * 
 * Written by Márton Münz and Philip C Biggin
 * Copyright (c) University of Oxford, United Kingdom
 * Visit http://sbcb.bioch.ox.ac.uk/jgromacs/
 * 
 * This source code file is part of JGromacs v1.0.
 * 
 * JGromacs v1.0 is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * JGromacs v1.0. is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with JGromacs v1.0. If not, see <http://www.gnu.org/licenses/>.
 * 
 */

package jgromacs.data;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.TreeSet;

/**
 * Objects of this class represent a single index set
 *
 */
public class IndexSet implements Cloneable{

	private String name = "Noname";
	private TreeSet<Integer> indices = new TreeSet<Integer>();
	
	// Constructors
	
	/**
	 * Constructs a new IndexSet object
	 *
	 * 
	 */
	public IndexSet() {
		
	}
	
	/**
	 * Constructs a new IndexSet object of a given name
	 *
	 * 
	 */
	public IndexSet(String name) {
		super();
		this.name = name;
	}
	
	/**
	 * Constructs a new IndexSet object and loads data from an ArrayList
	 *
	 * 
	 */
	public IndexSet(ArrayList<Integer> list) {
		indices = new TreeSet<Integer>(list);
	}
	
	/**
	 * Constructs a new IndexSet object of a given name and loads data from an ArrayList
	 *
	 * 
	 */
	public IndexSet(ArrayList<Integer> list, String name) {
		indices = new TreeSet<Integer>(list);
		this.name = name;
	}

	/**
	 * Constructs a new IndexSet object and loads data from a TreeSet
	 *
	 * 
	 */
	public IndexSet(TreeSet<Integer> set) {
		indices = new TreeSet<Integer>(set);
	}
	
	/**
	 * Constructs a new IndexSet object of a given name and loads data from a TreeSet
	 *
	 * 
	 */
	public IndexSet(TreeSet<Integer> set, String name) {
		indices = new TreeSet<Integer>(set);
		this.name = name;
	}
	
	/**
	 * Constructs a new IndexSet object identical to a given IndexSet
	 *
	 * 
	 */
	public IndexSet(IndexSet set){
		this.name = set.getName();
		indices = new TreeSet<Integer>(set.indices);
	}
	
	// Getters and setters

	/**
	 * Returns the name of index set
	 *
	 * @return Name of index set
	 */
	public String getName() {
		return name;
	}

	/**
	 * Sets the name of index set
	 *
	 * 
	 */
	public void setName(String name) {
		this.name = name;
	}
	
	/**
	 * Returns index set as a TreeSet
	 *
	 * @return index set as a TreeSet
	 */
	public TreeSet<Integer> getAsTreeSet(){
		return new TreeSet<Integer>(indices);
	}

	/**
	 * Returns index set as an ArrayList
	 *
	 * @return index set as an ArrayList
	 */
	public ArrayList<Integer> getAsArrayList(){
		return new ArrayList<Integer>(indices);
	}

	/**
	 * Returns the number of indices in this index set
	 *
	 * @return number of indices
	 */
	public int getNumberOfIndices(){
		return indices.size();
	}
	
	/**
	 * Returns true if the index set contains a given index
	 *
	 *
	 */
	public boolean isIndexIn(int index){
		return indices.contains(index);
	}
	
	// Modifications
	
	/**
	 * Adds a new index to the index set
	 *
	 * 
	 */
	public void addIndex(int index){
		indices.add(index);
	}
	
	/**
	 * Removes an index from the index set
	 *
	 * 
	 */
	public void removeIndex(int index){
		indices.remove(index);
	}
		
	// Set operations
	
	/**
	 * Returns the intersection of this index set and another
	 *
	 * @return intersection of two index sets
	 */
	public IndexSet intersect(IndexSet other) {
	TreeSet<Integer> ret = getAsTreeSet();
	ret.retainAll(other.getAsTreeSet());
	return new IndexSet(ret,"Intersection("+getName()+","+other.getName()+")");
	}

	/**
	 * Returns the subtraction of another index set from this index set
	 *
	 * @return subtraction of two index sets
	 */
	public IndexSet subtract(IndexSet other) {
		TreeSet<Integer> ret = new TreeSet<Integer>(getAsTreeSet());
		ret.removeAll(other.getAsTreeSet());
		return new IndexSet(ret,"Subtraction("+getName()+","+other.getName()+")");
	}

	/**
	 * Returns the union of this index set and another
	 *
	 * @return union of two index sets
	 */
	public IndexSet union(IndexSet other) {
		TreeSet<Integer> ret = getAsTreeSet();
		ret.addAll(other.getAsTreeSet());
		return new IndexSet(ret,"Union("+getName()+","+other.getName()+")");
	}
	
	// toString, equals and clone
	/**
	 * Returns the String representation of index set
	 *
	 * @return String representation
	 */
	public String toString(){
		StringBuffer buf = new StringBuffer("[ "+getName()+" ]");
		Iterator<Integer> itr = this.getAsTreeSet().iterator();
		int i=0;
		while (itr.hasNext()) {
			if (i%15==0) buf.append(" \n");
			else buf.append(" ");
			String numstr = String.valueOf(itr.next());
			if (numstr.length()==3) numstr = " "+numstr;
			if (numstr.length()==2) numstr = "  "+numstr;
			if (numstr.length()==1) numstr = "   "+numstr;
			buf.append(numstr);
			i++;
		}
		return buf.toString();
	}
	
	/**
	 * Returns summary information about the index set
	 *
	 * @return summary information
	 */
	public String toStringInfo(){
		return "Name: "+name+" | ("+getNumberOfIndices()+" indices)";
	}

	/**
	 * Returns true if this index sets is identical to another
	 *
	 * 
	 */
	public boolean equals(Object other){
		if (other == null) return false;
		if (other == this) return true;
		if (this.getClass() != other.getClass()) return false;
		IndexSet group = (IndexSet)other;
		return group.indices.equals(indices)&&group.name.equals(name);
	}
	
	/**
	 * Returns hash code
	 * 
	 */
	public int hashCode() {
		return indices.size()+name.length();
	}
	
	
	/**
	 * Returns an identical IndexSet object 
	 *
	 * @return clone of the index set
	 */
	public Object clone(){
		try {
			IndexSet ret = (IndexSet)super.clone();
			ret.name = name;
			ret.indices = new TreeSet<Integer>();
			Iterator<Integer> itr = indices.iterator();
			while(itr.hasNext()) ret.indices.add(itr.next());
			return ret;
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
			return null;
		}
	}
	
}
