package com.brackeen.javagamebook.graphics;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import javax.swing.ImageIcon;

/**
    Klasa ImageFont umożliwia wczytywanie i rysowanie tekstu
    za pomocą obrazów reprezentujących znaki.

    Wczytuje wszystkie pliki PNG znajdujące się w danym katalogu.
    Wczytywane pliki mają nazwę w postaci "charXX.png", gdzie "XX"
    jest dziesiętnym kodem unicode znaku.

    Znaki mogą się różnić wysokościami i szerokościami.
*/
public class ImageFont {

    public static final int HCENTER = 1;
    public static final int VCENTER = 2;
    public static final int LEFT = 4;
    public static final int RIGHT = 8;
    public static final int TOP = 16;
    public static final int BOTTOM = 32;

    private char firstChar;
    private Image[] characters;
    private Image invalidCharacter;


    /**
        Tworzy nowy obiekt klasy ImageFont bez znaków.
    */
    public ImageFont() {
        this(null);
        firstChar = 0;
        characters = new Image[0];
    }


    /**
        Tworzy nowy obiekt klasy ImageFont i wczytuje obrazy
        reprezentujące znaki z plików umieszczonych w danym katalogu.
    */
    public ImageFont(String path) {
        if (path != null) {
            load(path);
        }

        // powoduje, że dany znak będzie wykorzystywany dla niepoprawnych znaków
        invalidCharacter = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
        Graphics g = invalidCharacter.getGraphics();
        g.setColor(Color.RED);
        g.fillRect(0,0,10,10);
        g.dispose();
    }


    /**
        Wczytuje umieszczone we wskazanym katalogu pliki z obrazami
        dla poszczególnych znaków. Przykładowo, jeśli tym katalogiem
        jest "../fonts/large", metoda przeszukuje go w celu otwarcia
        wszystkich plików o nazwie "charXX.png", gdzie "XX" jest
        dziesiętnym kodem znaku unicode. Katalog nie musi zawierać
        plików z obrazami dla pełnego zbioru znaków; możemy np.
        stworzyć obrazy tylko dla wielkich liter lub cyfr.
    */
    public void load(String path) throws NumberFormatException {
        // w tym katalogu:
        // wczytaj każdy plik .png, którego nazwa rozpoczyna się od 'char'
        File dir = new File(path);
        File[] files = dir.listFiles();

        // znajdź znaki o minimalnym i maksymalnym kodzie
        char minChar = Character.MAX_VALUE;
        char maxChar = Character.MIN_VALUE;
        for (int i=0; i<files.length; i++) {
            int unicodeValue = getUnicodeValue(files[i]);
            if (unicodeValue != -1) {
                minChar = (char)Math.min(minChar, unicodeValue);
                maxChar = (char)Math.max(maxChar, unicodeValue);
            }
        }

        // wczytaj obraz
        if (minChar < maxChar) {
            firstChar = minChar;
            characters = new Image[maxChar - minChar + 1];
            for (int i=0; i<files.length; i++) {
                int unicodeValue = getUnicodeValue(files[i]);
                if (unicodeValue != -1) {
                    int index = unicodeValue - firstChar;
                    characters[index] = new ImageIcon(files[i].getAbsolutePath()).getImage();
                }
            }

        }

    }


    private int getUnicodeValue(File file) throws NumberFormatException
    {
        String name = file.getName().toLowerCase();
        if (name.startsWith("char") && name.endsWith(".png")) {
            String unicodeString = name.substring(4, name.length() - 4);
            return Integer.parseInt(unicodeString);
        }
        return -1;
    }


    /**
        Zwraca obraz dla określonego znaku. Jeśli dla danego znaku
        obraz nie istnieje, zwracany jest specjalny obraz dla
        "nieprawidłowego" znaku.
    */
    public Image getImage(char ch) {
        int index = ch - firstChar;
        if (index < 0 || index >= characters.length ||
            characters[index] == null)
        {
            return invalidCharacter;
        }
        else {
            return characters[index];
        }
    }


    /**
        Zwraca (wyrażoną w pikselach) szerokość przekazanego łańcucha.
    */
    public int stringWidth(String s) {
        int width = 0;
        for (int i=0; i<s.length(); i++) {
            width += charWidth(s.charAt(i));
        }
        return width;
    }


    /**
        Zwraca (wyrażoną w pikselach) szerokość przekazanego znaku.
    */
    public int charWidth(char ch) {
        return getImage(ch).getWidth(null);
    }


    /**
        Zwraca (wyrażoną w pikselach) wysokość przekazanego znaku.
    */
    public int charHeight(char ch) {
        return getImage(ch).getHeight(null);
    }


    /**
        Rysuje przekazany łańcuch w punkcie (x, y).
    */
    public void drawString(Graphics g, String s, int x, int y) {
        drawString(g, s, x, y, LEFT | BOTTOM);
    }


    /**
        Rysuje przekazany łańcuch w punkcie (x, y).
    */
    public void drawString(Graphics g, String s, int x, int y, int anchor)
    {
        if ((anchor & HCENTER) != 0) {
            x-=stringWidth(s) / 2;
        }
        else if ((anchor & RIGHT) != 0) {
            x-=stringWidth(s);
        }
        // czyści poziome flagi dla rysowanych znaków
        anchor &= ~HCENTER;
        anchor &= ~RIGHT;

        // rysuje znaki
        for (int i=0; i<s.length(); i++) {
            drawChar(g, s.charAt(i), x, y, anchor);
            x+=charWidth(s.charAt(i));
        }
    }


    /**
        Rysuje przekazany znak w punkcie (x, y).
    */
    public void drawChar(Graphics g, char ch, int x, int y) {
        drawChar(g, ch, x, y, LEFT | BOTTOM);
    }


    /**
        Rysuje przekazany znak w punkcie (x, y).
    */
    public void drawChar(Graphics g, char ch, int x, int y, int anchor)
    {
        if ((anchor & HCENTER) != 0) {
            x-=charWidth(ch) / 2;
        }
        else if ((anchor & RIGHT) != 0) {
            x-=charWidth(ch);
        }

        if ((anchor & VCENTER) != 0) {
            y-=charHeight(ch) / 2;
        }
        else if ((anchor & BOTTOM) != 0) {
            y-=charHeight(ch);
        }
        g.drawImage(getImage(ch), x, y, null);
    }
}
