//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package com.threerings.jme.tools;

import java.awt.image.BufferedImage;

import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

import com.jme.math.FastMath;
import com.jme.math.Vector3f;

/**
 * A tool for converting cube maps (sky boxes) to sphere maps.
 */
public class BuildSphereMap
{
    public static void main (String[] args)
    {
        if (args.length < 7) {
            System.err.println("Usage: BuildSphereMap front.ext back.ext " +
                "left.ext right.ext up.ext dest.ext size");
            System.exit(-1);
        }

        try {
            execute(new File(args[0]), new File(args[1]), new File(args[2]),
                new File(args[3]), new File(args[4]), new File(args[5]),
                Integer.parseInt(args[6]));
        
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * Builds the sphere map.
     *
     * @param front the file containing the front side of the cube map
     * @param back the file containing the back side of the cube map
     * @param left the file containing the left side of the cube map
     * @param right the file containing the right side of the cube map
     * @param up the file containing the up side of the cube map
     * @param target the file to contain the sphere map
     * @param size the size of the sphere map to generate
     */
    public static void execute (File front, File back, File left, File right,
        File up, File target, int size)
        throws IOException
    {
        // load up the sides of the cube map
        BufferedImage[] sides = new BufferedImage[5];
        sides[FRONT] = ImageIO.read(front);
        sides[BACK] = ImageIO.read(back);
        sides[LEFT] = ImageIO.read(left);
        sides[RIGHT] = ImageIO.read(right);
        sides[UP] = ImageIO.read(up);
        
        // compute the pixels
        int[] rgb = new int[size * size];
        Vector3f vec = new Vector3f();
        for (int y = 0, idx = 0; y < size; y++) {
            for (int x = 0; x < size; x++, idx++) {
                float vx = x / (size*0.5f) - 1f, vy = y / (size*0.5f) - 1f, d2 = vx*vx + vy*vy;
                if (d2 <= 1f) {
                    vec.set(vx, vy, FastMath.sqrt(1f - d2));
                    rgb[idx] = getCubeMapPixel(vec, sides);
                }
            }
        }
        
        // create and write the image
        BufferedImage image = new BufferedImage(size, size,
            BufferedImage.TYPE_INT_RGB);
        image.setRGB(0, 0, size, size, rgb, 0, size);
        String dest = target.toString(),
            ext = dest.substring(dest.lastIndexOf('.')+1);
        ImageIO.write(image, ext, target);
    }
    
    /**
     * Returns the pixel from the cube map to which the given vector points.
     */
    protected static int getCubeMapPixel (Vector3f vec, BufferedImage[] sides)
    {
        int side = getCubeMapSide(vec);
        
        float s, t;
        switch (side) {
            case FRONT:
                s = -vec.x / vec.y;
                t = vec.z / vec.y;
                break;
            case BACK:
                s = -vec.x / vec.y;
                t = -vec.z / vec.y;
                break;
            case LEFT:
                s = vec.y / vec.x;
                t = -vec.z / vec.x;
                break;
            case RIGHT:
                s = vec.y / vec.x;
                t = vec.z / vec.x;
                break;
            default:
            case UP:
                s = vec.x / vec.z;
                t = -vec.y / vec.z;
                break;
        }
        int width = sides[side].getWidth(), height = sides[side].getHeight();
        return sides[side].getRGB((int)((width-1) * (s+1f)/2f),
            (int)((height-1) * (1f-t)/2f));
    }
    
    /**
     * Returns the side index identifying the face of the cube map to which
     * the given vector points.
     */
    protected static int getCubeMapSide (Vector3f vec)
    {
        if (vec.x > vec.z && vec.x > vec.y && vec.x > -vec.y) {
            return RIGHT;
            
        } else if (vec.x < -vec.z && vec.x < -vec.y && vec.x < vec.y) {
            return LEFT;
        
        } else if (vec.y > vec.z) {
            return FRONT;
            
        } else if (vec.y < -vec.z) {
            return BACK;
            
        } else {
            return UP;
        }
    }
    
    /** The sides of the cube map. */
    protected static final int FRONT = 0, BACK = 1, LEFT = 2, RIGHT = 3,
        UP = 4;
}
