package com.brackeen.javagamebook.test;

import java.awt.*;
import javax.swing.ImageIcon;

import com.brackeen.javagamebook.graphics.ScreenManager;

/**
    Prosta klasa abstrakcyjna  wykorzystywana do testowania kodu. Podklasy
    powinny implementować metodę draw().
*/
public abstract class GameCore {

    protected static final int DEFAULT_FONT_SIZE = 24;

    // listy różnych trybów w preferowanej kolejności
    protected static final DisplayMode[] MID_RES_MODES = {
        new DisplayMode(800, 600, 16, 0),
        new DisplayMode(800, 600, 32, 0),
        new DisplayMode(800, 600, 24, 0),
        new DisplayMode(640, 480, 16, 0),
        new DisplayMode(640, 480, 32, 0),
        new DisplayMode(640, 480, 24, 0),
        new DisplayMode(1024, 768, 16, 0),
        new DisplayMode(1024, 768, 32, 0),
        new DisplayMode(1024, 768, 24, 0),
    };

    protected static final DisplayMode[] LOW_RES_MODES = {
        new DisplayMode(640, 480, 16, 0),
        new DisplayMode(640, 480, 32, 0),
        new DisplayMode(640, 480, 24, 0),
        new DisplayMode(800, 600, 16, 0),
        new DisplayMode(800, 600, 32, 0),
        new DisplayMode(800, 600, 24, 0),
        new DisplayMode(1024, 768, 16, 0),
        new DisplayMode(1024, 768, 32, 0),
        new DisplayMode(1024, 768, 24, 0),
    };

    protected static final DisplayMode[] VERY_LOW_RES_MODES = {
        new DisplayMode(320, 240, 16, 0),
        new DisplayMode(400, 300, 16, 0),
        new DisplayMode(512, 384, 16, 0),
        new DisplayMode(640, 480, 16, 0),
        new DisplayMode(800, 600, 16, 0),
    };

    private boolean isRunning;
    protected ScreenManager screen;
    protected int fontSize = DEFAULT_FONT_SIZE;

    /**
        Sygnalizuje pętli gry, że pora kończyć 
    */
    public void stop() {
        isRunning = false;
    }


    /**
        Przywołuje init() oraz gameLoop()
    */
    public void run() {
        try {
            init();
            gameLoop();
        }
        finally {
            if (screen != null) {
                screen.restoreScreen();
            }
            lazilyExit();
        }
    }


    /**
        Poleca maszynie wirtualnej wyjść z wątku demona. Wątek demona
        odczekuje 2 sekundy i przywołuje System.exit(0). Ponieważ maszyna
        wirtualna powinna kończyć pracę tylko, gdy wątek demona jeszcze działa, 
        upewniamy się w ten sposób, że funkcja System.exit(0) przywoływana jest
        tylko gdy trzeba, czyli gdy działa system dźwięku Java Sound.
    */
    public void lazilyExit() {
        Thread thread = new Thread() {
            public void run() {
                // najpierw poczekaj, aż maszyna wirtualna sama zakończy.
                try {
                    Thread.sleep(2000);
                }
                catch (InterruptedException ex) { }
                // system nadal działa, więc wymuś zamknięcie
                System.exit(0);
            }
        };
        thread.setDaemon(true);
        thread.start();
    }

    /**
        Włącza tryb pełnego ekranu i inicjuje obiekty. 
    */
    public void init() {
        init(MID_RES_MODES);
    }


    /**
        Włącza tryb pełnego ekranu i inicjuje obiekty. 
    */
    public void init(DisplayMode[] possibleModes) {
        screen = new ScreenManager();
        DisplayMode displayMode =
            screen.findFirstCompatibleMode(possibleModes);
        screen.setFullScreen(displayMode);

        Window window = screen.getFullScreenWindow();
        window.setFont(new Font("Dialog", Font.PLAIN, fontSize));
        window.setBackground(Color.blue);
        window.setForeground(Color.white);

        isRunning = true;
    }


    public Image loadImage(String fileName) {
        return new ImageIcon(fileName).getImage();
    }


    /**
        Wykonuje pętlę gry, dopóki nie przywołamy stop().
    */
    public void gameLoop() {
        long startTime = System.currentTimeMillis();
        long currTime = startTime;

        while (isRunning) {
            long elapsedTime =
                System.currentTimeMillis() - currTime;
            currTime += elapsedTime;

            // aktualizuj
            update(elapsedTime);

            // draw the screen
            Graphics2D g = screen.getGraphics();
            draw(g);
            g.dispose();
            screen.update();

            // nie czekaj! pracuj tak szybko, jak to możliwe
            /*try {
                Thread.sleep(20);
            }
            catch (InterruptedException ex) { }*/
        }
    }


    /**
        Aktualisuje stan gry/animacji bazując na
        czasie, który do tej pory upłynął.
    */
    public void update(long elapsedTime) {
        // nic nie rób
    }


    /**
        Rysuj na ekranie. Podklasy muszą przesłaniać tę
        metodę.
    */
    public abstract void draw(Graphics2D g);
}
