/*
  This file is part of the BJP package.

  BJP package is free software: you can redistribute it and/or modify it
  under the terms of the GNU Lesser General Public License as published by the
  Free Software Foundation, either version 3 of the License, or (at your
  option) any later version.

  BJP package 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 Lesser General Public
  License for more details.

  You should have received a copy of the GNU Lesser General Public License
  along with BJP package.  If not, see <http://www.gnu.org/licenses/>.
 */
package dataset.feature;

import java.util.BitSet;
import java.util.List;


/**
 * A binary feature is represented as an array of boolean.
 *
 **/
public class BinaryFeature extends Feature {
	private final BitSet val;

	public BinaryFeature(int n) {
		super(n);
		this.val = new BitSet(n);
	}

        public BinaryFeature(BinaryFeature f) {
	        super(f.getNumberOfDomainVariables(), f.getVar());
		this.val = new BitSet(f.getNumberOfDomainVariables());

		for(int i = f.val.nextSetBit(0); i >= 0; i = f.val.nextSetBit(i+1))
			this.addVal(i, 1);
        }

        @Override
	public boolean isSatisfied(Feature otherFeature) {
		for(int i = this.getVar().nextSetBit(0); i >= 0; i = this.getVar().nextSetBit(i+1)) {
			if(!otherFeature.getVar().get(i)) return false;

			if (this.getVal(i) != otherFeature.getVal(i)) return false;
		}

		return true;
	}


        @Override
	public void addVal(int var, Integer val) {
	        if (val == null) {
			this.val.set(var, false);
			this.rmVar(var);
			return;
		}

		this.addVar(var);

		if (val == 0) this.val.set(var, false);
		else this.val.set(var, true);
	}

        @Override
	public Integer getVal(int var) {
	        return this.val.get(var) ? 1 : 0;
	}


        @Override
	public int getLength() {
		return this.getVar().cardinality();
	}

        @Override
	public BinaryFeature clone() {
		return new BinaryFeature(this);
	}

	@Override
	public int hashCode() {
		int hash = super.hashCode();
		hash = 23 * hash + (this.val == null ? 0 : this.val.hashCode());
		return hash;
	}

	@Override
	public boolean equals(Object obj) {
		if (obj == null || getClass() != obj.getClass()) {
			return false;
		}
		final BinaryFeature other = (BinaryFeature) obj;
		return this.getVar().equals(other.getVar()) && this.val.equals(other.val);
	}


        @Override
	public String toString() {
		StringBuilder stringBuilder = new StringBuilder();

		for(int i=this.getVar().nextSetBit(0); i>=0; i=this.getVar().nextSetBit(i+1)) {
//			stringBuilder.append("+v" + i + "_" + getVal(i) + " ");
			stringBuilder.append(getVal(i) + " ");
		}

		return stringBuilder.toString().trim();
	}


	public BitSet getValues() {
		return this.val;
	}
	
	public void setValues(BitSet values) {
		for(int i = values.nextSetBit(0); i >= 0; i = values.nextSetBit(i+1))
			this.addVal(i, 1);
	}
	
	/*
	 * Returns a subset of this feature containing the variables set
	 * in the BitSet variables.
	 * 
	 * @param variables		The subset of variables in the feature to be returned 
	 */
	public Feature getSubset(BitSet variables) {
		if(variables.length() > this.getLength())
			try {
				throw new IllegalArgumentException("Length of subset should be equal to this feature's length.");
			} catch (IllegalArgumentException e) {
				System.out.println(e.getMessage());
			}
		
		
		Feature subset = new BinaryFeature(variables.length());
		for(int i=variables.nextSetBit(0); i>=0; i=variables.nextSetBit(i+1)) {
			subset.addVar(i);
			subset.addVal(i, this.getVal(i));
		}
		return subset;
	}

	@Override
	public Feature getSubset(List<Integer> blanketOfX) {
		Feature subset = new BinaryFeature(blanketOfX.size());
		for (Integer var : blanketOfX) {
			subset.addVal(var, this.getVal(var));
		}
		
		return subset;
	}

}