module main;

import std.stdio;
import std.json;
import std.file;
import std.conv;
import std.string;
import gamut;

void usage()
{
    stderr.writeln();
    stderr.writeln("Divise-texture-par-3");
    stderr.writeln("l'indispensable outil de division par 3\n");
    stderr.writeln("usage: divise-texture-par-3 input.png output.png\n");
    stderr.writeln();
    stderr.writeln("Arguments:");
    stderr.writeln("    --help        Shows this help.");
    stderr.writeln("A noter qu'un fichier diff.png est cree qui montre pourquoi on peut pas réduire par 3 en rouge.");
}


int main(string[] args)
{
    try
    {
        string input = null;
        string output = null;        
        bool showHelp = false;

        for(int i = 1; i < args.length; ++i)
        {
            string arg = args[i];
            if (arg == "-h" || arg == "--help")
            {
                showHelp = true;
            }
            else
            {
                if (input)
                {
                    if (output)
                        throw new Exception("Too many files provided");
                    else
                        output = arg;
                }
                else
                    input = arg;
            }
        }

        if (showHelp || input is null || output is null)
        {
            usage();
            return 0;
        }

        processFile(input, output, "diff.png");
    }
    catch(Exception e)
    {
        writefln("error: %s", e.message);
        usage();
        return 1;
    }
    return 0;
}

struct Color
{
    ubyte r, g, b, a;
}

void processFile(string inputPath, string outputPath, string diffPath)
{
    Image input;
    input.loadFromFile(inputPath);
    if (!input.isValid)
        throw new Exception("cant load " ~ inputPath);

    input.convertTo(PixelType.rgba8);

    if ( (input.width % 3) || (input.height % 3))
        throw new Exception("input is not a multiple of 3 in size");

    int w = input.width;
    int h = input.height;
    int w3 = input.width / 3;
    int h3 = input.height / 3;

    // show outlier pixels
    // compare each pixel with pixel at center of 3x3 patch, make it RED if not
    // else keep original color toned down 0.5x
    Image diff;
    diff.create(w, h, PixelType.rgba8);

    bool successOfDownscale = true;

    for(int y = 0; y < h; ++y)
    {        
        Color[] scanInput    = cast(Color[]) input.scanline(y);
        
        Color[] scanDiff     = cast(Color[]) diff.scanline(y);

        // Copy input to diff, darkened
        for(int x = 0; x < w; ++x)
        {
            Color c = scanInput[x];
            c.r /= 2;
            c.g /= 2;
            c.b /= 2;
            scanDiff[x] = c;
        }

        int refY = 1 + 3 * (y / 3);
        Color[] scanInputRef = cast(Color[]) input.scanline(refY);

        for(int x = 0; x < w; ++x)
        {   
            int refX = 1 + 3 * (x / 3);
            Color centerPixel = scanInputRef[refX];

            Color thisPixel = scanInput[x];
            if (!isSameColor(thisPixel, centerPixel))
            {
                scanDiff[x] = Color(255, 0, 0, 255); 
                successOfDownscale = false;
            }
            else
            {
                scanDiff[x].g = 255; // green = OK
            }
        }
    }

    if (!diff.saveToFile(diffPath))
    {
        throw new Exception("Couln't save " ~ diffPath);
    }
    

    // Output image

    Image output;
    output.create(w3, h3, PixelType.rgba8);

    // if the diff image isn't all black, there are errors that need to be fixed

    // always create the divided by 3 image
    // this is just sampling in the middle
    for(int y = 0; y < h3; ++y)
    {
        Color[] scanInput = cast(Color[]) input.scanline(y*3);
        Color[] scanOutput = cast(Color[]) output.scanline(y);
        for(int x = 0; x < w3; ++x)
        {
            // Take the first pixel of the 3x3 patch that isn't fully transparent

            Color found;
            for (int yy = 0; yy < 3; ++yy)
            {
                for (int xx = 0; xx < 3; ++xx)
                {
                    Color c = (cast(Color[]) input.scanline(y*3+yy))[x*3+xx];
                    if (c.a != 0)
                    {
                        found = c;
                        goto okay;
                    }
                }
            }
            okay:


            scanOutput[x] = found;
        }
    }

    if (!output.saveToFile(outputPath))
    {
        throw new Exception("Couln't save " ~ outputPath);
    }

    if (successOfDownscale)
    {
        writeln("SUCCESS");
        writeln("Image reduced by 3x, do not forget to use GIMP to make it palettized again!");
    }
    else
    {
        writeln("FAILURE");
        writeln("See diff.png for the problem in input image that prevents its reduction.");
        writeln("Not everything is align to 3x3 pixel patchs.");
    }
}

bool isSameColor(Color a, Color b)
{
    if ((a.a == 0) && (b.a == 0))
        return true; // do not check colors of transparent pixels
    else
        return a == b;
}