// ---------------------------------------------------------------------------
// 
//   Lily Engine alpha
// 
//   Copyright (c) 2010 Clifford Jolly
//     http://cliffjolly.com
//     cliff@cliffjolly.com
// 
// ---------------------------------------------------------------------------
// 
//   This program is free software; you can redistribute it and/or modify
//   it under the terms of the GNU General Public License as published by
//   the Free Software Foundation; either version 2 of the License, or
//   (at your option) any later version.
//  
//   This program 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
//   General Public License for more details.
// 
//   You should have received a copy of the GNU General Public License
//   along with this program; if not, write to the Free Software
//   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
//   USA
// -------------------------- END HEADER -------------------------------------

#include <iostream>
#include <cmath>
using namespace std;

#include <assert.h>

#include "atlastexture.h"
#include "../image/image.h"
#include "../filesystem/filesystem.h"
#include "../console/console.h"
using namespace Console;

AtlasTexture::AtlasTexture(void) {
}

AtlasTexture::~AtlasTexture(void) {
}

void AtlasTexture::startMultipleLoad(const std::vector<std::string> &names) {

    loadNames = names;

    // The original startLoad() does everything else we need to do,
    // and our overridden backgroundLoad() will just ignore the name
    // anyway.
    Texture::startLoad(names[0]);

}

static Image *loadImage(const std::string &fileName) {

	int length;
	
    Image *image = NULL;

	// TODO: Use a loader appropriate for a given file type here
	//   instead of just TGAs for everything.
	char *tgaData = FileSystem::loadFile(fileName, &length);
	if(tgaData) {
		image = loadTGA(tgaData, length, true);
		delete[] tgaData;
	} else {
		out("error") << "Failed to load texture \"" << fileName << "\"" << endl;
	}

    return image;
}

static bool isPow2(int x) {

    // Keep shifting x to the right until there's a 1 in the least
    // significant bit. Then check to see if it's greater than one.

    while(x > 0) {

        if((x & 1)) {
            return x == 1;
        } else {
            x = x >> 1;
        }
    }

    return false;
}

void AtlasTexture::backgroundLoad(void) {

	timeSinceAccess = 0;
	
	assert(!image);
	
    vector<Image *> subImages;

    int xmax = 0;
    int ymax = 0;

    // Load up every individual image.
    for(unsigned int i = 0; i < loadNames.size(); i++) {

        Image *subImage = NULL;

        if(loadNames[i].size()) {
            subImage = loadImage(loadNames[i]);
        }

        if(subImage) {
            if(subImage->getWidth() > xmax) {
                xmax = subImage->getWidth();
            }
            if(subImage->getHeight() > ymax) {
                ymax = subImage->getHeight();
            }
        }

        subImages.push_back(subImage);
    }

    // FIXME: Don't rely on sqrt and math weirdness here. OR hardcode
    // it.
    int rows = 2; //int(sqrt(float(loadNames.size())) + 0.99999999999999999999);

    int cols = rows;

    // This is the only case where we can still be 2^n size and shave
    // off any entire columns.
    if(subImages.size() == 2) {
        cols = 1;
    }

    int w = xmax * cols;
    int h = ymax * rows;

    image = new Image(w, h);
	
    if(!isPow2(w) || !isPow2(h)) {
        out("warning") << "Texture atlas isn't going to end up with 2^n size!" << endl;
    }

    origWidth = w;
    origHeight = h;

    int curRow = 0;
    int curCol = 0;

    // Space filler pixel for images that aren't as big as the
    // others in the set.
    Pixel p;
    p.rgba.r = 255;
    p.rgba.g = 0;
    p.rgba.b = 255;
    p.rgba.a = 255;

    // Go through all the images and draw them to the bigger one.
    for(unsigned int i = 0; i < subImages.size(); i++) {

        if(subImages[i]) {

            int xoffset = curCol * xmax;
            int yoffset = curRow * ymax;

            for(int x = 0; x < xmax; x++) {

                for(int y = 0; y < ymax; y++) {

                    Pixel *px = subImages[i]->getPixel(x, y);

                    if(px) {
                        image->setPixel(*px, xoffset + x, yoffset + y);
                    } else {
                        image->setPixel(p, xoffset + x, yoffset + y);
                    }
                }
            }

            delete subImages[i];

        }

        curRow++;
        if(curRow >= rows) {
            curRow = 0;
            curCol++;
        }
    }

    loaded = true;

    backgroundLoadFinished = true;

}

