package hwo.kurjatturskat.model;

import hwo.kurjatturskat.core.message.CarIdentifier;
import hwo.kurjatturskat.core.message.CrashMsg;
import hwo.kurjatturskat.core.message.SpawnMsg;
import hwo.kurjatturskat.core.message.carpositions.CarPosition;
import hwo.kurjatturskat.core.message.carpositions.CarPositionsMsg;
import hwo.kurjatturskat.core.message.carpositions.PiecePosition;
import hwo.kurjatturskat.core.message.gameinit.GameInitMsg;
import hwo.kurjatturskat.core.message.gameinit.TrackPieces;

import java.util.ArrayList;

public class World {

    private TrackModel trackModel;
    private CarIdentifier yourCar;
    private boolean onTrack;

    private ArrayList<TrackPosition> myCarTravels = new ArrayList<TrackPosition>();

    private TrackPosition previousPosition;

    private double distanceTraveled = 0.0;

    public void update(CarPositionsMsg msg) {
        PiecePosition myPiecePos = this.getPiecePositionForCar(msg.getData(),
                this.yourCar);
        trackModel.setCurrentPiece(myPiecePos.pieceIndex);
        TrackPosition trackPos = new TrackPosition(msg.gameTick,
                myPiecePos.pieceIndex, myPiecePos.inPieceDistance,
                myPiecePos.lane);

        // this.myCarTravels.add(trackPos);

        double speed = 0.0;

        if (this.previousPosition != null) {
            this.distanceTraveled += this.getDistance(this.previousPosition,
                    trackPos);
            speed = this.getSpeed(this.previousPosition, trackPos);
        }
        this.previousPosition = trackPos;
        System.out.println("Speed (" + myPiecePos.pieceIndex + ", distance "
                + this.distanceTraveled + "): " + speed);

    }

    public void update(GameInitMsg message) {
        this.trackModel = new TrackModel(message.getData().race.track.pieces);
    }

    public void setYourCarId(CarIdentifier carId) {
        this.yourCar = carId;
        this.onTrack = true;
    }

    public boolean onTrack() {
        return this.onTrack;
    }

    /**
     * Retrieves the piecePosition for given car identifier from car position
     * data.
     * 
     * @param data
     * @param carId
     * @return Null is returned if we have no position data for given car id.
     */
    private PiecePosition getPiecePositionForCar(CarPosition[] data,
            CarIdentifier carId) {
        for (CarPosition posData : data) {
            CarIdentifier id = posData.id;
            if (carId.color.equals(id.color)) {
                return posData.piecePosition;

            }
        }
        System.err.println("Could not determine yourself from car positions!");
        return null;
    }

    public TrackModel getTrackModel() {
        return trackModel;
    }

    public boolean isInitialized() {
        return this.trackModel != null && this.yourCar != null;
    }

    public void setOffTrack(CrashMsg message) {
        if (this.yourCar.name.equals(message.getData().name)
                && this.yourCar.color.equals(message.getData().color)) {
            System.out.println("We crashed!");
            this.onTrack = false;
        }
    }

    public void setOnTrack(SpawnMsg message) {
        if (this.yourCar.name.equals(message.getData().name)
                && this.yourCar.color.equals(message.getData().color)) {
            System.out.println("We are back on track!");
            this.onTrack = true;
        }
    }

    /**
     * Retrieve the speed between two track positions.
     * 
     * @param start
     * @param end
     * @return
     */
    public double getSpeed(TrackPosition start, TrackPosition end) {
        int startTime = start.gameTick;
        int endTime = end.gameTick;

        double distance = this.getDistance(start, end);

        return distance / (endTime - startTime);
    }

    /**
     * Tells distance between two following track positions.
     * 
     * @param start
     * @param end
     * @return
     */
    public double getDistance(TrackPosition start, TrackPosition end) {
        if (start == null) {
            return end.inPieceDistance;
        }
        if (start.pieceIndex == end.pieceIndex) {
            // no handling of lanes yet
            return end.inPieceDistance - start.inPieceDistance;
        }
        double distance = 0.0;
        TrackPieces startPiece = this.trackModel
                .getPieceForIndex(start.pieceIndex);
        if (startPiece.isCurve()) {
            distance += ((startPiece.angle / 360) * 2 * Math.PI)
                    * startPiece.radius - start.inPieceDistance;
        } else {
            distance += startPiece.length - start.inPieceDistance;
        }
        distance += end.inPieceDistance;

        return distance;
    }
}

@@ -430,24 +430,83 @@
 ameInitMsg;%0A
+import hwo.kurjatturskat.core.message.gameinit.TrackLanes;%0A
 import hwo.k
@@ -831,24 +831,57 @@
 sPosition;%0A%0A
+    private TrackLanes%5B%5D lanes;%0A%0A
     private 
@@ -1904,24 +1904,81 @@
 ck.pieces);%0A
+        this.lanes = message.getData().race.track.lanes;%0A
     %7D%0A%0A    p
@@ -4290,48 +4290,8 @@
 ) %7B%0A
-            // no handling of lanes yet%0A
     
@@ -4525,24 +4525,475 @@
 sCurve()) %7B%0A
+            // handle lane distance%0A            double correctedRadius = startPiece.radius;%0A            double ourLaneOffset = 0.0;%0A            for (TrackLanes lane : this.lanes) %7B%0A                if (start.lane.endLaneIndex == lane.id) %7B%0A                    ourLaneOffset = lane.distanceFromCenter;%0A                    break;%0A                %7D%0A            %7D%0A            if (startPiece.angle %3C 0) %7B%0A                ourLaneOffset *= -1;%0A            %7D%0A%0A
             
@@ -5067,16 +5067,17 @@
       * 
+(
 startPie
@@ -5087,16 +5087,53 @@
 radius -
+ ourLaneOffset)%0A                    -
  start.i

Handle correctly speed calculation