package com.brackeen.javagamebook.test;

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

import com.brackeen.javagamebook.graphics.ScreenManager;

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

    protected static final int DEFAULT_FONT_SIZE = 24;

    // lista różnych trybów, uporządkowana według preferencji
    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 czas kończyć pracę.
    */
    public void stop() {
        isRunning = false;
    }


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


    /**
        Kończy działanie maszyny wirtualnej z wątku demona. Wątek-demon
        czeka 2 sekundy a następnie wywołuje System.exit(0). Ponieważ maszyna
        wirtualna powinna kończyc się gdy działają tylko wątki-demony,
        metoda zapewnia, że System.exit(0) jest wywoływane tylko
        w ostatecznosci. Jest to niezbędne gy działa Java Sound.
    */
    public void lazilyExit() {
        Thread thread = new Thread() {
            public void run() {
                // na początku czekamy na samodzielne zakończenie działania VM
                try {
                    Thread.sleep(2000);
                }
                catch (InterruptedException ex) { }
                // system nadal działa, więc wymuszamy zakończenie
                System.exit(0);
            }
        };
        thread.setDaemon(true);
        thread.start();
    }

    /**
        Ustawia tryb pełnoekranowy i inicjuje obiekty.
    */
    public void init() {
        init(MID_RES_MODES);
    }


    /**
        Ustawia tryb pełnoekranowy 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();
    }


    /**
        Uruchamia pętlę gry działającą aż do wywołania metody stop().
    */
    public void gameLoop() {
        long startTime = System.currentTimeMillis();
        long currTime = startTime;

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

            // aktualizacja
            update(elapsedTime);

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

            // nie usypiamy zadania, praca z pełną prędkością!
            /*try {
                Thread.sleep(20);
            }
            catch (InterruptedException ex) { }*/
        }
    }


    /**
        Aktualizuje stan gry/animacji w oparciu o okres czas jaki minął
        od uruchomienia programu.
    */
    public void update(long elapsedTime) {
        // do nothing
    }


    /**
        Rysowanie na ekranie. Klasy pochodne muszą przedefiniować tą metodę.
    */
    public abstract void draw(Graphics2D g);
}
