/*
 *                    BioJava development code
 *
 * This code may be freely distributed and modified under the
 * terms of the GNU Lesser General Public Licence.  This should
 * be distributed with the code.  If you do not have a copy,
 * see:
 *
 *      http://www.gnu.org/copyleft/lesser.html
 *
 * Copyright for this code is held jointly by the individual
 * authors.  These should be listed in @author doc comments.
 *
 * For more information on the BioJava project and its aims,
 * or to join the biojava-l mailing list, visit the home page
 * at:
 *
 *      http://www.biojava.org/
 *
 */

package org.biojava.bio.symbol;

import java.util.*;

/**
 * <p>
 * A class to hide the messiness of CircularLocs from LocationTools
 * </p>
 *
 * <p>
 * <b>WARNING</b> All binary operations on CircularLocations are currently assumed to
 * be performed on Locations from the same sequence, or sequences of the same
 * length. To enforce this binary operations will currently only accept circular
 * locations of the same length.
 * </p>
 *
 * @author Mark Schreiber
 * @author Greg Cox
 * @version 1.0
 */

final class CircularLocationTools {
  /**
   * Translates coordinates from circular into linear
   *
   * @param val The circular coordinate
   * @param length The lenght of the circular molecule
   * @return the transformed coordinate
   */
  private static int realValue(int val, int length){
    val = ((val-1)%length)+1;
    if(val<0)val = length+1 + val;
    if(val == 0) val = 1;
    return val;
  }

  /**
   * Makes a circular location, called by LocationTools.
   *
   * @param min  minimum index of location
   * @param max  maximum index of Location
   * @param seqLength  used to work out if the feature wraps arround the origin
   */
  protected static CircularLocation makeCircLoc(int min, int max, int seqLength){
    if(min == 0|| max == 0 || seqLength == 0){
        throw new IllegalArgumentException(
             "Must use a non zero integer a a parameter"
        );
      }

      boolean overlap = false;
      int rmin = realValue(min, seqLength);
      int rmax = realValue(max, seqLength);

      if(rmin > rmax){
        overlap = true;
        rmax+= seqLength;
      }

      if(!overlap){
        RangeLocation loc = new RangeLocation(rmin,rmax);
        return new CircularLocation(loc,seqLength);
      }else{
        Location locA;
        Location locB;
        Location compound;

        if(rmin == seqLength){
          locB = new PointLocation(rmin);
        }else{
          locB = new RangeLocation(rmin, seqLength);
        }
        if(rmax - seqLength == 1){
          locA = new PointLocation(1);
        }else{
          locA = new RangeLocation(1,rmax - seqLength);
        }
        compound = LocationTools.union(locA,locB);
        return new CircularLocation(compound,seqLength);
      }
  }

  protected static CircularLocation union(CircularLocation locA, CircularLocation locB){
    int length = locA.getLength();
    Location temp;
    if(
      locA.isContiguous() &&
      locB.isContiguous() &&
      locA.overlaps(locB)
    ) {
      // the simple case
      temp =  LocationTools.buildLoc(
        Math.min(locA.getMin(), locB.getMin()),
        Math.max(locA.getMax(), locB.getMax())
      );

      return new CircularLocation(temp, length);

    } else {
      // either may be compound. They may not overlap. We must build the
      // complete list of blocks, merge overlapping blocks and then create the
      // appropriate implementation of Location for the resulting list.

      // list of all blocks
      List locList = new ArrayList();

      // add all blocks in locA
      for(Iterator i = locA.blockIterator(); i.hasNext(); ) {
        locList.add(i.next());
      }

      // add all blocks in locB
      for(Iterator i = locB.blockIterator(); i.hasNext(); ) {
        locList.add(i.next());
      }

      temp = LocationTools._union(locList);
      return new CircularLocation(temp, length);
    }
  }

  /**
   * Tests a location to see if it overlaps the origin of the circular
   * molecule to which it belongs. The origin is taken to be position
   * one.
   *
   * @return true if it does overlap or if it contains the entire sequence
   */
  protected static boolean overlapsOrigin(CircularLocation loc){
    if(loc.getWrapped() instanceof CompoundLocation){
      boolean min = false;
      boolean max = false;
      for(Iterator i = loc.blockIterator(); i.hasNext();){
        Location l = (Location)(i.next());
        if(l.getMax() == loc.getLength()) max = true;
        if(l.getMin() == 1 ) min = true;
      }
      if(min && max){
        return true;
      }else{
        return false;
      }
    }
    else if(loc.getMin() ==1 && loc.getMax() == loc.getLength())return true;
    else return false;
  }


  /**
   * Tests if the specified location is an instance of CircularLocation.
   *
   * @param loc  Locatoin to test
   * @return true if it is a circular location
   */
  protected static boolean isCircular(Location loc){
        boolean toReturn = false;
    try
    {
            if(loc.getDecorator(Class.forName("org.biojava.bio.symbol.CircularLocation")) != null)
            {
                toReturn = true;
            }
        }
        catch(Exception e)
        {
                throw new org.biojava.bio.BioError("class org.biojava.bio.symbol.BetweenLocation could not be loaded");
        }
    return toReturn;
  }


  public static boolean safeOperation(Location locA, Location locB){
    if(isCircular(locA) && isCircular(locB)){
      if (((CircularLocation)locA).getLength() ==
            ((CircularLocation)locB).getLength()) {
        return true;
      }
      else {
        return false;
      }

    }else{
      return false;
    }
  }
}
@@ -1195,16 +1195,175 @@
 o linear
+ where base 1 is the first%0A   * base and base 0 is the base before 1 or the last base of the sequence base%0A   * -1 is the penultimate base of the sequence etc.
 %0A   *%0A  
@@ -1427,18 +1427,18 @@
 The leng
-h
 t
+h
  of the 
@@ -1568,26 +1568,22 @@
 l = 
-(
 (val
--1)%25
+ %25 
 length)
-+1
 ;%0A  
@@ -1594,11 +1594,14 @@
 (val
-%3C0)
+ %3C 1) 
 val 
@@ -1612,18 +1612,16 @@
 ngth
-+1
  + val;%0A
     
@@ -1620,34 +1620,8 @@
 al;%0A
-    if(val == 0) val = 1;%0A
     
@@ -1955,24 +1955,26 @@
 seqLength)%7B%0A
+//
     if(min =
@@ -2008,16 +2008,18 @@
  == 0)%7B%0A
+//
         
@@ -2054,16 +2054,18 @@
 eption(%0A
+//
         
@@ -2113,16 +2113,18 @@
 ameter%22%0A
+//
         
@@ -2126,16 +2126,18 @@
      );%0A
+//
       %7D%0A

update of coordinate system to inteligently deal with 0's