package me.hammerle.supersnuvi.rendering;

import java.io.File;
import me.hammerle.supersnuvi.gamelogic.StateRenderer;
import javafx.animation.AnimationTimer;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.Image;
import javafx.scene.input.KeyEvent;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import me.hammerle.supersnuvi.input.KeyHandler;
import me.hammerle.text.SnuviTextPainter;

public class GameRenderer extends AnimationTimer implements IGameRenderer
{
    // constants
    public final static int TILE_SIZE = 32;
    
    // basic graphic stuff
    private final Scene scene;
    private final Canvas canvas;
    
    // input handler
    private final KeyHandler keys;
    
    // timing
    private long lastHandle;
    private long timingSum;
    
    // rendering
    private double cameraX;
    private double cameraY;
    private double offsetX;
    private double offsetY;
    private GraphicsContext context;
    
    // text rendering
    private final SnuviTextPainter textPainter;
    
    // world rendering
    private final StateRenderer state;
    
    public GameRenderer(Scene scene, Canvas canvas)
    {
        // basic graphic stuff
        this.scene = scene;
        this.canvas = canvas;
        // input handler
        this.keys = new KeyHandler();
        //timing
        this.lastHandle = 0;
        this.timingSum = 0;
        // rendering
        this.cameraX = 0;
        this.cameraY = 0;
        this.offsetX = 0;
        this.offsetY = 0;
        this.context = null;
        this.textPainter = new SnuviTextPainter(canvas);
        
        // events
        registerEvents();
        
        this.state = new StateRenderer(this);
    }
    
    private void registerEvents()
    {
        // basic input handling
        scene.setOnKeyPressed((KeyEvent e) -> 
        {
            keys.pressKey(e.getCode());
        });
        scene.setOnKeyReleased((KeyEvent e) -> 
        {
            keys.releaseKey(e.getCode());
        });
        // 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());
            }
        });
    }
    
    //public boolean once = true;
    //public MediaPlayer mediaPlayer;
    
    @Override
    public void handle(long now) 
    {
        timingSum += now - lastHandle;
        lastHandle = now;
        
        if(timingSum >= 15900000)
        {
            timingSum = 0;
            keys.tick();

            prepareRendering();
            state.tick(keys);
        }
        
        /*if(once)
        {
            once = false;
            Media sound = new Media(new File("Fabian.wav").toURI().toString());
            mediaPlayer = new MediaPlayer(sound);
        }
        mediaPlayer.play();*/
    }
    
    private void prepareRendering()
    {
        offsetX = -cameraX;
        offsetY = canvas.getHeight() + cameraY;
        context = canvas.getGraphicsContext2D();
        context.setFill(Color.BLACK);
        context.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
    }
    
    @Override
    public void prepareStringDrawing(Color c)
    {
        textPainter.prepareStringDrawing(c, 
                Math.min
                (
                    canvas.getHeight() / (textPainter.getBaseHeight() * LINES),
                    canvas.getWidth() / (textPainter.getBaseWidth() * CHARS_PER_LINE))
                );
    }
    
    @Override
    public void stopStringDrawing()
    {
        textPainter.stopStringDrawing();
    }
    
    @Override
    public double getStringOffsetX(int x)
    {
        double width = canvas.getWidth();
        width -= textPainter.getWidth(CHARS_PER_LINE);
        width /= 2;
        return width + textPainter.getWidth(x);
    }
    
    @Override
    public double getStringOffsetY(int y)
    {
        double height = canvas.getHeight();
        height -= textPainter.getHeight() * LINES;
        height /= 2;
        return height + textPainter.getHeight() * y;
    }

    @Override
    public double getLineHeight()
    {
        return textPainter.getHeight();
    }
    
    @Override
    public double getStringWidth(int length)
    {
        return textPainter.getWidth(length);
    }
    
    @Override
    public double getStringWidth(String s)
    {
        return textPainter.getWidth(s);
    }
    
    @Override
    public double getCharsWidth(char[] chars)
    {
        return textPainter.getWidth(chars);
    }
    
    @Override
    public void drawString(String s, double x, double y)
    {
        textPainter.paintString(s, x, y);
    }
    
    @Override
    public void drawChars(char[] chars, double x, double y)
    { 
        textPainter.paintString(chars, x, y);
    }
    
    @Override
    public void drawImage(Image image, double x, double y)
    {
        context.drawImage(image, offsetX + x, offsetY - y);
    }
    
    @Override
    public void drawFixedImage(Image image, double x, double y)
    {
        context.drawImage(image, x, y);
    }
    
    @Override
    public void drawFlippedImage(Image image, double x, double y)
    {
        context.drawImage(image, 0, 0, image.getWidth(), image.getHeight(), 
                offsetX + x + image.getWidth(), offsetY - y, -image.getWidth(), image.getHeight());
    }
    
    @Override
    public void drawBlockImage(Image image, int x, int y)
    {
        context.drawImage(image, offsetX + x * TILE_SIZE, offsetY - (y + 1) * TILE_SIZE);
    }
    
    @Override
    public void setStrokeColor(Color c)
    {
        context.setStroke(c);
    }
    
    @Override
    public void setFillColor(Color c)
    {
        context.setFill(c);
    }
    
    @Override
    public void fillRec(double x, double y, double width, double height, Color c)
    {
        Paint paint = context.getFill();
        context.setFill(c);
        context.fillRect(x, y, width, height);
        context.setFill(paint);
    }
    
    @Override
    public int getFirstVisibleBlockX()
    {
        return Math.max(0, (int) (cameraX / TILE_SIZE));
    }
    
    @Override
    public int getFirstVisibleBlockY()
    {
        return Math.max(0, (int) (cameraY / TILE_SIZE));
    }
    
    @Override
    public int getLastVisibleBlockX()
    {
        return getFirstVisibleBlockX() + (int) (canvas.getWidth() / TILE_SIZE);
    }
    
    @Override
    public int getLastVisibleBlockY()
    {
        return getFirstVisibleBlockY() + (int) (canvas.getHeight()/ TILE_SIZE);
    }
    
    @Override
    public int toBlock(double c)
    {
        return (int) (c / TILE_SIZE);
    }

    @Override
    public double toCoord(int b)
    {
        return b * TILE_SIZE;
    }

    @Override
    public double getWidth() 
    {
        return canvas.getWidth();
    }

    @Override
    public double getHeight() 
    {
        return canvas.getHeight();
    }

    @Override
    public void setViewCenter(double x, double y) 
    {
        cameraX = x;
        cameraY = y;
    }
}
