/******************************************************************************
 *{@C
 * Copyright:   2018-2020 Paul Obermeier (obermeier@poSoft.de)
 *
 *              See the file "Mawt_License.txt" for information on
 *              usage and redistribution of this file, and for a
 *              DISCLAIMER OF ALL WARRANTIES.
 *
 * Description: SWIG file for wrapping Tk photo images.
 *
 *****************************************************************************/

%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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

static int PhotoChans (Tcl_Interp *interp, char *photoName) 
{
    Tk_PhotoHandle photo;
    Tk_PhotoImageBlock photoCont;

    photo = Tk_FindPhoto(interp, photoName);
    if (!photo) {
        Tcl_AppendResult (interp, "cannot find photo image: ",
                          photoName, (char *)NULL);
        return 0;
    }
    Tk_PhotoGetImage(photo, &photoCont);
    return photoCont.pixelSize;
}

static int VectorFromPhoto (Tcl_Interp *interp, char *photoName,
                            void *imgVector, int numChans,
                            float scale, float offset)
{
    Tk_PhotoHandle photo;
    Tk_PhotoImageBlock photoCont;
    int x, y, w, h, chansInImg, chansToSkip;
    unsigned char *srcPtr;
    unsigned char *imgVec = (unsigned char *) imgVector;

    photo = Tk_FindPhoto(interp, photoName);
    if (!photo) {
        Tcl_AppendResult (interp, "cannot find photo image: ",
                          photoName, (char *)NULL);
        return TCL_ERROR;
    }
    Tk_PhotoGetImage(photo, &photoCont);
    w = photoCont.width;
    h = photoCont.height;
    chansInImg = photoCont.pixelSize;

    srcPtr = photoCont.pixelPtr;

    if (numChans > chansInImg || numChans <= 0) {
        numChans = chansInImg;
    }
    chansToSkip = chansInImg - numChans;

    for (y = h-1; y >= 0; y--) {
        if (numChans == 1) {
            for (x = 0; x < w; x++) {
                imgVec[y*w+x] = (unsigned char) (*srcPtr++ * scale + offset);
                srcPtr += chansToSkip;
            }
        } else if (numChans == 2) {
            for (x = 0; x < w; x++) {
                imgVec[(y*w+x)*2 +0] = (unsigned char) (*srcPtr++ * scale + offset);
                imgVec[(y*w+x)*2 +1] = (unsigned char) (*srcPtr++ * scale + offset);
                srcPtr += chansToSkip;
            }
        } else if (numChans == 3) {
            for (x = 0; x < w; x++) {
                imgVec[(y*w+x)*3 +0] = (unsigned char) (*srcPtr++ * scale + offset);
                imgVec[(y*w+x)*3 +1] = (unsigned char) (*srcPtr++ * scale + offset);
                imgVec[(y*w+x)*3 +2] = (unsigned char) (*srcPtr++ * scale + offset);
                srcPtr += chansToSkip;
            }
        } else {
            for (x = 0; x < w; x++) {
                imgVec[(y*w+x)*4 +0] = (unsigned char) (*srcPtr++ * scale + offset);
                imgVec[(y*w+x)*4 +1] = (unsigned char) (*srcPtr++ * scale + offset);
                imgVec[(y*w+x)*4 +2] = (unsigned char) (*srcPtr++ * scale + offset);
                imgVec[(y*w+x)*4 +3] = (unsigned char) (*srcPtr++ * scale + offset);
                srcPtr += chansToSkip;
            }
        }
    }
    return TCL_OK;
}

/* Enhanced function, which keeps track of the scanline order of the input vector. */
static int VectorToPhoto (Tcl_Interp *interp, void *imgVector, 
                          const char *photoName, int width, int height,
                          int numChans, int linesAreBottomUp)
{
    int y, yStart, usedChans;
    Tk_PhotoHandle photo;
    Tk_PhotoImageBlock photoCont;
    Tk_PhotoImageBlock photoCont2;
    unsigned char *imgVec = (unsigned char *) imgVector;
    unsigned char *srcPtr, *dstPtr;
    unsigned char *srcStop;
    unsigned char *pixBuf;

    if (!(photo = Tk_FindPhoto (interp, (char *)photoName))) {
        Tcl_AppendResult (interp, "cannot find photo image: ",
                          photoName, (char *)NULL);
        return TCL_ERROR;
    }

    #if (TK_MAJOR_VERSION > 8) || \
       ((TK_MAJOR_VERSION == 8) && (TK_MINOR_VERSION >= 5))
        Tk_PhotoSetSize (interp, photo, width, height);
    #else
        Tk_PhotoSetSize (photo, width, height);
    #endif

    usedChans = (numChans <= 3? 3: 4);

    if (!(pixBuf = (unsigned char *)malloc (width * height * usedChans * sizeof (unsigned char)))) {
        Tcl_AppendResult (interp, "cannot alloc scanline bufer for photo: ",
                          photoName, (char *)NULL);
        return TCL_ERROR;
    }

    photoCont.pixelSize = usedChans;
    photoCont.pitch = width * usedChans;
    photoCont.width = width;
    photoCont.height = height;
    photoCont.offset[0] = 0;
    photoCont.offset[1] = 1;
    photoCont.offset[2] = 2;
    photoCont.offset[3] = (usedChans == 3? 0: 3);

    photoCont.pixelPtr = pixBuf;
    srcPtr = imgVec;

    if (linesAreBottomUp) {
        /* Image in source vector is in bottom-up scanline order, so we have to
           invert the row order before transfering the image data into a Tk photo.
         */ 
        for (y = 0; y < height; y++) {
            dstPtr = pixBuf + (height - 1 - y) * (numChans * width);
            srcStop = srcPtr + numChans * width;
            if (numChans == 1) {
                while (srcPtr < srcStop) {
                    *(dstPtr++) = *srcPtr;
                    *(dstPtr++) = *srcPtr;
                    *(dstPtr++) = *(srcPtr++);
                }
            } else if (numChans == 2) {
                while (srcPtr < srcStop) {
                    *(dstPtr++) = *srcPtr;
                    *(dstPtr++) = *srcPtr;
                    *(dstPtr++) = *(srcPtr++);
                    *(dstPtr++) = *(srcPtr++);
                }
            } else if (numChans == 3) {
                memcpy (dstPtr, srcPtr, 3 * width);
                srcPtr += 3 * width;
            } else {
                memcpy (dstPtr, srcPtr, 4 * width);
                srcPtr += 4 * width;
            }
        }
    } else {
        /* Image in source vector is in top-bottom scanline order, which is
         * the order of a Tk photo. 
         */ 
        dstPtr = pixBuf;
        if (numChans == 3 || numChans == 4) {
            /* No need to copy. Just set the blockPtr to the image data. */
            photoCont.pixelPtr = imgVec;
        } else {
            for (y = 0; y < height; y++) {
                srcStop = srcPtr + numChans * width;
                if (numChans == 1) {
                    while (srcPtr < srcStop) {
                        *(dstPtr++) = *srcPtr;
                        *(dstPtr++) = *srcPtr;
                        *(dstPtr++) = *(srcPtr++);
                    }
                } else if (numChans == 2) {
                    while (srcPtr < srcStop) {
                        *(dstPtr++) = *srcPtr;
                        *(dstPtr++) = *srcPtr;
                        *(dstPtr++) = *(srcPtr++);
                        *(dstPtr++) = *(srcPtr++);
                    }
                }
            }
        }
    }
    #if (TK_MAJOR_VERSION > 8) || \
        ((TK_MAJOR_VERSION == 8) && (TK_MINOR_VERSION >= 5))
        Tk_PhotoPutBlock (interp, photo, &photoCont, 0, 0,
                          width, height, TK_PHOTO_COMPOSITE_SET);
    #elif ((TK_MAJOR_VERSION == 8) && (TK_MINOR_VERSION > 3))
        Tk_PhotoPutBlock (photo, &photoCont, 0, 0,
                          width, height, TK_PHOTO_COMPOSITE_OVERLAY);
    #else
        Tk_PhotoPutBlock (photo, &photoCont, 0, 0, width, height);
    #endif

    free (pixBuf);
    return TCL_OK;
}

%}

int PhotoChans      (Tcl_Interp *interp, char *imgName);
int VectorFromPhoto (Tcl_Interp *interp, char *imgName, void *imgVec, 
                     int numChans, float scale, float offset);
int VectorToPhoto   (Tcl_Interp *interp, void *imgVec, 
                     const char *photoName, int width, int height,
                     int numChans, int linesAreBottomUp);
