/**************************************************************************
 *{@C
 *      Copyright:      1988-2020 Paul Obermeier (obermeier@poSoft.de)
 *
 *      Module:         ImageProcessing
 *      Filename:       IPT_TclLib.c
 *
 *      Author:         Paul Obermeier
 *
 *      Description:    The interface between the image
 *                      library and the Tcl interpreter.
 *
 *      Additional documentation:
 *                      None.
 *
 *      Exported functions:
 *                      IPT_InitTclImgLib
 *
 **************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "UT_Compat.h"

#include "UT_Error.h"
#include "UT_Memory.h"
#include "FF_Image.h"
#include "IP_Image.h"

#include <tcl.h>
#include "UTT_TclIf.h"
#include "IPT_TclIf.h"
#include "IPT_ImagePrivate.h"

static UT_Bool
    initialized = UT_False;

/* Delete an image. */

static void DeleteImg (ClientData clientData)
{
    IP_ImageId image = (IP_ImageId) clientData;
 
    #if defined (DEBUG)
        printf ("Delete image (0x%x)\n", image);
        fflush (stdout);
    #endif
    IP_ImageDelete (image);
    return;
}

static Tcl_HashTable tclImgTable;

static UTT_TclConstInt32 pxconst[] = 
{
    {FF_ImgChanTypeRed,         "RED"},
    {FF_ImgChanTypeGreen,       "GREEN"},
    {FF_ImgChanTypeBlue,        "BLUE"},
    {FF_ImgChanTypeMatte,       "MATTE"},
    {FF_ImgChanTypeRedMatte,    "REDMATTE"},
    {FF_ImgChanTypeGreenMatte,  "GREENMATTE"},
    {FF_ImgChanTypeBlueMatte,   "BLUEMATTE"},
    {FF_ImgChanTypeGray,        "BRIGHTNESS"},
    {FF_ImgChanTypeDepth,       "DEPTH"},
    {FF_ImgChanTypeHoriSnr,     "HNORMAL"},
    {FF_ImgChanTypeVertSnr,     "VNORMAL"},
    {FF_ImgChanTypeTemperature, "TEMPERATURE"},
    {FF_ImgChanTypeRadiance,    "RADIANCE"},
    {FF_NumImgChanTypes,        "NUMCHAN"},
    {FF_ImgFmtTypeUByte,        "UBYTE"},
    {FF_ImgFmtTypeFloat,        "FLOAT"},
    {FF_ImgComprTypeRle,        "RLE"},

    /* Drawing modes */
    {IP_DrawModeReplace,        "REPLACE"},
    {IP_DrawModeAdd,            "ADD"},
    {IP_DrawModeSub,            "SUB"},
    {IP_DrawModeXor,            "XOR"},

    /* Pyramid "out of range" pixel filling modes for blending and warping */
    {IP_FillModeFill,           "IMGFILL"},
    {IP_FillModeWrap,           "IMGWRAP"},
    {IP_FillModeClip,           "IMGCLIP"},

    /* Field interpolation modes. */
    {IP_UnlaceModeCopy,         "REPLICATE"},
    {IP_UnlaceModeInterpolate,  "INTERPOLATE"}
};

static UTT_TclElemFunc imgmodeelemfunc[] =
{
    {IPT_GetFmtTypeName,        "GetFmtTypeName"},
    {IPT_GetChanTypeName,       "GetChanTypeName"},
    {IPT_GetComprTypeName,      "GetComprTypeName"},

    {IPT_GetFileInfo,           "GetFileInfo"},
    {IPT_GetFileFormat,         "GetFileFormat"},

    {IPT_GetTextSize,           "GetTextSize"},
    {IPT_GetTextScale,          "GetTextScale"},
    {IPT_SetTextScale,          "SetTextScale"},

    {IPT_SetFormat,             "SetFormat"},
    {IPT_GetFormat,             "GetFormat"},
    {IPT_SetCompression,        "SetCompression"},
    {IPT_GetCompression,        "GetCompression"},
    {IPT_SetDrawMask,           "SetDrawMask"},
    {IPT_GetDrawMask,           "GetDrawMask"},
    {IPT_SetDrawColor,          "SetDrawColor"},
    {IPT_GetDrawColor,          "GetDrawColor"},
    {IPT_SetDrawMode,           "SetDrawMode"},
    {IPT_GetDrawMode,           "GetDrawMode"},
    {IPT_SetColorCorrect,       "SetColorCorrect"},
    {IPT_GetColorCorrect,       "GetColorCorrect"},
    {IPT_SetNumThreads,         "SetNumThreads"},
    {IPT_GetNumThreads,         "GetNumThreads"}
};

static UTT_TclObjFunc imgfunc[] =
{
    {IPT_NewImage,              "NewImage"},
    {IPT_NewImageFromFile,      "NewImageFromFile"},
    {IPT_NewImageFromPhoto,     "NewImageFromPhoto"}
};

static UTT_TclElemFunc imgelemfunc[] =
{
    {IPT_GetImgInfo,            "GetImgInfo"},
    {IPT_GetImgFormat,          "GetImgFormat"},
    {IPT_DeleteImage,           "DeleteImage"},
    {IPT_ReadImage,             "ReadImage"},
    {IPT_WriteImage,            "WriteImage"},
    {IPT_WriteSimple,           "WriteSimple"},
    {IPT_DrawPixel,             "DrawPixel"},
    {IPT_DrawLine,              "DrawLine"},
    {IPT_DrawRect,              "DrawRect"},
    {IPT_DrawEllipse,           "DrawEllipse"},
    {IPT_DrawCircle,            "DrawCircle"},
    {IPT_DrawBrushLine,         "DrawBrushLine"},
    {IPT_DrawBrush,             "DrawBrush"},
    {IPT_MergeBrush,            "MergeBrush"},
    {IPT_DrawText,              "DrawText"},
    {IPT_Blank,                 "Blank"},
    {IPT_BlankRect,             "BlankRect"},
    {IPT_GetPixel,              "GetPixel"},
    {IPT_CopyRect,              "CopyRect"},
    {IPT_WrapRect,              "WrapRect"},
    {IPT_CopyChannel,           "CopyChannel"},
    {IPT_CopyImage,             "CopyImage"},
    {IPT_DrawAAPixel,           "DrawAAPixel"},
    {IPT_DrawAALine,            "DrawAALine"},
    {IPT_DrawAAText,            "DrawAAText"},
    {IPT_CompositeAlpha,        "CompositeAlpha"},
    {IPT_CompositeColorMatte,   "CompositeColorMatte"},
    {IPT_CompositeDepth,        "CompositeDepth"},
    {IPT_CrossDissolve,         "CrossDissolve"},
    {IPT_Interlace,             "Interlace"},
    {IPT_Unlace,                "Unlace"},
    {IPT_WarpKeypoint,          "WarpKeypoint"},
    {IPT_BlendKeypoint,         "BlendKeypoint"},
    {IPT_WarpFunct,             "WarpFunct"},
    {IPT_BlendFunct,            "BlendFunct"},
    {IPT_ChangeGamma,           "ChangeGamma"},
    {IPT_RemapValues,           "RemapValues"},
    {IPT_Blur,                  "Blur"},
    {IPT_DepthBlur,             "DepthBlur"},
    {IPT_Derivates,             "Derivates"},
    {IPT_Median,                "Median"},
    {IPT_MedianSequence,        "MedianSequence"},
    {IPT_Dilatation,            "Dilatation"},
    {IPT_Erosion,               "Erosion"},
    {IPT_Flicker,               "Flicker"},
    {IPT_CutOff,                "CutOff"},
    {IPT_Threshold,             "Threshold"},
    {IPT_MarkNonZeroPixels,     "MarkNonZeroPixels"},
    {IPT_DifferenceImage,       "DifferenceImage"},
    {IPT_FlipDiagonal,          "FlipDiagonal"},
    {IPT_FlipHorizontal,        "FlipHorizontal"},
    {IPT_FlipVertical,          "FlipVertical"},
    {IPT_Rotate,                "Rotate"},
    {IPT_ScaleRect,             "ScaleRect"},
    {IPT_GenNoise,              "GenNoise"},
    {IPT_GenJuliaSet,           "GenJuliaSet"},
    {IPT_GenMandelbrot,         "GenMandelbrot"},
    {IPT_Histogram,             "Histogram"},
    {IPT_GetChanStats,          "GetChanStats"},
    {IPT_GetChanRange,          "GetChanRange"},
    {IPT_GetDepthRange,         "GetDepthRange"},
    {IPT_GetTemperatureRange,   "GetTemperatureRange"},
    {IPT_GetRadianceRange,      "GetRadianceRange"},
    {IPT_DiffChannel,           "DiffChannel"},
    {IPT_Correlate,             "Correlate"},
    {IPT_FindPattern,           "FindPattern"},
    {IPT_GetSample,             "GetSample"},
    {IPT_ShearVertical,         "ShearVertical"},
    {IPT_ShearHorizontal,       "ShearHorizontal"},

    {IPT_ApplyLogo,             "ApplyLogo"},
    {IPT_AsPhoto,               "AsPhoto"}
};

static int ElementCmd (TCLPARAMLIST)
{
    Int32 i;

    if (argc < 2) {
        Tcl_AppendResult (interp, "Available image commands:\n", (char *)NULL); 
        for (i=0; i< sizeof (imgelemfunc) / sizeof (UTT_TclElemFunc); i++) {
            Tcl_AppendResult (interp, "\t", imgelemfunc[i].name, "\n", (char *)NULL);
        }
        return TCL_ERROR;
    }

    for (i=0; i< sizeof (imgelemfunc) / sizeof (UTT_TclElemFunc); i++) {
        if (strcmp (argv[1], imgelemfunc[i].name) == 0) {
            if (!imgelemfunc[i].func (clientData, interp, argc, argv)) {
                Tcl_AppendResult (interp, "\n", UT_ErrStr, "\n(",
                                  UT_ErrAddStr, ")\n", (char *)NULL); 
                return TCL_ERROR;
            } else {
                return TCL_OK;
            }
        }
    }

    Tcl_AppendResult (interp, "Bad image command \"", argv[1], (char *)NULL); 
    return TCL_ERROR;
}

static int ImageCmd (TCLPARAMLIST)
{
    Int32 i;
    UT_Bool found = UT_False;
    IP_ImageId image;
    char imgName[20];
    Tcl_HashEntry *hashEntryPtr;
    int newEntryFlag;

    static Int32 imgNo = 1;

    if (argc < 2) {
        Tcl_AppendResult (interp, "Available image commands:\n", (char *)NULL);
        for (i=0; i< sizeof (imgfunc) / sizeof (UTT_TclObjFunc); i++)
            Tcl_AppendResult (interp, "\t", imgfunc[i].name, "\n", (char *)NULL);
        return TCL_ERROR;
    }

    sprintf (imgName, "poImage%d", imgNo);

    for (i=0; i< sizeof (imgfunc) / sizeof (UTT_TclObjFunc); i++) {
        if (strcmp (argv[1], imgfunc[i].name) == 0) {
            if (!imgfunc[i].func ((ClientData *)&image, interp, argc, argv)) {
                Tcl_AppendResult (interp, "\n", UT_ErrStr, "\n(",
                                  UT_ErrAddStr, ")\n", (char *)NULL);
                return TCL_ERROR;
            } else {
                found = UT_True;
                break;
            }
        }
    }
    if (!found) {
        Tcl_AppendResult (interp, "Available image commands:\n", (char *)NULL);
        for (i=0; i< sizeof (imgfunc) / sizeof (UTT_TclElemFunc); i++) {
            Tcl_AppendResult (interp, "\t", imgfunc[i].name, "\n", (char *)NULL);
        }
        return TCL_ERROR;
    }

    #if defined (DEBUG)
        printf ("New image \"%s\" (0x%x)\n", imgName, image);
        fflush (stdout);
    #endif

    hashEntryPtr = Tcl_CreateHashEntry (&tclImgTable, imgName, &newEntryFlag);
    if (newEntryFlag == 0) {
        printf ("Hash entry for %s already exists\n", imgName);
        return TCL_ERROR;
    }
    if (hashEntryPtr == NULL) {
        printf ("Could not create hash table entry for %s\n", imgName);
        return TCL_ERROR;
    }
    Tcl_SetHashValue (hashEntryPtr, image);
    Tcl_CreateCommand (interp, imgName, (Tcl_CmdProc *)ElementCmd,
                       (ClientData) image, (Tcl_CmdDeleteProc *) DeleteImg);
    Tcl_SetResult (interp, imgName, TCL_VOLATILE);
    imgNo++;

    return TCL_OK;
}

static int ImageModeCmd (TCLPARAMLIST)
{
    Int32 i;
    UT_Bool found = UT_False;

    if (argc < 2) {
        Tcl_AppendResult (interp, "Available imagemode commands:\n", (char *)NULL); 
        for (i=0; i< sizeof (imgmodeelemfunc) / sizeof (UTT_TclElemFunc); i++)
            Tcl_AppendResult (interp, "\t", imgmodeelemfunc[i].name, "\n", (char *)NULL);
        return TCL_ERROR;
    }
    for (i=0; i< sizeof (imgmodeelemfunc) / sizeof (UTT_TclElemFunc); i++) {
        if (strcmp (argv[1], imgmodeelemfunc[i].name) == 0) {
            if (!imgmodeelemfunc[i].func ((ClientData)NULL, interp, argc, argv)) {
                Tcl_AppendResult (interp, "\n", UT_ErrStr, "\n(",
                                  UT_ErrAddStr, ")\n", (char *)NULL); 
                return TCL_ERROR;
            } else {
                found = UT_True;
                break;
            }
        }
    }
    if (!found) {
        Tcl_AppendResult (interp, "Available imgmode commands:\n", (char *)NULL);
        for (i=0; i< sizeof (imgmodeelemfunc) / sizeof (UTT_TclElemFunc); i++)
            Tcl_AppendResult (interp, "\t", imgmodeelemfunc[i].name, "\n", (char *)NULL);
        return TCL_ERROR;
    }

    return TCL_OK;
}

static int SetInt32Global (Tcl_Interp *interp, char *varName, int value)
{
    char valStr[50];
    sprintf (valStr, "%d", value);
    if (!Tcl_SetVar (interp, varName, valStr, TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG))
        return TCL_ERROR;
    return TCL_OK;
}

static int CreateConstants (Tcl_Interp *interp)
{
    Int32 i;

    for (i=0; i< sizeof (pxconst) / sizeof (UTT_TclConstInt32); i++) {
        if (SetInt32Global (interp, pxconst[i].name, pxconst[i].value) != TCL_OK) {
            return TCL_ERROR;
        }
    }
    return TCL_OK;
}

int IPT_GetImage (Tcl_Interp *interp, char *imgVarName, IP_ImageId *img)
{
    Tcl_HashEntry *hashEntryPtr;
    IP_ImageId image;

    hashEntryPtr = Tcl_FindHashEntry (&tclImgTable, imgVarName);
    if (!hashEntryPtr) {
        Tcl_AppendResult (interp, "No image named ", imgVarName, (char *)NULL);
        return TCL_ERROR;
    }
    image = (IP_ImageId)Tcl_GetHashValue (hashEntryPtr);
    if (!image) {
        return TCL_ERROR;
    }
    *img = image;
    return TCL_OK;
}

/***************************************************************************
 *[@e
 *      Name:           IPT_InitTclImgLib
 *
 *      Usage:          Initialize the Tcl image library.
 *
 *      Synopsis:       int IPT_InitTclImgLib (Tcl_Interp *interp)
 *
 *      Description:    Make the image data types and functions
 *                      available to the Tcl interpreter.
 *
 *      Return value:   TCL_OK if successful, else TCL_ERROR.
 *
 *      See also:       
 *
 ***************************************************************************/

int IPT_InitTclImgLib (Tcl_Interp *interp)
{
    if (initialized) {
        return TCL_OK;
    }

    Tcl_InitHashTable (&tclImgTable, TCL_STRING_KEYS);

    Tcl_CreateCommand (interp, "poImage", (Tcl_CmdProc *) ImageCmd,
                       (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
    Tcl_CreateCommand (interp, "poImageMode", (Tcl_CmdProc *) ImageModeCmd,
                       (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
    CreateConstants (interp);
    initialized = UT_True;
    return TCL_OK;
}
