package org.fda.intervaltree;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import org.fda.data.Enums.CoverageType;
import org.fda.exceptions.IllegalIntervalException;



public class Interval implements Comparable<Interval>, Serializable {
    protected List<Interval> subIntervals;
    private static final long serialVersionUID = 1901561012689L;
    protected int count=0;
    protected int low;   // left endpoint
    protected int high;  // right endpoint
    protected int length;
    protected int overlap =0;
    protected double overlapPercentage=0;
    protected CoverageType cov_type= CoverageType.NONE;
   
    protected boolean merged;
    public CoverageType getCoverageType(){
        return cov_type;
    }
    
    public void setOverlap(int ov){
        this.overlap = ov;
        this.overlapPercentage = ((double)100*this.overlap) / (double)this.length;
        this.cov_type = CoverageType.getCoverageType(overlapPercentage);
    }
    
    public int getOverlap(){
        return overlap;
    }
    public double getOverlapPercentage(){
        return overlapPercentage;
    }    
    
    public boolean getMerged(){
        return merged;
    }
    public int getlow(){
        return low;
    }
    public int gethigh(){
        return high;
    }
    public int getlength(){
        return length;
    }
    public void setlow(int low) throws IllegalIntervalException{
        if(low > this.high || low <= 0)
            throw new IllegalIntervalException("Illegal interval: "+low +"\t"+high+"\n");
        this.low = low;
        this.length = high - low + 1;
    }
    public void sethigh(int high) throws IllegalIntervalException{
        if(high < this.low || high <=0)
            throw new IllegalIntervalException("Illegal interval: "+low +"\t"+high+"\n");        
        this.high = high;
        this.length = high - low + 1;
    }
    public int getDistance(Interval i){
        if(intersects(i))
            return -1;
        else
            if(i.getlow() > gethigh())
                return i.getlow() - gethigh() - 1;
            else
                return getlow() - i.gethigh() - 1;
    }
//    public Interval getIntervalBetween(Interval j){        
//        if(!intersects(j)){
//            try{
//                if(j.getlow() > gethigh())
//                    return new Interval(gethigh()+1, j.getlow()-1, false);
//                else
//                    return new Interval(j.gethigh()+1, getlow()-1, false);
//            }
//            catch(IllegalIntervalException iie){iie.printStackTrace();}
//        }
//        return null;
//    }
    public static Interval getIntervalBetween(Interval i, Interval j){        
        if(!i.intersects(j) && i.getDistance(j)!=0){
            try{
                if(j.getlow() > i.gethigh())
                    return new Interval(i.gethigh()+1, j.getlow()-1, false);
                else
                    return new Interval(j.gethigh()+1, i.getlow()-1, false);
            }
            catch(IllegalIntervalException iie){iie.printStackTrace();}
        }
        return null;
    }

    public void incrementCount(){
        count++;
    }
    public int getCount(){
        return count;
    }
    public Interval(int left, int right, boolean merged) throws IllegalIntervalException{
        if (left <= right && right > 0 && left > 0) {
            this.low  = left;
            this.high = right;
            this.length = right - left + 1;
            this.merged = merged;
            this.subIntervals = new ArrayList<>();
        }
        else throw new IllegalIntervalException("Illegal interval: "+left +"\t"+right+"\n");        

    }
    public int overlap(Interval id){
        if(intersects(id)){
            if(id.contains(this.high)){
                if(id.contains(this.low))
                    return this.length;
                else 
                    return this.high - id.getlow() + 1;            
            }
            else {
                if(id.contains(this.low))
                    return id.high - this.low + 1;
                else
                    return id.getlength();
            }
        }
        else
            return 0;
    }
    // does this interval intersect that one?
    public boolean intersects(Interval that) {
        if (that.high < this.low) return false;
        if (this.high < that.low) return false;
        return true;
    }

    // does this interval a intersect b?
    public boolean contains(int x) {
        return (low <= x) && (x <= high);
    }

    public int compareTo(Interval that) {
        if      (this.low  < that.low)  return -1;
        else if (this.low  > that.low)  return +1;
        else if (this.high < that.high) return -1;
        else if (this.high > that.high) return +1;
        else                            return  0;
    }
    
    public List<Interval> getsubIntervals(){
        return subIntervals;
    }
    public boolean add2SubIntervals(Interval id){
        return subIntervals.add(id);
    }

    public String toStringWSubIntervals() {
        return "[" + low + ", " + high + "] => "+subIntervalsToString();
    }
    public String subIntervalsToString(){
        StringBuilder str = new StringBuilder();
        String splitter="," ;
        Interval s;
        Interval[] h = subIntervals.toArray(new Interval[0]);
        for(int i=0; i<h.length; i++){
            s = h[i];
            if(i==h.length-1)
                str.append(s);
            else
                str.append(s+splitter);
        }
        return str.toString();
        
    }

    
    @Override
    public String toString() {
        return "[" + low + ", " + high + "]";
    }
    
    public String toStringTabular() {
        return low + "\t" + high ;
    }
    
    
    public static double MinRO(Interval i1, Interval i2){
        int ov =i1.overlap(i2);
        return(Math.min((double)ov/i1.length, (double)ov/i2.length));        
    }
    public static double MinRO(Interval i1, List<Interval> ilist){
        int ov=0;
        for(Interval i : ilist){
            ov += i1.overlap(i);
        }                
        return(Math.min((double)ov/i1.length, (double)ov/getTotalSize(ilist)));        
    }
    public static double MaxRO(Interval i1, Interval i2){
        int ov =i1.overlap(i2);
        return(Math.max((double)ov/i1.length, (double)ov/i2.length));        
    }
    public static double MaxRO(Interval i1, List<Interval> ilist){
        int ov=0;
        for(Interval i : ilist){
            ov += i1.overlap(i);
        }                
        return(Math.max((double)ov/i1.length, (double)ov/getTotalSize(ilist)));        
    }
    
    private static int getTotalSize(List<Interval> intlist){
        int size=0;
        for(Interval i : intlist)
            size+=i.getlength();
        return size;
        
    }
    

}




