package me.hammerle.supersnuvi.javafx;

import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import me.hammerle.supersnuvi.rendering.Game;
import me.hammerle.text.SnuviTextPainter;

public class JavaRenderer extends Application implements IJavaRenderer
{
    // -------------------------------------------------------------------------
    // start up
    // -------------------------------------------------------------------------
    
    public static void main(String[] args) 
    {
        Application.launch(args);
    }
    
    // -------------------------------------------------------------------------
    // javafx stuff
    // -------------------------------------------------------------------------
    
    private Group root;
    private Scene scene;
    private Canvas canvas;
    private GraphicsContext context;
    
    @Override
    public void init() throws Exception 
    {
        root = new Group();
    }
    
    @Override
    public void start(Stage stage) throws Exception 
    {
        // setup
        stage.setTitle("Super Snuvi");
        scene = new Scene(root);
        stage.setScene(scene);
        canvas = new Canvas(800, 600);   
        root.getChildren().add(canvas);
        context = canvas.getGraphicsContext2D();
        textPainter = new SnuviTextPainter(canvas);
        // scene rescaling event
        scene.widthProperty().addListener((observable, oldValue, newValue) -> 
        {
            if(oldValue.intValue() != 0)
            {
                canvas.setWidth(scene.getWidth());
            }
        });
        scene.heightProperty().addListener((observable, oldValue, newValue) -> 
        {
            if(oldValue.intValue() != 0)
            {
                canvas.setHeight(scene.getHeight());
            }
        });
        // init key listener / register key events
        initKeyListener();
        // start game loop
        startLoop();
        // stage is shown after first game loop, the first frame can be seen
        stage.show();
    }

    @Override
    public void stop() throws Exception 
    {
        if(timer != null)
        {
            timer.stop();
        }
    }
    
    // -------------------------------------------------------------------------
    // game tick
    // -------------------------------------------------------------------------
    
    private AnimationTimer timer = null;
    private Game game;
    
    private void startLoop()
    {
        if(timer == null)
        {
            game = new Game(this);
            timer = new AnimationTimer() 
            {
                private long last = System.nanoTime();
                private long lag = 0;
                
                @Override
                public void handle(long now) 
                {
                    lag += now - last;
                    last = now;
                    while(lag >= 12_500_000)
                    {
                        KeyHandler.tick();
                        game.update();
                        lag -= 12_500_000;
                    }
                    game.render();
                }
            };
            timer.start();
        }
    }
    
    // -------------------------------------------------------------------------
    // key input
    // -------------------------------------------------------------------------
    
    private void initKeyListener()
    {
        // basic input handling
        scene.setOnKeyPressed((KeyEvent e) -> 
        {
            KeyHandler.pressKey(e.getCode());
        });
        scene.setOnKeyReleased((KeyEvent e) -> 
        {
            KeyHandler.releaseKey(e.getCode());
        });
    }

    // -------------------------------------------------------------------------
    // image rendering
    // -------------------------------------------------------------------------
    
    @Override
    public void drawImage(Image image, double x, double y)
    {
        context.drawImage(image.getImage(), x, y);
    }
    
    @Override
    public void drawImage(Image image, double x, double y, double w, double h)
    {
        context.drawImage(image.getImage(), x, y, w, h);
    }
    
    @Override
    public void drawImage(Image img, double sx, double sy, double sw, double sh, double dx, double dy, double dw, double dh)
    {
        context.drawImage(img.getImage(), sx, sy, sw, sh, dx, dy, dw, dh);
    }
    
    // -------------------------------------------------------------------------
    // primitive rendering
    // -------------------------------------------------------------------------
    
    @Override
    public double getWidth()
    {
        return canvas.getWidth();
    }
    
    @Override
    public double getHeight()
    {
        return canvas.getHeight();
    }
    
    @Override
    public void prepareRendering()
    {
        context.setFill(Color.BLACK);
        context.fillRect(0, 0, getWidth(), getHeight());
    }
    
    @Override
    public void save()
    {
        context.save();
    }

    @Override
    public void restore() 
    {
        context.restore();
    }

    @Override
    public void setFillColor(int r, int g, int b, double alpha) 
    {
        context.setFill(Color.rgb(r, g, b, alpha));
    }

    @Override
    public void setStrokeColor(int r, int g, int b, double alpha) 
    {
        context.setStroke(Color.rgb(r, g, b, alpha));
    }
    
    @Override
    public void fillRectangle(double x, double y, double w, double h) 
    {
        context.fillRect(x, y, w, h);
    }
    
    // -------------------------------------------------------------------------
    // text rendering
    // -------------------------------------------------------------------------
    
    public static final int CHARS_PER_LINE = 50;
    private SnuviTextPainter textPainter;

    @Override
    public void prepareTextDrawing(int r, int g, int b, double alpha, int width) 
    {
        textPainter.setScale(1);
        double scale = getWidth() / textPainter.getWidth(width);
        if(scale >= 1)
        {
            scale = (int) scale;
        }
        textPainter.prepareStringDrawing(Color.rgb(r, g, b, alpha), scale);
    }
    
    @Override
    public void stopTextDrawing() 
    {
        textPainter.stopStringDrawing();
    }

    @Override
    public void drawText(double x, double y, char[] text, int max) 
    {
        textPainter.paintString(text, x, y, max);
    }

    @Override
    public double getTextWidth(int chars) 
    {
        return textPainter.getWidth(chars);
    }

    @Override
    public double getTextHeight(int lines) 
    {
        return textPainter.getHeight(lines);
    }
}
