/* Reorganize (c) 2006 Bjørn Lindeijer
 * License: GPL, v2 or later
 */

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.util.Vector;
import java.io.IOException;
import java.io.File;
import javax.imageio.ImageIO;

/**
 * Tool to reorganize the headgears.
 */
public class Reorganize
{
    private static final int SPRITE_WIDTH = 60;
    private static final int SPRITE_HEIGHT = 60;
    private static final int FRAMES = 10;
    private static final int DIRECTIONS = 4;

    private static final int HAIR_COLORS = 10;
    private static final int HAIR_FRAMES = 9;
    private static final int HAIR_SPRITE_WIDTH = 40;
    private static final int HAIR_SPRITE_HEIGHT = 40;

    private static final int TRANSPARENT = new Color(255, 0, 255).getRGB();

    public static void main(String[] arg)
    {
        if (arg.length != 2) {
            System.out.println("Usage:\n   java Reorganize [source] [target]");
            return;
        }

        BufferedImage source = null;
        try {
            source = ImageIO.read(new File(arg[0]));
        } catch (IOException e) {
            System.out.println("Error while trying to read " + arg[0] + ".");
            e.printStackTrace();
            System.exit(1);
        }

        // Read the existing frames into a vector
        Vector<BufferedImage> spriteSet = gridCut(source,
                HAIR_SPRITE_WIDTH, HAIR_SPRITE_HEIGHT,
                HAIR_FRAMES, 1);

        // Determine minimal rectangle that can still contain the contents of
        // any frame
        /*
        Rectangle cropRect = minimumCropRect(spriteSet);

        if (cropRect == null) {
            System.out.println(
                    "Error: no optimal crop rect could be determined.");
            System.exit(1);
        }

        System.out.println(arg[0] + ": width=\"" +
                cropRect.width + "\" height=\"" + cropRect.height + "\"");
        */

        filterHeadgear(spriteSet);

        BufferedImage target = gridDraw(
                spriteSet,
                new Rectangle(0, 0, HAIR_SPRITE_WIDTH, HAIR_SPRITE_HEIGHT),
                HAIR_FRAMES - 4, 1);

        // Save the target image
        try {
            ImageIO.write(target, "png", new File(arg[1]));
        } catch (IOException e) {
            System.out.println("Error while trying to write " + arg[1] + ".");
            e.printStackTrace();
            System.exit(1);
        }
    }

    private static Vector<BufferedImage> gridCut(
            BufferedImage source,
            int width, int height, int xFrames, int yFrames)
    {
        Vector<BufferedImage> spriteSet = new Vector<BufferedImage>();

        for (int y = 0; y < yFrames; y++) {
            for (int x = 0; x < xFrames; x++) {
                BufferedImage sprite = source.getSubimage(
                        x * width,
                        y * height,
                        width,
                        height);

                spriteSet.add(sprite);
            }
        }

        return spriteSet;
    }

    private static BufferedImage gridDraw(Vector<BufferedImage> spriteSet,
            Rectangle cropRect, int xFrames, int yFrames)
    {
        // Create a new image
        BufferedImage target = new BufferedImage(
                xFrames * cropRect.width,
                yFrames * cropRect.height,
                BufferedImage.TYPE_INT_ARGB);

        // Draw the frames onto the target image
        Graphics g = target.getGraphics();
        for (int y = 0; y < yFrames; y++) {
            for (int x = 0; x < xFrames; x++) {
                g.drawImage(
                        spriteSet.get(x + xFrames * y).getSubimage(
                            cropRect.x,
                            cropRect.y,
                            cropRect.width,
                            cropRect.height),
                        x * cropRect.width,
                        y * cropRect.height,
                        null);
            }
        }

        return target;
    }

    private static Rectangle minimumCropRect(Vector<BufferedImage> spriteSet)
    {
        Rectangle cropRect = null;

        for (BufferedImage sprite : spriteSet) {
            Rectangle frameCropRect = determineCropRect(sprite);

            if (cropRect == null) {
                cropRect = frameCropRect;
            } else {
                cropRect.add(frameCropRect);
            }
        }

        // Make crop rect one pixel larger (since we want an inclusive rect)
        if (cropRect != null) {
            cropRect.add(
                    cropRect.x + cropRect.width + 1,
                    cropRect.y + cropRect.height + 1);
        }

        return cropRect;
    }

    private static Rectangle determineCropRect(BufferedImage image)
    {
        // Loop through all the pixels, ignoring transparent ones.
        Rectangle rect = null;

        for (int y = 0; y < image.getHeight(); y++) {
            for (int x = 0; x < image.getWidth(); x++) {
                int color = image.getRGB(x, y);

                if (color != TRANSPARENT && (color & 0xFF000000) != 0) {
                    if (rect == null) {
                        rect = new Rectangle(x, y, 0, 0);
                    } else {
                        rect.add(x, y);
                    }
                }
            }
        }

        return rect;
    }

    private static void filterHairstyle(Vector<BufferedImage> spriteSet)
    {
        // Remove frame 1, 2, 6 and 7 from each color
        for (int i = HAIR_COLORS - 1; i >= 0; i--) {
            spriteSet.remove(i * HAIR_FRAMES + 7);
            spriteSet.remove(i * HAIR_FRAMES + 6);
            spriteSet.remove(i * HAIR_FRAMES + 2);
            spriteSet.remove(i * HAIR_FRAMES + 1);
        }
    }

    private static void filterHeadgear(Vector<BufferedImage> spriteSet)
    {
        // Remove frame 1, 2, 6 and 7
        spriteSet.remove(7);
        spriteSet.remove(6);
        spriteSet.remove(2);
        spriteSet.remove(1);
    }
}
