/***************************************************************************
* Wolf: That bad guy that chases after and eats sheep.
***************************************************************************/
class Wolf
{
  /***************************************************************************
  * Variables
  ***************************************************************************/
  
  // Possible states
  private static final int STATE_WAITING       = 0;
  private static final int STATE_DARTING       = 1;
  private static final int STATE_HERDING       = 2;
  
  // Info about icon
  private PImage m_img;
  private float m_scale;
  
  // Location and movement
  private int m_x, m_y, m_direction;
  private float m_speed;
  private int m_timeDarting;
  private int m_timeStopped;
  
  private int m_currentState;
  
  private static final int MAX_TIME_STOPPED = 50;
  private static final int MAX_TIME_DARTING = 20;
  
  // List of all the sheep and other stuff
  private ArrayList m_sheepList;
  private Dog m_dog;
  
  private int m_runAwayCornerX, m_runAwayCornerY;
  
  
  /***************************************************************************
  * Constructor
  ***************************************************************************/
  public Wolf(String imgLoc, float scale, int x, int y, ArrayList sheepList, Dog dog)
  {
    // Load and save the image
    m_img = loadImage(imgLoc);
    m_scale = scale;
    
    // Starting position
    m_x = x;
    m_y = y;
    m_direction = 0;
    m_speed = 0;
    m_timeDarting = 0;
    m_timeStopped = 0;
    
    // List of all the sheep
    m_sheepList = sheepList;
    
    m_dog = dog;
    
    m_currentState = STATE_WAITING;
    
    m_dog.wolfArrived(this);
    for (int s=0; s < m_sheepList.size(); s++)
    {
      ((Sheep)(m_sheepList.get(s))).wolfArrived(this);
    }
    
    m_runAwayCornerX = m_runAwayCornerY = 0;
    
    if (m_dog.isCircling())
    {
      startHerding();
    }
  }
  
  
  /***************************************************************************
  * Getters/setters
  ***************************************************************************/
  
  public void setSpeed(float s) { m_speed = s; }
  //public void setDirection(int d) { m_direction = d; }
  
  public int getW() { return (int)(m_img.width * m_scale); }
  public int getH() { return (int)(m_img.height * m_scale); }
  
  public void setLocation(int x, int y) { m_x = x; m_y = y; }
  
  
  /***************************************************************************
  * Drawing function
  ***************************************************************************/
  public void draw()
  {
    imageMode(CORNER);
    image(m_img, m_x, m_y, (m_img.width*m_scale), (m_img.height*m_scale));
  }
  
  
  /***************************************************************************
  * Check the state of the wolf and move it accordingly.
  ***************************************************************************/
  public void move()
  { 
    if (m_currentState == STATE_WAITING)
    {
      m_timeStopped++;
  
      if (m_timeStopped > MAX_TIME_STOPPED)
      {
        m_currentState = STATE_DARTING;
        m_timeDarting = 0;
        orientToSheep();
      }
    }
    
    if (m_currentState == STATE_DARTING)
    {
      moveDart();
      
      m_timeDarting++;
      
      if (m_timeDarting > MAX_TIME_DARTING)
      {
        m_currentState = STATE_WAITING;
        m_timeStopped = 0;
      }
    }
    
    if (m_currentState == STATE_HERDING)
    {
      moveHerding();
    }
    
    checkSheepCollisions();
  }
  
  
  /***************************************************************************
  * Dart
  ***************************************************************************/
  private void moveDart()
  {
    float changeInX = m_speed * cos(radians(m_direction));
    float changeInY = m_speed * sin(radians(m_direction));
    
    if (!checkBoundaries((int)changeInX, (int)changeInY))
    {
      m_x += (int)changeInX;
      m_y += (int)changeInY;
    }
  }
  
  
  /***************************************************************************
  * Run away from the herd into a corner
  ***************************************************************************/
  private void moveHerding()
  {
    
    if (dist(m_x, m_y, m_runAwayCornerX, m_runAwayCornerY) < 5)
    {
      return;
    }
   
    m_direction = (int)(degrees(atan2(m_runAwayCornerY - m_y, m_runAwayCornerX - m_x)));
    
    float changeInX = m_speed * 2 * cos(radians(m_direction));
    float changeInY = m_speed * 2 * sin(radians(m_direction));
    
    if (!checkBoundaries((int)changeInX, (int)changeInY))
    {
      m_x += (int)changeInX;
      m_y += (int)changeInY;
    }
  }
  
  
  /***************************************************************************
  * Orient towards a sheep
  ***************************************************************************/
  private void orientToSheep()
  {
    if (m_sheepList.size() > 0)
    {
      Sheep s;
      do
      {
        // Pick a random sheep
        s = (Sheep)m_sheepList.get((int)random(1,m_sheepList.size())-1);
        
        // Figure out the angle towards it
        m_direction = s.getAngle(m_x, m_y);

      } while (s.isDeadOrDying() || s.isDoneOrGettingDone());
    }
  }
  
  
  /***************************************************************************
  * Orient to corner
  ***************************************************************************/
  private void orientToCorner()
  {
    int w = (int)(m_img.width * m_scale);
    int h = (int)(m_img.height * m_scale);
    
    int finalX, finalY;
    
    float d[] = new float[4];
    d[0] = d[1] = d[2] = d[3] = -1;
    
    int x[] = new int[4];
    int y[] = new int[4];

    if (!m_dog.withinCirclingRadius(sheepBoundaryX1, sheepBoundaryY1) &&
        !m_dog.crossingCirclingRadius(m_x, m_y, sheepBoundaryX1, sheepBoundaryY1))
    {
      finalX = x[0] = sheepBoundaryX1;
      finalY = y[0] = sheepBoundaryY1;
      
      d[0] = dist(m_x, m_y, finalX, finalY);
    }
    if (!m_dog.withinCirclingRadius(sheepBoundaryX2-w, sheepBoundaryY1) &&
        !m_dog.crossingCirclingRadius(m_x, m_y, sheepBoundaryX2-w, sheepBoundaryY1))
    {
      finalX = x[1] = sheepBoundaryX2-w;
      finalY = y[1] = sheepBoundaryY1;
      
      d[1] = dist(m_x, m_y, finalX, finalY);
    }
    if (!m_dog.withinCirclingRadius(sheepBoundaryX1, sheepBoundaryY2-h) &&
        !m_dog.crossingCirclingRadius(m_x, m_y, sheepBoundaryX1, sheepBoundaryY2-h))
    {
      finalX = x[2] = sheepBoundaryX1;
      finalY = y[2] = sheepBoundaryY2-h;
      
      d[2] = dist(m_x, m_y, finalX, finalY);
    }
    if (!m_dog.withinCirclingRadius(sheepBoundaryX2-w, sheepBoundaryY2-h) &&
        !m_dog.crossingCirclingRadius(m_x, m_y, sheepBoundaryX2-w, sheepBoundaryY2-h))
    {
      finalX = x[3] = sheepBoundaryX2-w;
      finalY = y[3] = sheepBoundaryY2-h;
      
      d[3] = dist(m_x, m_y, finalX, finalY);
    }
    
    float minD = d[0];
    int minIdx = 0;
    for (int i=1; i < 4; i++)
    {
      if (minD > d[i])
      {
        minD = d[i];
        minIdx = i;
      }
    }
    
    m_runAwayCornerX = x[minIdx];
    m_runAwayCornerY = y[minIdx];
    
    m_direction = (int)(degrees(atan2(m_runAwayCornerY-m_y, m_runAwayCornerX-m_x)));
  }
  
  /***************************************************************************
  * Check the boundaries, and adjust the direction accordingly
  ***************************************************************************/
  private boolean checkBoundaries(int dx, int dy)
  {
    boolean hitBoundary = false;
    
    // Left boundary
    if (m_x + dx < sheepBoundaryX1)
    {
      //m_x = sheepBoundaryX1;
      hitBoundary = true;
    }
    
    // Right boundary
    if (m_x + dx + m_scale*m_img.width > sheepBoundaryX2)
    {
      //m_x = sheepBoundaryX2 - (int)(m_scale*m_img.width);
      hitBoundary = true;
    }
    
    // Top boundary
    if (m_y + dy < sheepBoundaryY1) 
    {
      //m_y = sheepBoundaryY1;
      hitBoundary = true;
    }

    // Bottom boundary
    if (m_y + dy + m_scale*m_img.height > sheepBoundaryY2)
    {
      //m_y = sheepBoundaryY2 - (int)(m_scale*m_img.height);
      hitBoundary = true;
    }
   
    return hitBoundary; 
  }
  
  
  /***************************************************************************
  * Check whether we are touching a sheep (unless we are in escape mode)
  ***************************************************************************/
  public void checkSheepCollisions()
  {
    if (m_currentState != STATE_HERDING)
    {
      int w = (int)(m_img.width * m_scale);
      int h = (int)(m_img.height * m_scale);
    
      for (int s=0; s < m_sheepList.size(); s++)
      {
        Sheep sheep = (Sheep)(m_sheepList.get(s));
        if (!sheep.isDeadOrDying() && !sheep.isDoneOrGettingDone())
        {
          if (sheep.collides(m_x, m_x+w, m_y, m_y+h))
          {
            killSheep(sheep);
          }
        }
      }
    }
  }
  
  
  /***************************************************************************
  * Does the wolf collide with the given sheep?
  ***************************************************************************/
  public boolean collides(Sheep s)
  {
    return s.collides(m_x, m_x+getW(), m_y, m_y+getH());
  }
  
  
  /***************************************************************************
  * The wolf collided with a sheep - eat it! :D
  ***************************************************************************/
  public void killSheep(Sheep sheep)
  {
    // Get the sheep to go into kill mode
    sheep.die();
  }
  
  
  /***************************************************************************
  * Does the wolf collide with a given set of boundaries?
  ***************************************************************************/
  public boolean collides(int x1, int x2, int y1, int y2)
  {
//    int x1Me = m_x;
//    int x2Me = m_x + (int)(m_img.width * m_scale);
//    
//    int y1Me = m_y;
//    int y2Me = m_y + (int)(m_img.height * m_scale);
//    
//    if ((x1Me >= x1 && x1Me <= x2 && y1Me >= y1 && y1Me <= y2) ||
//        (x2Me >= x1 && x2Me <= x2 && y2Me >= y1 && y2Me <= y2))
//    {
//      return true;
//    }
//    else
//    {
//      return false;
//    }
    
    int xMe = m_x + (int)(0.5 * m_img.width * m_scale);
    int yMe = m_y + (int)(0.5 * m_img.height * m_scale);
    
    return (xMe > x1 && xMe < x2 && yMe > y1 && yMe < y2);
  }
  
  /***************************************************************************
  * Get the angle from the location to the wolf
  ***************************************************************************/
  public int getAngleTo(int x, int y)
  {
    float rads = atan2(m_y - y, m_x - x);
    return (int)(degrees(rads));
  }
  
  /***************************************************************************
  * Get the angle to the location from the wolf
  ***************************************************************************/
  public int getAngleFromCenter(int x, int y)
  {
    int xWolf = (int)(m_x + m_img.width/2);
    int yWolf = (int)(m_y + m_img.height/2);
    
    float rads = atan2(y - yWolf, x - xWolf);
    return (int)(degrees(rads));
  }
  
  /***************************************************************************
  * Get the distance to the location from the wolf
  ***************************************************************************/
  public int getDistanceFromCenter(int x, int y)
  {
    int xWolf = (int)(m_x + m_img.width/2);
    int yWolf = (int)(m_y + m_img.height/2);
    return (int)dist(x,y,xWolf,yWolf);
  }
  
  /***************************************************************************
  * Start or stop herding behaviour
  ***************************************************************************/
  public void startHerding()
  {
    m_currentState = STATE_HERDING;
    orientToCorner();
  }
  public void stopHerding()
  {
    m_currentState = STATE_DARTING;
    m_timeDarting = 0;
    orientToSheep();
  }
  
}// end class Wolf
