/*
 * rbcWindow.c --
 *
 *      This module implements additional window functionality "snap".
 *
 * Copyright (c) 2001 BLT was created by George Howlett.
 * Copyright (c) 2009 RBC was created by Samuel Green, Nicholas Hudson, Stanton Sievers, Jarrod Stormo
 * Copyright (c) 2018 Rene Zaumseil

 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#include "tkoGraph.h"

#include <X11/Xlib.h>

static int GetWindowSize(
    Tcl_Interp * interp,
    Window window,
    int *widthPtr,
    int *heightPtr);

/*
 *--------------------------------------------------------------
 *
 * Rbc_SnapObjCmd --
 *
 *      Implementation of the "::tko::snap" command.
 *      Snaps a picture of a window and stores it in a
 *      designated photo image.  The window must be completely
 *      visible or the snap will fail.
 *
 * Results:
 *      Returns a standard Tcl result.  interp->result contains
 *      the list of the graph coordinates. If an error occurred
 *      while parsing the window positions, TCL_ERROR is
 *      returned, then interp->result will contain an error
 *      message.
 *
 * Side effects:
 *      TODO: Side Effects
 *
 *--------------------------------------------------------------
 */
int
Rbc_SnapObjCmd(
	ClientData clientData,  /* Not used. */
	Tcl_Interp *interp,     /* Current interpreter. */
	Tcl_Size objc,               /* Number of arguments. */
	Tcl_Obj *const objv[])  /* Argument objects. */
{
    int x=0;                /* Offset of image from drawable origin, used if >0 */
    int y=0;                /* Offset of image from drawable origin, used if >0 */
    int width=0;            /* Dimension of the drawable, used if >0 */
    int height=0;           /* Dimension of the drawable, used if >0 */
    int destWidth=0;        /* Desired size of the Tk photo, used if >0 */
    int destHeight=0;       /* Desired size of the Tk photo, used if >0 */
    Tk_Window tkmain;
    Tk_Window tkwin;
    int tkWidth, tkHeight;
    Window window;

 	if (objc < 3 || objc > 9) {
		Tcl_WrongNumArgs(interp, 1, objv, "window photo ?x? ?y? ?width? ?height? ?imagewidth? ?imageheight?");
		return TCL_ERROR;
	}
    if (objc > 3 && Tcl_GetIntFromObj(interp,objv[3],&x)!=TCL_OK) {
        Tcl_SetObjResult(interp,
            Tcl_ObjPrintf("wrong x value: %s",Tcl_GetString(objv[3])));
		return TCL_ERROR;
    }
    if (objc > 4 && Tcl_GetIntFromObj(interp,objv[4],&y)!=TCL_OK) {
        Tcl_SetObjResult(interp,
            Tcl_ObjPrintf("wrong y value: %s",Tcl_GetString(objv[4])));
		return TCL_ERROR;
    }
    if (objc > 5 && Tcl_GetIntFromObj(interp,objv[5],&width)!=TCL_OK) {
        Tcl_SetObjResult(interp,
            Tcl_ObjPrintf("wrong width value: %s",Tcl_GetString(objv[5])));
		return TCL_ERROR;
    }
    if (objc > 6 && Tcl_GetIntFromObj(interp,objv[6],&height)!=TCL_OK) {
        Tcl_SetObjResult(interp,
            Tcl_ObjPrintf("wrong height value: %s",Tcl_GetString(objv[6])));
		return TCL_ERROR;
    }
    if (objc > 7 && Tcl_GetIntFromObj(interp,objv[7],&destWidth)!=TCL_OK) {
        Tcl_SetObjResult(interp,
            Tcl_ObjPrintf("wrong imagewidth value: %s",Tcl_GetString(objv[7])));
		return TCL_ERROR;
    }
    if (objc > 8 && Tcl_GetIntFromObj(interp,objv[8],&destHeight)!=TCL_OK) {
        Tcl_SetObjResult(interp,
            Tcl_ObjPrintf("wrong imageheight value: %s",Tcl_GetString(objv[8])));
		return TCL_ERROR;
    }

	tkmain = Tk_MainWindow(interp);
	if (tkmain == NULL) {
        Tcl_SetObjResult(interp,
            Tcl_ObjPrintf("can't get main window"));
		return TCL_ERROR;
	}
	/* Name of window to make photo from */
    tkwin = Tk_NameToWindow(interp, Tcl_GetString(objv[1]), tkmain);
    if(tkwin == NULL) {
        Tcl_SetObjResult(interp,
            Tcl_ObjPrintf("can't get name of main window"));
		return TCL_ERROR;
    }
    if(Tk_WindowId(tkwin) == None) {
        Tk_MakeWindowExist(tkwin);
    }
    window = Tk_WindowId(tkwin);

    if(GetWindowSize(interp, window, &tkWidth, &tkHeight) != TCL_OK) {
        Tcl_SetObjResult(interp,
            Tcl_ObjPrintf("can't get window geometry of \"%s\"", Tcl_GetString(objv[1])));
        return TCL_ERROR;
    }
    /* do some sanity checks */
    if (x <= 0 || x >= tkWidth) x = 0;
    if (y <= 0 || y >= tkHeight) y = 0;
    if (width <= 0 || width > tkWidth-x) width = tkWidth-x;
    if (height <= 0 || height > tkHeight-y) height = tkHeight-y;
    if (destWidth <= 0) destWidth = width;
    if (destHeight <= 0) destHeight = height;
    if (width <= 0 || height <= 0) {
        Tcl_SetObjResult(interp,
            Tcl_ObjPrintf("computed size wrong: %dx%d", width, height));
        return TCL_ERROR;
    }
	/* Put result in photo image */
    return RbcSnapPhoto(interp, tkwin, window, x, y, width, height, destWidth,
        destHeight, Tcl_GetString(objv[2]), 1.0);
}

#ifdef WIN32

/*
 *--------------------------------------------------------------
 *
 * GetWindowSize --
 *
 *      TODO: Description
 *
 * Results:
 *      TODO: Results
 *
 * Side effects:
 *      TODO: Side Effects
 *
 *--------------------------------------------------------------
 */
static int
GetWindowSize(
    Tcl_Interp * interp,
    Window window,
    int *widthPtr,
    int *heightPtr)
{
    int result;
    RECT region;
    TkWinWindow *winPtr = (TkWinWindow *) window;

    result = GetWindowRect(winPtr->handle, &region);
    if(result) {
        *widthPtr = region.right - region.left;
        *heightPtr = region.bottom - region.top;
        return TCL_OK;
    }
    return TCL_ERROR;
}
#else

/*
 *----------------------------------------------------------------------
 *
 * XGeometryErrorProc --
 *
 *	Flags errors generated from XGetGeometry calls to the X server.
 *
 * Results:
 *	Always returns 0.
 *
 * Side Effects:
 *	Sets a flag, indicating an error occurred.
 *
 *----------------------------------------------------------------------
 */

/* ARGSUSED */
static int
XGeometryErrorProc(
    clientData,
    errEventPtr)
    ClientData clientData;
    XErrorEvent *errEventPtr;
{
int *errorPtr = clientData;

    *errorPtr = TCL_ERROR;
    return 0;
}

/*
 *--------------------------------------------------------------
 *
 * GetWindowSize --
 *
 *      TODO: Description
 *
 * Results:
 *      TODO: Results
 *
 * Side effects:
 *      TODO: Side Effects
 *
 *--------------------------------------------------------------
 */
static int
GetWindowSize(
    Tcl_Interp * interp,
    Window window,
    int *widthPtr,
    int *heightPtr)
{
    int result;
    int any = -1;
    int x, y, borderWidth, depth;
    Window root;
    Tk_ErrorHandler handler;
    Tk_Window tkwin;

    tkwin = Tk_MainWindow(interp);
    handler = Tk_CreateErrorHandler(Tk_Display(tkwin), any, X_GetGeometry,
        any, XGeometryErrorProc, &result);
    result = XGetGeometry(Tk_Display(tkwin), window, &root, &x, &y,
        (unsigned int *)widthPtr, (unsigned int *)heightPtr,
        (unsigned int *)&borderWidth, (unsigned int *)&depth);
    Tk_DeleteErrorHandler(handler);
    XSync(Tk_Display(tkwin), False);
    if(result) {
        return TCL_OK;
    }
    return TCL_ERROR;
}
#endif

/* vim: set ts=4 sw=4 sts=4 ff=unix et : */
