// ---------------------------------------------------------------------------
// 
//   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 "../hacks/toolhacks.h"

#include <iostream>
#include <sstream>
#include <vector>
#include <iomanip>
#include <ctime>
using namespace std;

#include "../image/image.h"
#include "../filesystem/filesystem.h"

struct Section {
	int start;
	int end;
};

bool rowEmpty(Image *img, int row) {

	for(int col = 0; col < img->getWidth(); col++) {
		Pixel *p = img->getPixel(col, row);
		
		if(p->rgba.a != 0) {
			return false;
		}
	}

	return true;

}

bool colEmpty(Image *img, int col, Section *row) {

	for(int rowNum = row->start; rowNum < row->end; rowNum++) {
		Pixel *p = img->getPixel(col, rowNum);
		
		if(p->rgba.a != 0) {
			return false;
		}
	}

	return true;

}

vector<Section> findRows(Image *img) {

	vector<Section> sections;

	bool rowStarted = false;
	Section currentSection;

	for(int i = 0; i < img->getHeight(); i++) {

		if(rowEmpty(img, i)) {

			if(rowStarted) {
				rowStarted = false;
				currentSection.end = i;
				sections.push_back(currentSection);
			}

		} else {

			if(!rowStarted) {
				rowStarted = true;
				currentSection.start = i;
			}
		}

	}

	if(rowStarted) {
		rowStarted = false;
		currentSection.end = img->getHeight();
		sections.push_back(currentSection);
	}

	return sections;

}

vector<Section> findCols(Image *img, Section *row) {

	vector<Section> sections;

	bool colStarted = false;
	Section currentSection;

	for(int i = 0; i < img->getWidth(); i++) {

		if(colEmpty(img, i, row)) {

			if(colStarted) {
				colStarted = false;
				currentSection.end = i;
				sections.push_back(currentSection);
			}

		} else {

			if(!colStarted) {
				colStarted = true;
				currentSection.start = i;
			}
		}

	}

	if(colStarted) {
		colStarted = false;
		currentSection.end = img->getWidth();
		sections.push_back(currentSection);
	}

	return sections;

}

bool isPink(Pixel *p) {

	if(p->rgba.r == 255 &&
	   p->rgba.g == 0 &&
	   p->rgba.b == 255 &&
	   p->rgba.a == 255) {

		return true;
	}
	
	return false;
}

bool findPinkCrosshair(Image *img, int *col, int *row, Section *colSec, Section *rowSec) {
	
	*col = -1;
	*row = -1;

	int x, y;

	for(x = colSec->start; x < colSec->end; x++) {

		int pinkInCol = 0;

		for(y = rowSec->start; y < rowSec->end; y++) {

			Pixel *p = img->getPixel(x, y);
			
			if(isPink(p)) {

				pinkInCol++;

			}
		}

		if(pinkInCol > 1) {
			*col = x;
			break;
		}
	}

	for(y = rowSec->start; y < rowSec->end; y++) {
		
		int pinkInRow = 0;

		for(x = colSec->start; x < colSec->end; x++) {

			Pixel *p = img->getPixel(x, y);
			
			if(isPink(p)) {

				pinkInRow++;

			}
		}

		if(pinkInRow > 1) {
			*row = y;
			break;
		}
	}

	return (*col != -1 && *row != -1);

}

void initArchives(void) {
    
    // Find all the .poop files in the working directory and add them.
    vector<string> wdFiles;
    vector<string> poopFiles;

    FileSystem::getNondirectories(".", wdFiles);
    FileSystem::filterFileListByType("poop", wdFiles, poopFiles);

    for(unsigned int i = 0; i < poopFiles.size(); i++) {
        FileSystem::addArchiveForSearch(poopFiles[i]);
    }

}

void getGameList(std::vector<std::string> &gameList) {

    FileSystem::getSubdirectories("games", gameList);
        
}


std::string scanFile(const std::string &fileName) {

    Image *img = NULL;
	char *fileData = NULL;
	int fileLen = 0;
    ostringstream outStr;

    fileData = FileSystem::loadFile(fileName, &fileLen);

    if(fileData) {

        img = loadTGA(fileData, fileLen);

        vector<Section> sections = findRows(img);
			
        outStr << "spriteDefs[\"" << FileSystem::getBaseName(fileName) << "\"] = {" << endl;

        int spriteCounter = 0;

        for(unsigned int i = 0; i < sections.size(); i++) {

            // Adhere to Lua's wonky 1-based indexing.
            outStr << "-- Row " << (i+1) << " (" << spriteCounter << ")" << endl;

            vector<Section> colSections = findCols(img, &sections[i]);

            outStr << "  {" << endl;

            for(unsigned int j = 0; j < colSections.size(); j++) {

                int pcx, pcy;

                spriteCounter++;

                if(findPinkCrosshair(img, &pcx, &pcy, &colSections[j], &sections[i])) {

                    // Found it. This is good.
                    
                } else {

                    // Unknown bottom-center mark.
                    pcx = pcy = -1;

                }

                bool lastCol = (j == colSections.size() - 1);

                outStr << 
                    "    { " << setw(6) << colSections[j].start <<
                    ", " << setw(6) << sections[i].start << 
                    ", " << setw(6) << colSections[j].end <<
                    ", " << setw(6) << sections[i].end <<
                    ", " << setw(6) << (pcx /*- colSections[j].start*/) <<
                    ", " << setw(6) << (pcy /*- sections[i].start*/) << " }" << 
                    (lastCol ? "" : ", ") << endl;
            }

            bool lastRow = (i == sections.size() - 1);

            outStr << "  }" << (lastRow ? "" : ",") << endl;

        }

        outStr << "};" << endl;
			
        delete img;
        delete[] fileData;

    } else {

        outStr << "-- Error: Cannot load " << fileName << endl;

    }

    return outStr.str();

}

void doGameSprites(const std::string &game) {

    vector<string> textureFiles;
    vector<string> tgaFiles;

    FileSystem::getNondirectories("games/" + game + "/textures", textureFiles);
    FileSystem::filterFileListByType("tga", textureFiles, tgaFiles);

    cout << "Processing sprites for game: " << game << endl;

    ostringstream outStr;
	outStr << "if spriteDefs == nil then spriteDefs = {}; end" << endl << endl;

    for(unsigned int i = 0; i < tgaFiles.size(); i++) {

        int nameSpace = 30;
        nameSpace -= int(tgaFiles[i].size());

        cout << "  Calculating sprite bounds for: " << tgaFiles[i] << "... ";

        clock_t startTime = clock();

        string spriteDefStr = scanFile("games/" + game + "/textures/" + tgaFiles[i]);
        outStr << spriteDefStr << endl;

        // Align the "done" column.
        for(int i = 0; i < nameSpace; i++) {
            cout << " ";
        }

        // Spit out the time it took.
        clock_t endTime = clock();
        float timeTaken = float(endTime - startTime) / float(CLOCKS_PER_SEC);
        cout << timeTaken << endl;
    }

    string outFileName = "games/" + game + "/lua/autogen_spritedefs.lua";
    string outData = outStr.str();

    cout << "  Writing to " << outFileName << endl;

    FileSystem::saveFile(outFileName, outData.c_str(), outData.size(), true);
            
    cout << endl;

}

int main(int argc, char *argv[]) {

    initArchives();

    if(argc > 1) {

        // Scan the specified files and output to stdout.

        cout << "if spriteDefs == nil then spriteDefs = {}; end" << endl << endl;

        for(int i = 1; i < argc; i++) {
            string spriteDefStr = scanFile(argv[i]);
            cout << spriteDefStr << endl;
        }

    } else {

        // No parameters given? Just scan everything and output to the
        // appropriate files.
        
        vector<string> gameList;
        getGameList(gameList);

        if(gameList.size()) {

            for(unsigned int i = 0; i < gameList.size(); i++) {
                doGameSprites(gameList[i]);
            }

        } else {

            cout <<
                "No games found to generate sprite information for. Are you running" << endl <<
                "this from the tools directory? You should run it from the main game" << endl <<
                "directory instead." << endl;
        }

    }


	return 0;
}

