// ============================================================================
// File:               $File$
//
// Project:            
//
// Purpose:            
//
// Author:             Harald Boegeholz / Rammi
//
// Copyright Notice:   (c) Original C++ code: Harald Boegeholz (c't)
//                     (c) Java port: 2008  Rammi (rammi@caff.de)
//                     This code is in the public domain.
//                     Use at own risk.
//                     No guarantees given.
//
// Latest change:      $Date$
//
// History:	       $Log$
//=============================================================================
package de.caff.asteroid;

import java.awt.*;

/**
 *  Sample player quite similar to the one programmed by Harald Boegeholz.
 *
 *  It does not use threading, but instead calculates everything in the frameReceived() method and so
 *  runs in the communication thread. Usually it's preferable to make the calculations in an own thread,
 *  so the communication thread is just doing communication.
 *
 *  Meanwhile I added the enhancement thrust is no longer pressed if no target is available.
 *
 *  This class is part of a solution for a
 *  <a href="http://www.heise.de/ct/creativ/08/02/details/">competition by the German computer magazine c't</a>.
 */
public class SampleAsteroidPlayer
  implements FrameListener,
             GameData
{
  /** Switch between shooting and not shooting. */
  private boolean shot;
  /** The communication object. */
  private Communication com;

  /**
   *  Constructor.
   *  @param com communications object for key sending
   */
  public SampleAsteroidPlayer(Communication com)
  {
    this.com = com;
    com.setFramePreparer(new FramePreparerSequence(new SimpleVelocityPreparer(),
                                                   new ScoreFixer()));
    com.addFrameListener(new HallOfFameFiller(com, "bo"));
  }

  /**
   *  Called each time a frame is received.
   *
   *  <b>ATTENTION:</b> this is called from the communication thread!
   *  Implementing classes must be aware of this and take care by synchronization or similar!
   *  @param frame the received frame
   */
  public void frameReceived(FrameInfo frame)
  {
    if (frame != null) {
      // this is basically Harald's algorithm
      SpaceShip ship = frame.getSpaceShip();
      if (ship != null) {
        MovingGameObject target = findNearestTarget(ship, frame);
        if (target == null) {
          if (frame.getTargetCount() > 0) {
            com.pushButton(BUTTON_THRUST);
          }
        }
        else {
          Point delta = ship.getDelta(target);
          int dist2 = delta.x*delta.x + delta.y*delta.y;
          if (dist2 < 31*31) {
            com.pushButton(BUTTON_HYPERSPACE);
          }
          else {
            if (ship.getDirX() * delta.y - ship.getDirY() * delta.x  > 0) {
              com.pushButton(BUTTON_LEFT);
            }
            else {
              com.pushButton(BUTTON_RIGHT);
            }
          }
        }

        com.setButton(BUTTON_FIRE, shot);
        shot = !shot;
      }
    }
  }

  /**
   *  Find the nearest target. Everything farer away than 400 pixel is considered to be in infinity.
   *  @param ship ship info
   *  @param info frame info
   *  @return nearest target or <code>null</code> if there is no target near enough
   */
  private static MovingGameObject findNearestTarget(SpaceShip ship, FrameInfo info)
  {
    MovingGameObject nearest = null;
    int minDist2 = 400*400;
    for (Asteroid ast: info.getAsteroids()) {
      int dist2 = ship.getSquaredDistance(ast) - ast.getSquaredSize();
      if (dist2 < minDist2) {
        minDist2 = dist2;
        nearest  = ast;
      }
    }
    Ufo ufo = info.getUfo();
    if (ufo != null) {
      int dist2 = ship.getSquaredDistance(ufo) - ufo.getSquaredSize();
      if (dist2 < minDist2) {
        nearest  = ufo;
      }
    }
    //System.out.println("Nearest: "+nearest);
    return nearest;
  }
}
