/*
 * imgtools-test.c --
 *
 *      Provides some utils for testing
 *      Exposes c functions to Tcl, where needed for testing.
 *
 * Copyright (c) 2016-2017 by Simon Bachmann <simonbachmann@bluewin.ch>
 * All rights reserved.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 * 
 */

#include <tcl.h>
#include <tk.h>
#include <stdio.h>
#include <limits.h>

#include "imgtoolsDecl.h"
// #include "config.h"
#include "imgtools-test.h"
#include "debugmsg.h"

/*
 * Prototypes
 */
static int CompareImages (ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]);
static int GetPixel (ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]);
static int GetIntMax (ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]);
static int GetIntMin (ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]);

/*
 * CompareImages --
 * 
 *      compares two Tk photo images for equalitiy
 * 
 * Results:
 *      1 if the images are equal, a list of fail coords if they differ
 *      (coords of first pixel that is different)
 * 
 * Side effects: 
 *      none.
 */
static int CompareImages (clientData, interp, objc, objv)
    ClientData clientData;  /* not used */
    Tcl_Interp *interp;     /* return result to this interpreter */
    int objc;               /* arg count */
    Tcl_Obj *const objv[];  /* arguments */
{
    /*
     * Do some basic args check
     */
    if (objc != 3) {
        Tcl_WrongNumArgs(interp, 1, objv, "image1 image2");
        return TCL_ERROR;
    }
    
    Tk_PhotoHandle img1, img2;
    Tk_PhotoImageBlock data1, data2;
    
    if ((img1 = Tk_FindPhoto(interp, Tcl_GetString(objv[1]))) == NULL) {
        Tcl_SetObjResult(interp, Tcl_ObjPrintf("Image %s not found", Tcl_GetString(objv[1])));
        return TCL_ERROR;
    }
    
    if ((img2 = Tk_FindPhoto(interp, Tcl_GetString(objv[2]))) == NULL) {
        Tcl_SetObjResult(interp, Tcl_ObjPrintf("Image %s not found", Tcl_GetString(objv[2])));
        return TCL_ERROR;
    }
    
    Tk_PhotoGetImage(img1, &data1);
    Tk_PhotoGetImage(img2, &data2);
    
    if (data1.width != data2.width || data1.height != data2.height) {
        Tcl_SetObjResult(interp, Tcl_NewIntObj(0));
        //printf("CompareImages: size of the images does not match\n");
        return TCL_OK;
    }
    
    Tcl_Obj *result = NULL;
    Tcl_Obj *coords[2];
    int offset;
    for (int x = data1.width; x--; ) {
        for (int y = data1.height; y--; ) {
            for (int c = 4; c--; ) {
                offset = x*data1.pixelSize + y*data1.pitch + data1.offset[c];
                if (data1.pixelPtr[offset] != data2.pixelPtr[offset]) {
                    if (result == NULL) {
                        coords[0] = Tcl_NewIntObj(x);
                        coords[1] = Tcl_NewIntObj(y);
                        result = Tcl_NewListObj(2, coords);
                    }
                    debugmsg(interp, DEBUG_TESTS, "CompareImages: images differ at pixel %dx%d, channel %d (%d/%d)\n", x, y, c, data1.pixelPtr[offset], data2.pixelPtr[offset]);
                }
            }
        }
    }
    if (result == NULL) {
        result = Tcl_NewIntObj(1);
    }
    Tcl_SetObjResult(interp, result);
    return TCL_OK;
}
    
/*
 * GetPixel --
 * 
 *      Returns the RGBA values of one pixel as a tcl list
 * 
 * Results:
 *      a list with the red, green, blue and alpha values of the pixel
 * 
 * Side effects: 
 *      none.
 */
static int GetPixel (clientData, interp, objc, objv)
    ClientData clientData;  /* not used */
    Tcl_Interp *interp;     /* return result to this interpreter */
    int objc;               /* arg count */
    Tcl_Obj *const objv[];  /* arguments: imgName, x, y */
{
    /*
     * Do some basic args check
     */
    if (objc != 4) {
        Tcl_WrongNumArgs(interp, 1, objv, "image x y");
        return TCL_ERROR;
    }
    
    int x, y;
    Tk_PhotoHandle img;
    Tk_PhotoImageBlock data;
    
    if ((img = Tk_FindPhoto(interp, Tcl_GetString(objv[1]))) == NULL) {
        Tcl_SetObjResult(interp, Tcl_ObjPrintf("Image %s not found", Tcl_GetString(objv[1])));
        return TCL_ERROR;
    }
    if (Tcl_GetIntFromObj(interp, objv[2], &x) != TCL_OK) {
        return TCL_ERROR;
    }
    if (Tcl_GetIntFromObj(interp, objv[3], &y) != TCL_OK) {
        return TCL_ERROR;
    }
        
    Tk_PhotoGetImage(img, &data);
    
    int offset;
    Tcl_Obj *result;
    result = Tcl_NewListObj(0, NULL);
    
    for (int c = 0; c < 4; c++) {
        offset = x*data.pixelSize + y*data.pitch + data.offset[c];
        Tcl_ListObjAppendElement(interp, result, Tcl_NewIntObj(data.pixelPtr[offset]));
    }
    Tcl_SetObjResult(interp, result);
    return TCL_OK;
}

/*
 * GetIntMax --
 * 
 *      Returns the max value for a signed int
 * 
 * Result: 
 *      max int
 * 
 * Side effects:
 *      none.
 */
int GetIntMax(clientData, interp, objc, objv)
    ClientData clientData;  /* not used */
    Tcl_Interp *interp;     /* return result to this interpreter */
    int objc;               /* arg count */
    Tcl_Obj *const objv[];  /* arguments: NONE*/
{
    if (objc > 1) {
        Tcl_WrongNumArgs(interp, 1, objv, "");
        return TCL_ERROR;
    }
    
    Tcl_SetObjResult(interp, Tcl_NewIntObj(INT_MAX));
    return TCL_OK;
}

/*
 * GetIntMin --
 * 
 *      Returns the min value for a signed int
 * 
 * Result: 
 *      min int
 * 
 * Side effects:
 *      none.
 */
int GetIntMin(clientData, interp, objc, objv)
    ClientData clientData;  /* not used */
    Tcl_Interp *interp;     /* return result to this interpreter */
    int objc;               /* arg count */
    Tcl_Obj *const objv[];  /* arguments: NONE*/
{
    if (objc > 1) {
        Tcl_WrongNumArgs(interp, 1, objv, "");
        return TCL_ERROR;
    }
    
    Tcl_SetObjResult(interp, Tcl_NewIntObj(INT_MIN));
    return TCL_OK;
}
/*
 *--------------------------------------------------------------
 * 
 * Imgtools-test_Init --
 * 
 *      Init proc, called from Imgtools_Init. (These test commands
 *      are part or Imgtools, not a separate package)
 * 
 * Results:
 *      Standard Tcl result
 * 
 * Side effects:
 *      Creates a namespace ::imgtools::test
 *      Creates additional commands
 * 
 * -------------------------------------------------------------
*/
int Imgtools_Test_Init(interp)
Tcl_Interp *interp; /* The interpreter where the package is loaded */
{
    
    Tcl_Namespace *testNS;
    
    testNS = Tcl_CreateNamespace(interp, "::imgtools::test", NULL, NULL);
    
    Tcl_CreateObjCommand(interp, "::imgtools::test::compareImages", CompareImages, NULL, NULL);
    Tcl_CreateObjCommand(interp, "::imgtools::test::getPixel", GetPixel, NULL, NULL);
    Tcl_CreateObjCommand(interp, "::imgtools::test::getIntMax", GetIntMax, NULL, NULL);
    Tcl_CreateObjCommand(interp, "::imgtools::test::getIntMin", GetIntMin, NULL, NULL);
    
    Tcl_Export(interp, testNS, "*", 1);
    
    return TCL_OK;
    
}
