
package org.fda.alignment;

import org.fda.data.Enums.Orientation;
import org.fda.data.Reference;
import org.fda.data.Utilities;
import org.fda.exceptions.IllegalIntervalException;

/**
 *
 * @author Gokhan.Yavas
 */
public class NucmerAlignment extends Alignment{
    private int[] deltas;
    private double sim;
    private double stp;
    
    private int simc;
    private int idc;
    private int stpc;
    private static final long serialVersionUID = -159257338189L;


    public NucmerAlignment(Reference refID, int s_Ref, int e_Ref, int s_Cont, int e_Cont, /*int ref_Len, int contig_Len, */int idc, int simc, int stpc,     
            double id, double sim, double stp, int[] deltas) throws IllegalIntervalException{
        super(refID, s_Ref, e_Ref, s_Cont, e_Cont, id);
        this.deltas = deltas;
        this.simc = simc;
        this.stpc = stpc;
        this.idc = idc;       
        this.stp = stp;
        this.sim = sim;        
    }
    public void resetDelta(){
        deltas = null;
    }
    public int[] getDelta(){
        return deltas;
    }
    @Override
    public void setContEnd(int e_Cont) throws IllegalIntervalException{
//        if(e_Cont > this.e_Cont)
        if(e_Cont > contigInterval.gethigh())        
            throw new RuntimeException("New end coordinate for this alignment exceeds the old end coordinate"+Utilities.ls+"Coordinates can only be shrunk!");        
        int prevC, prevR, displacement, currdelta, i, indC, indR;
//        if(deltas.length==0 || idt == InputDataType.Read){
//        if(deltas.length==0){
        if(deltas==null){
            super.setContEnd(e_Cont);
            return;
        }
        int[] newDelta=null;
        endTrimmed = true;
        if(this.contigOr==Orientation.F){        
            // Find where this new e_Cont falls in terms of the deltas
//            indC=this.s_Cont - 1;
//            indR=this.s_Ref - 1;
            indC = contigInterval.getlow()-1;
            indR = referenceInterval.getlow()-1;
            for(i =0; i<deltas.length; i++){
                prevC = indC;
                prevR = indR;            
                currdelta = Math.abs(deltas[i]);
                if(deltas[i]<0){
                    indC = indC + currdelta;
                    indR = indR + currdelta-1;
                    // insertion in the contig, deletion in the reference
                    if(indC == e_Cont){
//                        e_Ref = indR;
                        referenceInterval.sethigh(indR);
                        newDelta = new int[i+1];
                        newDelta[i] = deltas[i];
                        break;
                    }
                    else if(indC  > e_Cont){
                        // stop here and do the adjustment on the deltas[i]
                        displacement = e_Cont - prevC;
//                        e_Ref = prevR + displacement;
                        referenceInterval.sethigh(prevR + displacement);
                        newDelta = new int[i];
                        break;
                    }
                }
                else{
                    indC = indC + currdelta-1;
                    indR = indR + currdelta;
                    // insertion in the reference, deletion in the contig
                    if(indC == e_Cont){
//                        e_Ref = indR-1;
                        referenceInterval.sethigh(indR-1);
                        newDelta = new int[i];
                        break;
                    }                
                    else if(indC  > e_Cont){
                        // stop here and do the adjustment on the deltas[i]
                        displacement =  e_Cont- prevC  ;
//                        e_Ref = prevR + displacement;
                        referenceInterval.sethigh(prevR + displacement);
                        newDelta = new int[i];
                        break;
                    }
                }
            }
            i--;
            if(newDelta!=null){
                for(int k=i; k>=0; k--){
                    newDelta[k]=deltas[k];            
                }
                this.deltas=newDelta;
            }
            else{
                // we looped over all the deltas but none of them is bigger than the e_Cont
                // so we should include all the deltas , no change needed for the deltas array
//                e_Ref = indR+(e_Cont-indC);            
                referenceInterval.sethigh(indR+(e_Cont-indC));
            }
        }
        else{
            // this is the case where actually we are to find the start of the alignment in the reference, since it is in the reverse direction
//            indC=this.e_Cont + 1;
//            indR=this.s_Ref - 1;
            indC = contigInterval.gethigh() + 1;
            indR = referenceInterval.getlow() - 1;
            for(i =0; i<deltas.length; i++){
                prevC = indC;
                prevR = indR;            
                currdelta = Math.abs(deltas[i]);
                if(deltas[i]<0){
                    indC = indC - currdelta;
                    indR = indR + currdelta-1;
                    // insertion in the contig, deletion in the reference
                    if(indC == e_Cont){
//                        s_Ref = indR+1;
                        referenceInterval.setlow(indR+1);
                        newDelta = new int[deltas.length-i];
                        newDelta[0] = -1;
                        break;
                    }
                    else if(indC  < e_Cont){
                        // stop here and do the adjustment on the deltas[i]
                        displacement = prevC - e_Cont;
//                        s_Ref = prevR + displacement;
                        referenceInterval.setlow(prevR + displacement);
                        newDelta = new int[deltas.length-i];
                        newDelta[0] = -(e_Cont - indC + 1);
                        break;
                    }
                }
                else{
                    indC = indC - currdelta + 1;
                    indR = indR + currdelta;
                    // insertion in the reference, deletion in the contig
                    if(indC == e_Cont){
//                        this.s_Ref = indR-1;
                        referenceInterval.setlow(indR-1);
                        newDelta = new int[deltas.length-i];
                        newDelta[0]=2;
                        break;

                    }                
                    else if(indC  < e_Cont){                        
                        // stop here and do the adjustment on the deltas[i]
                        displacement =  prevC  - e_Cont  ;
//                        this.s_Ref = prevR + displacement;
                        referenceInterval.setlow(prevR + displacement);
                        newDelta = new int[deltas.length-i];
//                        newDelta[0] = indR - s_Ref + 1;
                        newDelta[0] = indR - referenceInterval.getlow() + 1;
                        break;
                    }
                }
            }
            i++;
            if(newDelta!=null){
                for(int k=1; k<newDelta.length; k++, i++){
                    newDelta[k]=deltas[i];            
                }
                this.deltas=newDelta;
            }
            else{
                // we looped over all the deltas but none of them is bigger than the e_Cont
                // so we should include all the deltas , no change needed for the deltas array
//                this.s_Ref = indR+(indC-e_Cont);  
                referenceInterval.setlow(indR+(indC-e_Cont));
                this.deltas = new int[0];
            }
        }
//        this.e_Cont=e_Cont;
        contigInterval.sethigh(e_Cont);
//        this.contig_alignment_Len = this.e_Cont-this.s_Cont+1;        
//        this.ref_alignment_Len =  e_Ref -s_Ref +1;
//        System.out.println(this);
//        System.out.println(this.getDeltaRepresentation());
        
    }
    @Override
    public void setContSt(int s_Cont) throws IllegalIntervalException{
        int prevC, prevR, displacement, currdelta, i, indC, indR;
//        if(s_Cont < this.s_Cont)
        if(s_Cont < contigInterval.getlow())        
            throw new RuntimeException("New start coordinate for this alignment is smaller than the old start coordinate"+Utilities.ls+"Coordinates can only be shrunk!");
        
//        if(deltas.length==0 || idt == InputDataType.Read){
//        if(deltas.length==0){
        if(deltas==null){
            super.setContSt(s_Cont);
            return;
        }
        startTrimmed = true;
        int[] newDelta=null;
        if(this.contigOr==Orientation.F){                
            // Find where this new s_Cont falls in terms of the deltas
//            indC=this.s_Cont - 1;
//            indR=this.s_Ref - 1;
            indC = contigInterval.getlow() - 1;
            indR = referenceInterval.getlow() - 1;
            for(i =0; i<deltas.length; i++){
                prevC = indC;
                prevR = indR;            
                currdelta = Math.abs(deltas[i]);
                if(deltas[i]<0){
                    indC = indC + currdelta;
                    indR = indR + currdelta-1;
                    // insertion in the contig, deletion in the reference
                    if(indC == s_Cont){
//                        s_Ref = indR+1;
                        referenceInterval.setlow(indR+1);
                        newDelta = new int[deltas.length-i];
                        newDelta[0] = -1;
                        break;
                    }
                    else if(indC  > s_Cont){
                        // stop here and do the adjustment on the deltas[i]
                        displacement = s_Cont - prevC;
//                        s_Ref = prevR + displacement;
                        referenceInterval.setlow(prevR + displacement);
                        newDelta = new int[deltas.length-i];
                        newDelta[0] = -(indC - s_Cont + 1);
                        break;
                    }
                }
                else{
                    indC = indC + currdelta-1;
                    indR = indR + currdelta;
                    // insertion in the reference, deletion in the contig
                    if(indC == s_Cont){
//                        s_Ref = indR-1;
                        referenceInterval.setlow(indR-1);
                        newDelta = new int[deltas.length-i];
//                        newDelta[0] = indR-s_Ref+1;
                        newDelta[0] = indR-referenceInterval.getlow()+1;
                        break;
                    }                
                    else if(indC  > s_Cont){
                        // stop here and do the adjustment on the deltas[i]
                        displacement = s_Cont - prevC;
//                        s_Ref = prevR + displacement;
                        referenceInterval.setlow(prevR + displacement);
                        newDelta = new int[deltas.length-i];
                        newDelta[0] = (indC - s_Cont + 1);
                        break;
                    }                
                }
            }
            i++;
            if(newDelta!=null){
                for(int k=1; k<newDelta.length; k++, i++){
                    newDelta[k]=deltas[i];            
                }
                this.deltas=newDelta;
            }
            else{
                // we skipped all the deltas so
//                s_Ref = e_Ref-(e_Cont-s_Cont);
                referenceInterval.setlow(referenceInterval.gethigh()-(contigInterval.gethigh()-s_Cont));
                this.deltas = new int[0];
            }                   
        }
        else{
            // this is the case where actually we are to find the end of the alignment in the reference, since it is in the reverse direction
//            indC=this.e_Cont + 1;
//            indR=this.s_Ref - 1;  
            indC=contigInterval.gethigh() + 1;
            indR=referenceInterval.getlow() - 1;            
            
            for(i =0; i<deltas.length; i++){
                prevC = indC;
                prevR = indR;            
                currdelta = Math.abs(deltas[i]);
                if(deltas[i]<0){
                    indC = indC - currdelta;
                    indR = indR + currdelta-1;
                    // insertion in the contig, deletion in the reference
                    if(indC == s_Cont){
//                        e_Ref = indR;
                        referenceInterval.sethigh(indR);
                        newDelta = new int[i+1];
                        newDelta[i] = -(indR - prevR + 1);
                        
                        break;
                    }
                    else if(indC  < s_Cont){
                        // stop here and do the adjustment on the deltas[i]
                        displacement = prevC - s_Cont;
//                        e_Ref = prevR + displacement;
                        referenceInterval.sethigh(prevR + displacement);
                        newDelta = new int[i];
                        break;
                    }
                }
                else{
                    indC = indC - currdelta + 1;
                    indR = indR + currdelta;
                    // insertion in the reference, deletion in the contig
                    if(indC == s_Cont){
//                        this.e_Ref = indR-1;
                        referenceInterval.sethigh(indR-1);
                        newDelta = new int[i];
                        break;
                    }                
                    else if(indC  < s_Cont){
                        // stop here and do the adjustment on the deltas[i]
                        displacement =  prevC  -  s_Cont  ;
//                        this.e_Ref = prevR + displacement;
                        referenceInterval.sethigh(prevR + displacement);
                        newDelta = new int[i];                        
                        break;
                    }
                }
            }
            i--;
            if(newDelta!=null){
                for(int k=0; k<=i; k++){
                    newDelta[k]=deltas[k];            
                }
                this.deltas=newDelta;
            }
            else{
                // we looped over all the deltas but none of them is bigger than the e_Cont
                // so we should include all the deltas , no change needed for the deltas array
//                this.e_Ref = indR+(indC-s_Cont);            
                referenceInterval.sethigh(indR+(indC-s_Cont));
            }
            
        }
//        this.s_Cont=s_Cont;
        contigInterval.setlow(s_Cont);
//        this.contig_alignment_Len = this.e_Cont-this.s_Cont+1;        
//        this.ref_alignment_Len =  e_Ref -s_Ref +1;       
//        System.out.println(this);
//        System.out.println(this.getDeltaRepresentation());

    }    
    
    public String getDeltaRepresentation(){
        StringBuilder sb = new StringBuilder();        
//        sb.append((this.refOr==Enums.Orientation.F)?(s_Ref+" "+e_Ref+" "):(e_Ref+" "+s_Ref+" ")); 
        sb.append((this.refOr==Orientation.F)?(referenceInterval.getlow()+" "+referenceInterval.gethigh()+" "):(referenceInterval.gethigh()+" "+referenceInterval.getlow()+" ")); 
//        sb.append((this.contigOr==Enums.Orientation.F)?(s_Cont+" "+e_Cont+" "):(e_Cont+" "+s_Cont+" "));
        sb.append((this.contigOr==Orientation.F)?(contigInterval.getlow()+" "+contigInterval.gethigh()+" "):(contigInterval.gethigh()+" "+contigInterval.getlow()+" "));
        sb.append(idc).append(" ").append(simc).append(" ").append(stpc).append(Utilities.ls);
        if(deltas!=null)
            for(int i:deltas){
                sb.append(i).append(Utilities.ls);
            }
        sb.append("0").append(Utilities.ls);
        
        return sb.toString();
    }
    
}
