package uk.co.patrickhaston.mars;

public class MarsWaypoints extends MarsArray 
{
  public MarsWaypoints(MarsModel model)
  {
    super(model);
    this.CollectionName = "[Waypoints]";
  }
  
  public void readData(String line)
  {
    int idx = readIndex(line);
    if (idx>0)
    {
      MarsWaypoint w = new MarsWaypoint(line);

      this.setElementAt(w, idx);
    }
  }
  
  public LatLong getLocation(int i)
  {
    if(elementAt(i) != null)
    {
      return ((MarsWaypoint) this.elementAt(i)).getLocation();
    }
    return null;
  }
  
  public double distanceTo(int i, LatLong loc)
  {
    if(elementAt(i) != null)
    {
      return ((MarsWaypoint) this.elementAt(i)).distanceTo(loc);
    }
    return 0;
  }
  
  private double distanceTo(int i, MarsWaypoint w)
  {
    if(elementAt(i) != null && w != null)
    {
      return ((MarsWaypoint) this.elementAt(i)).distanceTo(w);
    }
    return 0;
  }
  
  private double distanceTo(int i, int w)
  {
    if(elementAt(i) != null && elementAt(w) != null)
    {
      return ((MarsWaypoint) this.elementAt(i)).distanceTo( (MarsWaypoint) this.elementAt(w) );
    }
    return 0;
  }
  
  public int nearestWaypoint(int w)
  {
    // finds the nearest Way point to the waypoint specified
    if (size()<w) return 0;
    if (elementAt(w) == null) return 0;
    return nearestWaypoint(((MarsWaypoint) elementAt(w)).getLocation());
  }
  
  public int nearestWaypoint(LatLong loc)
  {
    // finds the nearest way point to the location specified
    if (this.size() < 1) return 0;
    if (loc == null) return 0;
    double distance = 0;
    double shortestDistance = -1;
    int nearest = 0;
    MarsWaypoint w = null;
    
    for (int i=1; i<size(); i++)
    {
      w = (MarsWaypoint) elementAt(i);
      if ( w != null)
      {
        if (w.isAt(loc) )
        {
          // skip = we don't want to know if it is the same
        }
        else
        {
          // lets find out the distance
          distance = w.distanceTo(loc);
          if (((distance < shortestDistance) || shortestDistance < 0))
          {
            shortestDistance = distance;
            nearest = i;
          }
        }
      }
    }
    return nearest;
  }
  
  public int[] nearestWaypoints(int w)
  {
    // finds the nearest 3 Waypoints to the waypoint specified

    // initiate the variables
    int wp[] = new int[3];
    double distance[] = new double[3];
    double dist, dist2;
    int i, counter, j, k;
    for (i=0; i<3; i++)
    {
      wp[i] = 0;
      distance[i] = -1;
    }

    // check to prevent errors
    if (size()<w) return wp;
    if (elementAt(w) == null) return wp;

    // initiate the variables
    MarsWaypoint here = (MarsWaypoint) elementAt(w);
    MarsWaypoint there = null;

    // loop through all the waypoints in turn
    for(counter = 1; counter < size(); counter++)
    {
      there = (MarsWaypoint) elementAt(counter);
      if(counter != w && there != null) // no point in checking distance to self
      {
        // find distance from waypoint.loc to counter.loc
        dist = here.distanceTo(there);
        j = counter;
        for (i=0; i<3; i++)
        {
          if(distance[i] < 0 || dist < distance[i])
          {
            k = wp[i];
            dist2 = distance[i];
            wp[i] = j;
            distance[i] = dist;
            dist = dist2;
            j = k;
          }
        }
      }
    }
    return wp;
  }
  
  public void createWaypoints(int density)
  {
    int count, i;
    //double lat, lon;
    MarsWaypoint wp;
    
    for (int e=0; e<36; e++)
    {
      for (int n=-9; n<9; n++)
      {
        count = (int) (((Math.random() * density * 0.3) + density * 0.7) * Math.cos(n*3.14159/18));
        i = 0;
        while (i<count)
        {
          wp = new MarsWaypoint(size(), theModel, (n+Math.random())*10, (e+Math.random())*10);
          // check that it is not too close to an existing waypoint
          if(i > 0)
          {
            double d = distanceTo(nearestWaypoint(wp.getLocation()), wp.getLocation());
            if( d < 0.08 )
            {
              // choose another location - we won't check again
              wp = new MarsWaypoint(size(), theModel, (n+Math.random())*10, (e+Math.random())*10);
            }
          }
          this.addElement(wp);
          i++;
        }
      }
    }
  }
  
  public void createLinks()
  {
    // Initialise variables
    int linked[] = new int [this.size()];
    int count = 0;
    boolean inList = false;
    int [] nearest = null;
    // error checking
    if (size() < 2) return;
    // seed the list with the first entry in the book
    int wp = 1; // the id of the current waypoint in Waypoints
    int index = 0; // the id of the current wp in the Linked array
    linked[count] =(wp);
    count ++;
    // start loop
    if(theModel.logEnabled) theModel.logEvent("MarsWaypoints.creatLinks() starting loop.  Size = " + size());
    while(count < size() - 1) // keep going until every waypoint is linked
    {
      //if(theModel.logEnabled) theModel.logEvent("MarsWaypoints.creatLinks() loop counter " + count);
      // check the current waypoint
      nearest = this.nearestWaypoints(wp);
      if (nearest != null)
      {
        // analyse the results
        for (int i=0; i<nearest.length; i++)
        {
          if (nearest[i] != 0)
          {
            // check to see if these two waypoints are already linked
            if( !theModel.Links.isLinked(wp, nearest[i]) )
            {
              // not linked, so add a new link
              theModel.Links.addLink(wp, nearest[i], distanceTo(wp, nearest[i]));
              // check to see if wp is in our list of linked waypoints (this shouldn't happen very often)
              inList = false;
              for (int j=0; j<count; j++)
              {
                if(linked[j] == wp) inList = true;
              }
              if (!inList)
              {
                linked[count] = wp;
                count++;
              }
              // check to see if it is in our list of linked waypoints
              inList = false;
              for (int j=0; j<count; j++)
              {
                if(linked[j] == nearest[i]) inList = true;
              }
              if (!inList)
              {
                linked[count] = nearest[i];
                count++;
              }
            }
          }
        }
      }
      // find the next wp to check
      index++;
      if (index < count)
      {
        wp = linked[index];
      }
      else
      {
        index--;
        // search for the first unlinked waypoint
        int i = 1;
        wp = 0;
        while (wp == 0 && i < count)
        {
          inList = false;
          for (int j=0; j<count; j++)
          {
            if(linked[j] == i) inList = true;
          }
          if (!inList)
          {
            wp = i;
          }
          i++;
        }
      }
    }
  }
  
  public void linkCreated(int w1, int w2, int link)
  {
    if(w1 >= size()  || w2 >= size()) return;
    if(elementAt(w1) == null || elementAt(w2) == null) return;
    ((MarsWaypoint) elementAt(w1)).addLink(link);
    ((MarsWaypoint) elementAt(w2)).addLink(link);
  }
  
  public int getStoredResources(int w)
  {
    if (w > this.size()) return 0;
    return ((MarsWaypoint)  elementAt(w)).getStoredResources();
  }
  
  public int findNearestUnsurveyed(int w)
  {
    if(w < 1 || w >= size() ) return 0;
    if(!((MarsWaypoint) elementAt(w)).isSurveyed()) return w;
    double distance[] = new double[size()];
    boolean toCheck[] = new boolean[size()];
    int i = 0;
    for (i=1; i<size(); i++)
    {
      distance[i] = -1;
      toCheck[i] = false;
    }
    distance[w] = 0;
    toCheck[w] = true;
    
    double closest = -1;
    int closestWP = 0;
    int[] links = null;
    boolean doneLooking = false;
    //int wp = w; // the current waypoint
    while (!doneLooking)
    {
      // code goes here...
      doneLooking = true; 
      for (i=1; i<size(); i++)
      {
        if(toCheck[i] == true)
        {
          // check this waypoint
          // theModel.systemMessage("Checking " + i);
          links = ((MarsWaypoint) elementAt(i)).getLinks();
          for (int j=0; j<links.length; j++)
          {
            int nextWP = theModel.Links.getOtherEnd(links[j], i);
            if (nextWP > 0)
            {
              double dist = distance[i] + theModel.Links.getAdjustedDistance(links[j]);
              // theModel.systemMessage("Distance from " + i + " to " + nextWP + " is " + dist);
              if( ((closest > 0) && (dist < closest)) 
                || (closest < 0) )
                {
                  if( (distance[nextWP] <= 0) || ((distance[nextWP]>0) && (distance[nextWP] < dist) ))
                  {
                    // found a quicker way to get to the nextWP
                    distance[nextWP] = dist;
                    toCheck[nextWP] = true;
                    doneLooking = false;
                    if ( !((MarsWaypoint) elementAt(i)).isSurveyed() )
                    {
                      // found an unsurveyed one
                      closest = dist;
                      closestWP = nextWP;
                    }
                  }
                }
            }
          
          }
          toCheck[i] = false;
          
        }
      }
      
      
    }
    
    return closestWP;
  }
  
  public void setSurveyed(int wp, int surveyor)
  {
    if(wp < 1 || wp >= size() ) return;
    ((MarsWaypoint) elementAt(wp)).setSurveyed(surveyor);
  }
  
  public String getName(int wp)
  {
    if(wp < 1 || wp >= size() ) return "";
    if(elementAt(wp) == null) return "";
    return ((MarsWaypoint) elementAt(wp)).getName();
  }

  public int[] getConstructions(int wp)
  {
    if (wp < 1) return null;
    if (wp > this.size()) return null;
    return theModel.Constructions.getConstructionsAt(wp);
  }
}
