/**
 * Copyright (c) 2001-2018 Mathew A. Nelson and Robocode contributors
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://robocode.sourceforge.net/license/epl-v10.html
 */
package net.sf.robocode.host.proxies;


import net.sf.robocode.host.RobotStatics;
import net.sf.robocode.host.IHostManager;
import net.sf.robocode.peer.IRobotPeer;
import net.sf.robocode.repository.IRobotItem;
import robocode.Rules;
import robocode.robotinterfaces.peer.IJuniorRobotPeer;


/**
 * NOTE: This is temporary implementation of the interface. You should not build any external component on top of it.
 *
 * @author Flemming N. Larsen (original)
 * @author Pavel Savara (contributor)
 */
public class JuniorRobotProxy extends BasicRobotProxy implements IJuniorRobotPeer {

	public JuniorRobotProxy(IRobotItem specification, IHostManager hostManager, IRobotPeer peer, RobotStatics statics) {
		super(specification, hostManager, peer, statics);
	}

	public void turnAndMove(double distance, double radians) {
		if (distance == 0) {
			turnBody(radians);
			return;
		}

		// Save current max. velocity and max. turn rate so they can be restored
		final double savedMaxVelocity = commands.getMaxVelocity();
		final double savedMaxTurnRate = commands.getMaxTurnRate();

		final double absDegrees = Math.abs(Math.toDegrees(radians));
		final double absDistance = Math.abs(distance);

		// -- Calculate max. velocity for moving perfect in a circle --

		// maxTurnRate = 10 * 0.75 * abs(velocity)  (Robocode rule), and
		// maxTurnRate = velocity * degrees / distance  (curve turn rate)
		//
		// Hence, max. velocity = 10 / (degrees / distance + 0.75)

		final double maxVelocity = Math.min(Rules.MAX_VELOCITY, 10 / (absDegrees / absDistance + 0.75));

		// -- Calculate number of turns for acceleration + deceleration --

		double accDist = 0; // accumulated distance during acceleration
		double decDist = 0; // accumulated distance during deceleration

		int turns = 0; // number of turns to it will take to move the distance

		// Calculate the amount of turn it will take to accelerate + decelerate
		// up to the max. velocity, but stop if the distance for used for
		// acceleration + deceleration gets bigger than the total distance to move
		for (int t = 1; t < maxVelocity; t++) {

			// Add the current velocity to the acceleration distance
			accDist += t;

			// Every 2nd time we add the deceleration distance needed to
			// get to a velocity of 0
			if (t > 2 && (t % 2) > 0) {
				decDist += t - 2;
			}

			// Stop if the acceleration + deceleration > total distance to move
			if ((accDist + decDist) >= absDistance) {
				break;
			}

			// Increment turn for acceleration
			turns++;

			// Every 2nd time we increment time for deceleration
			if (t > 2 && (t % 2) > 0) {
				turns++;
			}
		}

		// Add number of turns for the remaining distance at max velocity
		if ((accDist + decDist) < absDistance) {
			turns += (int) ((absDistance - accDist - decDist) / maxVelocity + 1);
		}

		// -- Move and turn in a curve --

		// Set the calculated max. velocity
		commands.setMaxVelocity(maxVelocity);

		// Set the robot to move the specified distance
		setMoveImpl(distance);
		// Set the robot to turn its body to the specified amount of radians
		setTurnBodyImpl(radians);

		// Loop thru the number of turns it will take to move the distance and adjust
		// the max. turn rate so it fit the current velocity of the robot
		for (int t = turns; t >= 0; t--) {
			commands.setMaxTurnRate(getVelocity() * radians / absDistance);
			execute(); // Perform next turn
		}

		// Restore the saved max. velocity and max. turn rate
		commands.setMaxVelocity(savedMaxVelocity);
		commands.setMaxTurnRate(savedMaxTurnRate);
	}
}
