package de.hems.trafficsim;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.SurfaceHolder;

import java.util.ConcurrentModificationException;
import java.util.List;

/**
 * User interface class rendering the track history on a SurfaceView.
 */
public class Renderer {
    /**
     * the track to render
     */
    protected Track track;
    /**
     * width of a rectangle representing one vehicle
     */
    protected int pixelPerVehicle;
    /**
     * height of a rectangle representing one vehicle
     */
    protected int pixelPerLine;
    /**
     * amount of pixels per track position which are lost by rounding
     */
    protected float tooShortPerTrackLength;
    /**
     * amount of pixels per height which are lost by rounding
     */
    protected float tooShortPerHeight;
    /**
     * Paint instance of the renderer
     */
    protected Paint paint;
    /**
     * holder of the surface the renderer draws to
     */
    protected SurfaceHolder holder;
    /**
     * width of the surface to draw to
     */
    protected int width;
    /**
     * height of the surface to draw to
     */
    protected int height;
    /**
     * temporary reference to the time records of one simulation step
     */
    private List<VehicleTimeRecord> stepRecords;
    /**
     * temporary reference to the canvas to draw to
     */
    private Canvas canvas;

    /**
     * Constuctor for a Renderer.
     *
     * @param track  the track to render
     * @param holder the holder of the surface to draw to
     */
    public Renderer(Track track, SurfaceHolder holder) {
        this.track = track;
        this.holder = holder;
        this.paint = new Paint();
    }

    /**
     * Updates the dimension information of the renderer.
     *
     * @param width  the width of the SurfaceView
     * @param height the height of the SurfaceView
     */
    public void setSize(int width, int height) {
        if (width > 0 && height > 0) {
            this.pixelPerVehicle = width / (int) this.track.getTrackLength();
            this.tooShortPerTrackLength =
                    (width - this.pixelPerVehicle * this.track.getTrackLength())
                            / this.track.getTrackLength();
            this.pixelPerLine = height / this.track.getHistoryLength();
            this.tooShortPerHeight = (height - this.pixelPerLine * this.track.getHistoryLength())
                    / (float) height;
            System.out.println("Viewport: " + width + "x" + height);
            this.width = width;
            this.height = height;
        }
    }

    /**
     * Updates the track of the Surface.
     *
     * @param track the new track to render
     */
    public void setTrack(Track track) {
        this.track = track;
        this.setSize(width, height);
    }

    /**
     * Utility function which calculates a color from the relation between the current speed of a
     * vehicle and it's maximum speed (from red over yellow to green).
     *
     * @param curVelocity current velocity of the vehicle
     * @param maxVelocity maximum velocity of the vehilce
     * @return color encoded as Android color integer
     */
    protected int getColor(float curVelocity, float maxVelocity) {
        float perc = curVelocity / maxVelocity;
        perc = 1 - perc;
        if (perc <= 0.5) {
            int red = ((int) (2 * perc * 0xFF)) << 16;
            int green = 0xFF << 8;
            int blue = 0;
            return 0xff000000 | red | green | blue;
        } else {
            int red = 0xFF << 16;
            int green = ((int) (0xFF - 0xFF * (perc - 0.5) * 2)) << 8;
            int blue = 0;
            return 0xff000000 | red | green | blue;
        }
    }

    /**
     * Draws the current state of the track history to the Surface.
     */
    protected void draw() {
        try {
            if (this.pixelPerVehicle == 0) {
                Rect rect = this.holder.getSurfaceFrame();
                this.setSize(rect.right, rect.bottom);
            }
            canvas = this.holder.lockCanvas();
            synchronized (holder) {
                int y = 0;
                int left = 0;
                int hCompensateStart = 0;
                int hCompensateEnd = 0;
                int vCompensateStart = 0;
                int vCompensateEnd = 0;
                canvas.drawColor(Color.BLACK);
                for (int curStepIdx = this.track.getVtrList().size()-1; curStepIdx >= 0;
                     curStepIdx--) {
                    try {
                        try {
                            track.getListSemaphore().acquire();
                        } catch (InterruptedException ex) {
                            return;
                        }
                        vCompensateStart = Math.round(y * this.tooShortPerHeight);
                        vCompensateEnd = Math.round((y+this.pixelPerLine) * this.tooShortPerHeight);
                        stepRecords = this.track.getVtrList().get(curStepIdx);
                        int i = 0;
                        for (VehicleTimeRecord r : stepRecords) {
                            left = (int) (this.pixelPerVehicle * r.getPosition());
                            hCompensateStart = Math.round(r.getPosition() * this.tooShortPerTrackLength);
                            hCompensateEnd = Math.round((r.getPosition() + this.pixelPerVehicle) * this.tooShortPerTrackLength);
                            this.paint.setColor(getColor(r.getVelocity(), r.getMaxVelocity()));
                            canvas.drawRect(left + hCompensateStart, y+vCompensateStart,
                                    left + this.pixelPerVehicle + hCompensateEnd,
                                    y + pixelPerLine + vCompensateEnd, this.paint);
                            i++;
                        }
                        track.getListSemaphore().release();
                        y += pixelPerLine;
                    } catch (ConcurrentModificationException ex) {
                        System.out.println("Concurrent Exception occured, skipping record");
                        y += pixelPerLine;
                    }
                }
            }
        } finally {
            if (canvas != null) {
                this.holder.unlockCanvasAndPost(canvas);
            }
        }
    }
}
