/*
 * texparse.cpp by Jeromy Walsh <jwalsh@fidnet.com>
 *
 * Copyright (C) 2002 PlaneShift Team (info@planeshift.it,
 * http://www.planeshift.it)
 *
 * 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 (version 2 of the License)
 * 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.
 *
 */

// Crystal Space Include Files
#include <cssysdef.h>

#include <igraphic/imageio.h>
#include <imap/loader.h>
#include <iutil/vfs.h>
#include <ivaria/reporter.h>
#include <csgfx/xorpat.h>
#include <csgfx/rgbpixel.h>
#include <cstool/initapp.h>
#include <csutil/cmdline.h>
#include <csutil/databuf.h>

// Planeshift Include Files
#include <util/texfactory.h>
#include <util/psxmlparser.h>
#include "texparse.h"

// System Include Files
#include <ctype.h>

CS_IMPLEMENT_APPLICATION


// The global pointer to simple
psTxPrtParse *parser;

psTxPrtParse::psTxPrtParse()
{
    object_reg             = NULL;

    colormask   = NULL;
    region      = NULL;
    outname     = NULL;
    filename    = NULL;
    race        = NULL;
    scanlines   = NULL;

    cmdOptions = 0;
}

psTxPrtParse::~psTxPrtParse()
{
    if (colormask)     { delete [] colormask;    colormask     = NULL;}
    if (region)         { delete [] region;     region         = NULL;}
    if (outname)     { delete [] outname;     outname     = NULL;}
    if (filename)     { delete [] filename;     filename     = NULL;}
    if (race)         { delete [] race;     race         = NULL;}
    if (scanlines)     { delete [] scanlines;     scanlines     = NULL;}
}

int psTxPrtParse::VerifyCmdLine()
{
    // Gain access to the command line
    iCommandLineParser* cmdline =
    dynamic_cast<iCommandLineParser*>(object_reg->Get("iCommandLineParser"));
    
    // Check if they asked for help
    if (cmdline->GetOption("help"))
    return 5;
    
    // Check if a filename was provided
    if (!cmdline->GetName())
    return 1;
    
    // Get the translated path
    csRef<iDataBuffer> xname = VFS->ExpandPath( cmdline->GetName() );
    
    // Add the VFS "/this" extension to the front
    filename = new char[strlen("/this/") +
    strlen((const char*)xname->GetData() ) ];
    
    strcpy(filename, "/this");
    strcat(filename, (const char*)xname->GetData() );
    
    // Check if the file exists as the VFS sees it
    if ( !VFS->Exists(filename) )
    return 2;
    
    fprintf(stdout, "Reading from VFS file: %s\n", filename);
    
    // Create output name, this will end in xml...hopefully
    outname = new char[strlen(filename) + 1] ;
    strcpy(outname, filename);
    outname[strlen(filename)-3] =  outname[strlen(filename)];
    strcat(outname, "xml");
    
    fprintf(stdout, "Outputting XML to VFS file: %s\n", outname);
    
    // Check for a race flag
    if ( !cmdline->GetOption("race") )
    return 3;

    // Check if there is just a race flag with no value assigned
    else if ( !strcmp(cmdline->GetOption("race"), "") )
    return 4;
    
    // assign the value of the race option to application class
    race = new char[strlen(cmdline->GetOption("race") ) +1];
    strcpy(race, cmdline->GetOption("race"));
    
    // Begin checking for options.
    colormask    = new csRGBcolor[cmdOptions];
    region     = new psImageRegion[cmdOptions];
    scanlines     = new int[cmdOptions];
    
    int marker = 0;
    
    char bodyparts[][10] = {"nose", "eyes", "chin", "hair"};
    
    // Iterate through body part array, and check for matching parts and wellformed colorrefs
    for (int options = 0; options < 4; options++)
    {
    if (cmdline->GetOption(bodyparts[options]) )
    {
        if (strlen(cmdline->GetOption( bodyparts[options] )) == 6)
        {
        colormask[marker] = TransColor(cmdline->GetOption( bodyparts[options] ) );
        region[marker].race = race;
        region[marker].part = bodyparts[options];
        marker++;
        }
        else
        return 6;
    }
    }
    
    // If the marker does not match the cmdOptions, then there are either too many or
    // too few options
    if (marker != cmdOptions)
    return 6;
    
    return 0;
}

csRGBcolor psTxPrtParse::TransColor(char const *arg)
{
    int color[5] = {0};
    
    // iterate through the 3 bytes, seperating and summing
    // into 3 colors.
    for (int i = 0; i < 5; i+=2)
    {
    // check if the first character is a letter or number
    if (isalpha(arg[i]) )
        color[i] = 16 * (int)(tolower(arg[i] - 87) );
    else
        color[i] = 16 * (arg[i] - 48);
    
    // check if the second character is a letter or a number
    if (isalpha(arg[i+1]) )
        color[i] += (int)(tolower(arg[i+1] - 87) );
    else
        color[i] += (arg[i+1] - 48);
    }
    
    // Create a csRGBcolor object from the values obtained.
    csRGBcolor rgbcolor(color[0], color[2], color[4]);
    
    return rgbcolor;
}

bool psTxPrtParse::Initialize(int argc, const char* const argv[])
{
    // Get the number of command line arguments, then subtract 3 due to
    // appname, filename, and race flag...the rest should be regions to parse
    cmdOptions = argc - 3;
    
    // Initialize the environment and register an object registry
    object_reg = csInitializer::CreateEnvironment(argc, argv);
    if (!object_reg) return false;
    
    // Request important plugins
    if (!csInitializer::RequestPlugins(object_reg,
        CS_REQUEST_VFS,
        CS_REQUEST_IMAGELOADER,
        CS_REQUEST_END))
    {
    csReport(object_reg, CS_REPORTER_SEVERITY_ERROR,
        "crystalspace.application.simple",
        "Can't initialize plugins!");
    return false;
    }
    
    // Obtain an interface to the Virtual File System
    VFS = CS_QUERY_REGISTRY(object_reg, iVFS);
    if (!VFS)
    {
    csReport(object_reg, CS_REPORTER_SEVERITY_ERROR,
        "crystalspace.application.texpartparse",
        "No iVFS plugin!");
    return false;
    }
    
    // Obtain an interface to the iImageIO image loading class
    ImageLoader = CS_QUERY_REGISTRY(object_reg, iImageIO);
    if (!ImageLoader)
    {
    csReport(object_reg, CS_REPORTER_SEVERITY_ERROR,
        "crystalspace.application.texpartparse",
        "No iImageIO plugin!");
    return false;
    }
    
    // Output information to the console
    csReport(object_reg, CS_REPORTER_SEVERITY_NOTIFY,
            "crystalspace.application.texpartparse",
        "\n<>==Texture Part Parser Version 0.1==<>");
    
    // Now parse/verify the cmd line to make sure everything is available
    int value = 0;
    
    if ( (value = VerifyCmdLine()) )
    {
    switch(value)
    {
        case 1:
        fprintf(stderr, "texpartparse: Error - No file name provided!\n");
        break;
        
            case 2:
        fprintf(stderr, "texpartparse: Error - File is either corrupt or does not exist!\n");
        break;
        
            case 3:
        fprintf(stderr, "texpartparse: Error - No race provided!\n");
        break;
        
            case 4:
        fprintf(stderr, "texpartparse: Error - Please specify which race this is!\n");
        break;
        
            case 5:
        break;
        
            case 6:
        fprintf(stderr, "texpartparse: Error - Poorly formed colorref(s)!\n");
        break;
    }
    
    PrintUsage();
    
    return false;
    }
    else
    fprintf(stdout, "Regions to parse: %d\n", cmdOptions);
    
    return true;
}

void psTxPrtParse::WriteFile()
{
    csString output;
    char tempString[80];
    psScanline *myline;
    
    // Print oppening Race element
    sprintf(tempString, "    <Race name=\"%s\">\n", race);
    output.Append(tempString);
    
    // For each region declared, iterate through and print the scanlines
    for (int i=0; i < cmdOptions; i++)
    {
    // Print oppening region element
    sprintf(tempString, "        <Region name=\"%s\">\n", region[i].part.GetData());
    output.Append(tempString);
    
        int p = 0; 
    
    for (int j = 0; j < scanlines[i]; j++)
    {
        myline = region[i].GetLine(p);
            ++p;
        
        // Print scanline
        sprintf(tempString, "            <line x1=\"%d\" x2=\"%d\" y=\"%d\" />\n",
            myline->x1, myline->x2, myline->y);
        
        output.Append(tempString);
    }
    
    // Print closing element
    sprintf(tempString, "        </Region>\n");
    output.Append(tempString);
    }
    
    // Print closing Race element
    sprintf(tempString, "    </Race>\n");
    output.Append(tempString);
    
    // Now try and write that all to the output file
    if (!VFS->WriteFile(outname, output, strlen(output)) )
    fprintf(stderr, "Error - Could not write to file %s!\n", outname);
}


void psTxPrtParse::PrintUsage()
{
    fprintf(stdout, "\nUsage: texparse -<part>=<colorref>... -race=<race> filename\n\n");
    fprintf(stdout, "Note: filename should NOT be a VFS path.  Use real-world paths.\n");
    fprintf(stdout, "Provide as many unique part/colorref pairs as you like.\n");
    fprintf(stdout, "Example: >texparse -nose=ff0000 -race=elf basicelf.png\n\n");
}


void psTxPrtParse::Start()
{
    // Print out some messages
    fprintf(stdout, "Race: %s\n", race);

    // Load the image
    csRef<iImage> image = LoadImage(filename, CS_IMGFMT_TRUECOLOR);
    
    // Determine image height
    fprintf(stdout, "Checking texture height...");
    int height = image->GetHeight();
    fprintf(stdout, "%d\n", height);

    // Determine image width
    fprintf(stdout, "Checking texture width...");
    int width = image->GetWidth();
    fprintf(stdout, "%d\n", width);
    
    // Create csRGBpixels
    csRGBpixel *destpix = (csRGBpixel *)image->GetImageData();

    int x1=0;
    long index = 0;
    psScanline *scanline;
    
    for (int c = 0; c < cmdOptions; c++)
    {
    scanlines[c] = 0;
    
    // Iterate down each row
    for (int i = 0; i < height; i++)
    {
        // Iterate over each column
        for (int j = 0; j < width; j++)
        {
        // calculate linear array index
        index = j + (i * width);
        
        // Check and see if there is a matching color
        if (destpix[index] == colormask[c])
        {
            // We matched, check previous color if this is not beginning
            if (j != 0)
            {
            // Check to see if this is beginning of scanline
            if (destpix[index-1] != destpix[index])
                x1 = j;
            }
            else
            x1 = j;
        }
        
        // No color match, lets check if we just ended a scanlins
        else
        {
            if (j != 0)
            {
            if (destpix[index-1] == colormask[c])
            {
                scanline = new psScanline(x1, j, i);
                region[c].AddScanline(scanline);
                scanlines[c]++;
            }
            }
        }
        }
    }
    
    fprintf(stdout, "Found %d scanlines for the %s\n", scanlines[c], region[c].part.GetData());
    }
    
    WriteFile();
}

csPtr<iImage> psTxPrtParse::LoadImage(const char* name, int Format)
{
    // Check if we have a pointer to the ImageLoader plugin
    if (!ImageLoader)
    return NULL;
    
    // Check if we were passed an invalid format
    if (Format & CS_IMGFMT_INVALID)
    {
    Format = CS_IMGFMT_TRUECOLOR;
    }
    
    // Create an image file and buffer
    // Then read the file into the buffer
    csRef<iImage> ifile;
    csRef<iDataBuffer> buf = VFS->ReadFile(name);
    
    // Check to see if for some reason the buffer was not
    // created or is empty
    if (!buf || !buf->GetSize() )
    {
    fprintf(stderr, "Could not open image file '%s' on VFS!", name);
    }
    else
    {
    // Load the image into the image class based on the given
    //    format
    ifile = ImageLoader->Load(buf, Format);
    
    if (!ifile)
            fprintf(stderr, "Could not load image '%s'. Unknown format or wrong extension!", name);        
    }
    
    // Check if the image class was not created correctly
    if (!ifile)
    {
        ifile = csCreateXORPatternImage(32, 32, 5);
    }
    
    // Give the image a VFS compatible name.
    csRef<iDataBuffer> xname = VFS->ExpandPath(name);
    ifile->SetName(**xname);
    
    ifile->IncRef ();
    return csPtr<iImage> (ifile);
}

/*---------------*
 * Main function
 *---------------*/
int main(int argc, char* argv[])
{
    parser = new psTxPrtParse();

    char str[1000];

    if ( parser->Initialize(argc, argv) )
    parser->Start();
    else
    fgets(str,1000,stdin);

    // save for Destroy call below
    iObjectRegistry *obj_reg = parser->object_reg;

    delete parser;

    csInitializer::DestroyApplication(obj_reg);
    
    return 0;
}

