package mock.model.commandFactory;

import mock.model.MockBoat;
import mock.model.MockRace;
import shared.model.Bearing;

import java.util.Observable;

/**
 * Command class for tacking and gybing
 */
public class TackGybeCommand extends ObserverCommand {
    private double goalRotation;
    private double totalRotation = 0;
    private int direction;  // -1 for anticlockwise, 1 for clockwise
    private double goalAngle;

    /**
     * Constructor for class
     * @param race mock race
     * @param boat mock boat to update
     */
    public TackGybeCommand(MockRace race, MockBoat boat) {
        super(race, boat);
        race.addAngularCommand(this);
    }

    @Override
    public void execute() {
        double boatAngle = boat.getBearing().degrees();
        double windAngle = race.getWindDirection().degrees();
        double differenceAngle = calcDistance(boatAngle, windAngle);
        double angleA = windAngle + differenceAngle;
        double angleB = windAngle - differenceAngle;
        if (angleA % 360 == boatAngle) {
            goalAngle = angleB % 360;
        } else {
            goalAngle = angleA % 360;
        }

        goalRotation = goalAngle - boatAngle;
        if (goalRotation < 0) {
            goalRotation += 360;
        }
        if (goalRotation > 180) {
            goalRotation = 360 - goalRotation;
            direction = -1;
        } else {
            direction = 1;
        }
    }

    /**
     * Method to calculate smallest angle between 2 angles
     * @param degreeA first angle degree
     * @param degreeB second angle degree
     * @return the calculated smallest angle
     */
    public double calcDistance(double degreeA, double degreeB){
        double phi = Math.abs(degreeB - degreeA) % 360;
        return phi > 180 ? 360 - phi : phi;
    }

    @Override
    public void update(Observable o, Object arg) {
        double offset = 3.0;
        if (totalRotation < goalRotation) {
            boat.setBearing(Bearing.fromDegrees(boat.getBearing().degrees() + offset * direction));
            totalRotation += offset;
        } else {
            boat.setBearing(Bearing.fromDegrees(goalAngle));
            race.deleteObserver(this);
        }
    }
}

