/*
 * c3d.h --
 *
 *     Main internal header file for canvas3d widget code.
 *
 *---------------------------------------------------------------------------
 *
 */

#ifndef __CANVAS3D_H__
#define __CANVAS3D_H__

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

#if (TK_MAJOR_VERSION >= 8 && TK_MINOR_VERSION >= 6)
# define UNICODE
# define _UNICODE
#endif

#ifdef MAC_OSX_TK
# include <OpenGL/gl.h>
# if (TK_MAJOR_VERSION >= 8 && TK_MINOR_VERSION >= 5)
#  define CAN3D_NSOPENGL
#  include <OpenGL/OpenGL.h>
#  include <AppKit/NSOpenGL.h>	/* Use NSOpenGLContext */
#  include <AppKit/NSView.h>	/* Use NSView */
#  include <AppKit/NSOpenGLView.h>	/* Use NSView setWantsBestResolutionOpenGLSurface */
#  include <AppKit/NSEvent.h>	/* Use NSEvent */
#  include <AppKit/NSTouch.h>	/* Use NSTouch */
#  include <Foundation/Foundation.h>	/* Use NSRect */
#  include <tkMacOSXInt.h>      /* Use MacDrawable */
#  include <ApplicationServices/ApplicationServices.h>
#  define Togl_MacOSXGetDrawablePort(togl) TkMacOSXGetDrawablePort((Drawable) ((TkWindow *) togl->TkWin)->privatePtr)
#  include <Availability.h>
# else
#  define CAN3D_AGL
#  define Cursor QDCursor
#  include <AGL/agl.h>
#  undef Cursor
#  include <tkMacOSXInt.h>      /* for MacDrawable */
#  include <ApplicationServices/ApplicationServices.h>
#  define Togl_MacOSXGetDrawablePort(togl) TkMacOSXGetDrawablePort((Drawable) ((TkWindow *) togl->TkWin)->privatePtr)
# endif
#elif defined(_WIN32) && !defined(PLATFORM_SDL)
# include <windows.h>
# undef CAN3D_WGL
# undef CAN3D_X11
# undef CAN3D_SDL
# define CAN3D_WGL
# include "tkWinInt.h"
# include <shellapi.h>
# include <GL/gl.h>
#elif defined(CAN3D_SDL) || defined(PLATFORM_SDL)
# undef CAN3D_X11
# undef CAN3D_GLX
# undef CAN3D_SDL
# define CAN3D_SDL 1
# ifdef _WIN32
#  include <windows.h>
# endif
# include "tkInt.h"
# include "SdlTk.h"
# ifndef USE_TK_STUBS
#  undef _TKINTXLIBDECLS
#  include "SdlTkInt.h"
#  define _TKINTXLIBDECLS
# endif
# include <GL/gl.h>
#else
# define CAN3D_X11
# define CAN3D_GLX
# include <GL/glx.h>
# include <GL/gl.h>
#endif

#define CANVAS3D_VERSION "1.2.2"

/* Structures available to all source code files. */
typedef struct C3dWidget C3dWidget;
typedef struct C3dItem C3dItem;
typedef struct C3dWidgetOptions C3dWidgetOptions;
typedef struct C3dVertex C3dVertex;
typedef struct C3dItemType C3dItemType;
typedef struct C3dColor C3dColor;
typedef struct C3dStatistics C3dStatistics;
typedef struct C3dOverlayLink C3dOverlayLink;
typedef struct C3dTexture C3dTexture;
typedef struct C3dTextureCoords C3dTextureCoords;

/* Structures manipulated by a single source file. */
typedef struct C3dTag C3dTag;                    /* c3dsearch.c    */
typedef struct C3dTransform C3dTransform;        /* c3dtransform.c */
typedef struct C3dDrawCallback C3dDrawCallback;  /* c3ditem.c */

/*
 * An instance of the following structure is used to store a texture.
 *
 * The same format is used for all textures. The aTexels variable is set to
 * point at an array of dimensions C3dTexture.aTexels[iHeight][iWidth][4].
 * Textures are allocated and deallocated by code for the special texture
 * option type in c3doptions.c.
 */
struct C3dTexture {
    Tcl_Obj *pObj;
    int iWidth;
    int iHeight;
    GLubyte *aTexels;
};

struct C3dTextureCoords {
    Tcl_Obj *pObj;
    int nCoords;
    float *aCoords;
};

/*
 * Each item managed by the canvas3d widget has an item-type, represented
 * by an instance of the following structure. A single structure instance
 * is shared between all items of a single type.
 *
 * Each item type has it's own source file (i.e. c3dpolygon.c).
 * Additionally, some code common to more than one item type is in
 * c3ditem.c. Code outside of item files or c3ditem.c should use the public
 * interface to c3ditem.c instead of manipulating this structure directly.
 *
 * Variable C3dItemType.zType stores the item type name (i.e. "polygon").
 *
 * The C3dItemType.xCreate function is used to  allocate and initialise a
 * new item instance. The arguments are a pointer to the widget that the
 * new item will be managed by, and a Tcl list containing the coordinates
 * passed by the user to [$win create]. The return value should be a fully
 * initialised C3dItem structure (fully initialised except for the
 * C3dItem.id variable, which is set by the caller).
 *
 * The xDelete function frees resources allocated by xCreate. If it is
 * NULL, then ckfree() is called on the item pointer to deallocate it.
 *
 * The C3dItemType.xDraw function invokes OpenGL functions to draw the
 * primitive to the widget window.
 *
 * The C3dItemType.xOptions and xTable functions return a record-pointer
 * and Tk option-table for use with the Tk_SetOptions() and
 * Tk_GetOptionValue() functions to implement the [$win itemcget] and
 * [$win itemconfigure] commands.
 */
struct C3dItemType {
    const char *zType;
    C3dOverlayLink *(*xOverlay)(C3dItem *);
    C3dItem *       (*xCreate)(C3dWidget *, Tcl_Obj *);
    C3dItem *       (*xMultiCreate)(C3dWidget *, int, Tcl_Obj **);
    void            (*xDelete)(C3dItem *);
    void            (*xDraw)(C3dWidget *, C3dItem *);
    Tk_OptionTable  (*xTable)(Tcl_Interp *);
    void            (*xStatistics)(C3dItem *, C3dStatistics *);
    void            (*xCoords)(C3dItem *, Tcl_Obj *);
    double          (*xSegment)(C3dItem *, int x, int y);
};

/*
 * For overlay items, the C3dItemType.xOverlay function returns a pointer
 * to an instance of the following structure allocated by item code along
 * with the item. It is used to link the overlay items into a list so they
 * can be drawn after the 3d items.
 */
struct C3dOverlayLink {
    C3dOverlayLink *pNext;
    C3dOverlayLink *pPrev;
    C3dItem *pItem;
};

/*
 * This structure is used to accumulate the return values of the
 * [pathName statistics] command.
 */
struct C3dStatistics {
    int nVertex;
    int nFace;
};

/*
 * Colors are represented by an instance of the following structure.
 */
struct C3dColor {
    Tcl_Obj *pObj;
    GLfloat aChannel[4];
};

struct C3dVertex {
    float x;
    float y;
    float z;
};

/*
 * The following definitions are for use with the  the C3dItem.flags
 * bitmask.
 */
#define ITEMFLAG_NORMALS_VALID      0x00000001
#define ITEMFLAG_SAVEDOPTIONS_VALID 0x00000002
#define ITEMFLAG_CACHE_VALID        0x00000004

#define ITEM_OPTION(t, n, d, c) {                                         \
    TK_OPTION_ ## t, "-" #n, 0, 0, d, -1, Tk_Offset(C3dItem, n), 0, c, 0  \
}


/*
 * This struct is passed as the clientData from widgetCommandAddtag() to
 * addtagSearchCb(), and between widgetCommandDtag() and dtagSearchCb().
 */
struct AddTagContext {
    C3dWidget *pCanvas;
    int nTag;
    Tcl_Obj *const *aTag;
};

/*
 * This struct is passed as the clientData from widgetCommandItemcget() to
 * itemcgetSearchCb().
 */
struct ItemCgetContext {
    C3dWidget *pCanvas;
    Tcl_Obj *pObj;
};

/*
 * This struct is passed as the clientData from
 * widgetCommandItemconfigure() to itemconfigureSearchCb().
 */
struct ItemConfigureContext {
    C3dWidget *pCanvas;        /* Canvas widget */

    int objc;                  /* Number of option/value arguments */
    Tcl_Obj * const * objv;    /* Option/value arguments passed to itemconfig */

    int nItem;
    int nItemAlloc;
    C3dItem **apItem;
};

struct BboxContext {
    C3dWidget *pCanvas;
    int width;
    int height;
    C3dTransform *pProjection;
    int valid;
    int xmin, xmax;
    int ymin, ymax;
};

struct FindSearchContext {
    C3dTransform *pTransform;
    int nAlloc;
    int nItems;
    struct _findsearchitem {
        float z;
        int id;
    } *aItems;
};

struct C3dItem {
    C3dWidget *pCanvas;      /* Must be first! */
    C3dItemType *pType;
    int id;
    int nVertex;
    C3dVertex *aVertex;
    C3dTag *pTagList;
    Tk_SavedOptions savedOptions;
    int flags;               /* Combination of ITEMFLAG_xxx values */
    int hidden;              /* Current (boolean) value of the -hidden option */
    C3dItem *pNextNotHidden; /* Next non-hidden item */
};

struct C3dWidgetOptions {
    double closeenough;             /* Pixels of tolerance in Select mode */
    int width;                      /* Width in pixels of window */
    int height;                     /* Height in pixels of window */
    int enablealpha;                /* True to enable GL_BLEND mode */
    C3dColor background;            /* Background color for widget */
    double visibleangle;            /* Vertical field of view in degrees */
    C3dVertex *cameralocation;      /* Location of camera */
    C3dVertex *cameracenter;        /* Scene center */
    C3dVertex *cameraup;            /* Viewport "up" vector */
    Tk_Cursor cursor;               /* Cursor shape for the window */
    int saveunder;                  /* True to render to a pixmap first */
};

#define CANVAS3D_MAX_LIGHTS 8

/*
 * State information for each widget instance is stored in a structure of
 * the following type. A pointer to this structure is passed as the
 * 'clientData' to the widget command.
 *
 * Variable C3dWidget.optionTable stores a Tk_OptionTable configured with
 * the widget options. The option values themselves are stored in
 * C3dWidget.options.
 *
 * Item Allocation/Storage
 *
 *     Each item added to a canvas3d widget is represented by an instance
 *     of the C3dItem structure, allocated using ckalloc(). If the
 *     item includes a list of vertices and/or normals, then space for
 *     these is allocated as part of the same ckalloc() call. So to free
 *     any type of item only a single call to ckfree() is required.
 *
 *     A pointer to each allocated item is stored in the
 *     C3dWidget.aItemsById hash table, indexed by numeric id (a copy of
 *     the corresponding C3dItem.id variable). To iterate through all
 *     items, iterate through the entries C3dWidget.aItemsById array.
 *
 *     If the item is a light source, then a pointer to it is also stored
 *     in the C3dWidget.aLight array. This is because light sources must be
 *     added to an Open-GL scene before any other primitives are added.
 *
 * Light items
 *
 *     Light items are handled differently from other canvas3d items
 *     because of two restrictions mandated by OpenGL:
 *
 *         (a) There may be a maximum of 8 light sources, named GL_LIGHT0
 *             through GL_LIGHT7.
 *         (b) Light sources must be specified before any OpenGL primitives
 *             that are to be illuminated by the light source.
 *
 *     To accomadate the above, pointers to light items are stored in the
 *     C3dWidget.aLight array (as well as in the aItemsById hash table).
 *     The first C3dWidget.nLight entries in the array are valid.
 *     C3dWidget.aLight[0] corresponds to OpenGL light source GL_LIGHT0,
 *     C3dWidget.aLight[1] is GL_LIGHT1, and so on.
 *
 *     Manipulation of the C3dWidget.aLight and C3dWidget.nLight variables
 *     is done in c3dlight.c, by the light item C3dItemType callbacks.
 *
 */
struct C3dWidget {
    Tk_OptionTable optionTable;       /* Option table for widget */
    C3dWidgetOptions options;         /* Structure to store configuration */
    Tk_ClassProcs *pClassProcs;       /* Structure to store class callbacks */

    Tcl_Interp *interp;               /* Interpreter widget is created in */
    Tcl_Command widgetCmd;            /* Token for canvas's widget command. */
    Tk_Window tkwin;                  /* Tk widget window */
    Display *display;		/* Display containing widget; needed, among
				 * other things, to release resources after
				 * tkwin has already gone away. */
#if defined(CAN3D_WGL)
    HDC hDC;                          /* Device drawing context */
    HGLRC context;                    /* OpenGL context */

    HDC hpixDC;                       /* Device pixmap drawing context */
    HGLRC pixcontext;                 /* OpenGL pixmap context */
    HBITMAP pixmap3d;                 /* DIB with 3d scene cache */
#elif defined(CAN3D_NSOPENGL)
    NSOpenGLPixelBuffer *pbuf;
    Pixmap pixmap3d;                  /* Pixmap with cache of 3d scene only. */
    NSOpenGLView *context;
#elif defined(CAN3D_AGL)
    AGLContext context;               /* AGL drawing context */
    Pixmap pixmap3d;                  /* Pixmap with cache of 3d scene only. */
#elif defined(CAN3D_SDL)
    void *context;                    /* GL drawing context */
#else
    GLXContext context;               /* GLX drawing context */
    GLXContext pixcontext;            /* GLX pixmap drawing context */
    GLXPixmap glxpixmap;              /* GLX pixmap wrapper for pixmap */
    Pixmap pixmap3d;                  /* Pixmap with cache of 3d scene only. */
#endif
    Pixmap pixmap;                    /* Pixmap to render to if -saveunder */
    int pixmapwidth;                  /* Height of C3dWidget.pixmap */
    int pixmapheight;                 /* Width of C3dWidget.pixmap */
    int eState;                       /* State of the widget */

    int eSelectMode;                  /* True if drawing in GL_SELECT mode */
    float fSelectX1;                  /* Projected X coord of select point */
    float fSelectY1;                  /* Projected Y coord of select point */
    float fSelectX2;
    float fSelectY2;
    int nSelectBufferSize;

    /* Cache of non-hidden items */
    C3dItem *pNotHidden;              /* List of unhidden items */
    int validNotHiddenList;           /* True if the list is valid */

    int nLight;                            /* Number of light items */
    C3dItem *aLight[CANVAS3D_MAX_LIGHTS];  /* Array of light items */

    int iNextItem;                    /* Id for next item created */
    Tcl_HashTable aItemsById;         /* Canvas items indexed by id */
    Tcl_HashTable aItemsByTag;        /* Canvas items indexed by tag */

    int nDrawCallbackAlloc;
    int nDrawCallback;
    C3dDrawCallback *aDrawCallback;

    C3dOverlayLink *pOverlay;         /* Linked list of overlay items */
};

/*
 * The following are valid values for the C3dWidget.eState variable. The
 * widget is initially in state STATE_NONE.
 */
#define STATE_NONE                  0
#define STATE_REDRAW_WINDOW         1
#define STATE_REDRAW_OVERLAY        2
#define STATE_REDRAW_ALL            3

/*
 * If NDEBUG is not defined, use various wrappers around functions that
 * dynamically allocate and deallocate resources to help track down
 * resource leaks and so on. All widget code should use these wrappers
 * instead of the underlying functions.
 */
#ifndef NDEBUG
char *C3dAlloc(int);
void C3dFree(char *);
char *C3dRealloc(char *, int);

void C3dIncrRefCount(Tcl_Obj *);
void C3dDecrRefCount(Tcl_Obj *);

GC C3dGetGC(Tk_Window, unsigned long, XGCValues*);
void C3dFreeGC(Display*, GC);

Pixmap C3dGetPixmap(Display *, Drawable, int, int, int);
void C3dFreePixmap(Display*, Pixmap);

void C3dFreeColor(XColor *);
XColor *C3dAllocColorFromObj(Tcl_Interp *, Tk_Window, Tcl_Obj *);
XColor *C3dGetColorByValue(Tk_Window, XColor *);

Tcl_ObjCmdProc C3dAllocCommand;
#else

#define C3dAlloc(x)                 ckalloc(x)
#define C3dFree(x)                  ckfree(x)
#define C3dRealloc(x,y)             ckrealloc(x,y)
#define C3dIncrRefCount(x)          Tcl_IncrRefCount(x)
#define C3dDecrRefCount(x)          Tcl_DecrRefCount(x)
#define C3dGetGC(x,y,z)             Tk_GetGC(x,y,z)
#define C3dFreeGC(x,y)              Tk_FreeGC(x,y)
#define C3dGetPixmap(v,w,x,y,z)     Tk_GetPixmap(v,w,x,y,z)
#define C3dFreePixmap(x,y)          Tk_FreePixmap(x,y)
#define C3dFreeColor(x)             Tk_FreeColor(x)
#define C3dAllocColorFromObj(x,y,z) Tk_AllocColorFromObj(x,y,z)
#define C3dGetColorByValue(x,y)     Tk_GetColorByValue(x,y)

#endif

/*
 * The public interface to code in c3dmain.c.
 */
void C3dDrawWhenIdle(C3dWidget *, int);
void C3d_Canvas_DrawNow(C3dWidget *);

/*
 * A combination of the following flags may be passed as the third argument
 * to C3dSearch().
 */
#define SEARCHFLAG_ONEONLY                   0x01
#define SEARCHFLAG_MODIFIES_TAGS             0x02

/*
 * The public interface to code in c3dsearch.c.
 */
typedef int (*search_callback)(ClientData, C3dItem *);
int C3dSearch(C3dWidget *, Tcl_Obj *, int, search_callback, ClientData);
int C3dAddTagToItem(C3dWidget *, Tcl_Obj *, C3dItem *pItem);
int C3dRemoveTagFromItem(C3dWidget *, Tcl_Obj *, C3dItem *pItem);
int C3dGetTags(C3dItem *pItem, Tcl_Obj *);

/*
 * The public interface to code in c3dtransform.c.
 */
void C3dTransformApply(C3dTransform *, C3dVertex *);
void C3dTransformDelete(C3dTransform *);
C3dTransform *C3dTransformCompile(Tcl_Interp *, C3dWidget *, Tcl_Obj *);
void C3dCross(float, float, float, float, float, float, float*, float*, float*);
float C3dDot(float, float, float, float, float, float);
void C3dNormalize(float*, float*, float*, float*);
float C3dMagnitude(float, float, float);
C3dTransform *C3dTransformProjection(C3dWidget *);
C3dTransform *C3dTransformOverview(C3dWidget *);
void C3dTransformToMatrix(C3dTransform *, GLdouble *);

/*
 * The public interface to code in c3dcamera.c
 */
int C3dBoundingSphere(C3dWidget*, Tcl_Obj*, int*, float*,float*,float*,float*);
void C3dDrawCamera(C3dWidget *);
int C3dLookat(C3dWidget *, Tcl_Obj *);
int C3dCameraTransform(C3dWidget *, C3dTransform *);

/*
 * The public interface to code in c3ditem.c.
 */
C3dItem *C3dItemCreate(C3dWidget *, int, Tcl_Obj **, C3dItem *);
int C3dItemCget(C3dWidget *, C3dItem *, Tcl_Obj *);
int C3dItemConfigureInfo(C3dWidget *, C3dItem *, Tcl_Obj *);
void C3dItemDelete(C3dItem *);
void C3dItemDraw(C3dWidget *, C3dItem *);
void C3dItemDrawOverlay(C3dWidget *);
void C3dItemGetType(C3dItem *, Tcl_Obj *);
int C3dItemConfigure(C3dWidget *, C3dItem *, int, Tcl_Obj *const[]);
void C3dItemConfigureRollback(C3dItem *);
void C3dItemConfigureCommit(C3dItem *);
int C3dItemProjection(C3dItem *, C3dTransform *, float, float);
float C3dItemDepth(C3dTransform *, C3dItem *);
void C3dItemStatistics(C3dItem *, C3dStatistics *);
void C3dItemCoords(C3dItem *, Tcl_Obj *);
int C3dGetCoords(int, Tcl_Obj**, int*, Tcl_Obj***);

/*
 * Functions implemented in c3ditem.c that are used by the item-type files.
 */
int C3dCheckCoords(Tcl_Interp *, Tcl_Obj *, int, int, int *);
int C3dSetCoords(Tcl_Interp *, Tcl_Obj *, int, C3dItem *);
void C3dItemDrawCallback(
C3dWidget *, void (*)(C3dItem *, ClientData), C3dItem *, ClientData, float);
void C3dItemDrawDoCallbacks(C3dWidget *);

/*
 * Functions in c3dlight.c. As well as the implementation of the "light"
 * item, c3dlight.c contains the following function, used to position all
 * light sources before drawing any three dimensional geometric primitives.
 */
int C3dDrawLights(C3dWidget *);

/* c3dpolygon.c */
int C3dPolygonSmooth(C3dWidget *, Tcl_Obj *);

/*
 * The following functions are implemented in the various item files. Each
 * returns a pointer to the C3dItemType structure for the corresponding
 * item type.
 */
C3dItemType * C3dPolygon();
C3dItemType * C3dLight();
C3dItemType * C3d2dLine();
C3dItemType * C3d2dPolygon();
C3dItemType * C3dText();
C3dItemType * C3dLine();
C3dItemType * C3dPoint();

/*
 * The macros TAGS_OPTION and HIDDEN_OPTION are defined to the contents of
 * Tk_OptionSpec structures that may be passed as part of an array to
 * C3dCreateOptionTable() to implement the -tags and -hidden item options.
 *
 * It is assumed that the record-pointer passed to Tk_SetOptions() & co. is
 * a pointer to a C3dItem structure.
 */
#define C3DOPTION(s, t, n, d, c) {                           \
    TK_OPTION_ ## t, "-" #n, 0, 0, d, -1,                    \
    Tk_Offset(s, n), 0, c, 0                                 \
}
#define TAGS_OPTION   {0, "-tags", 0, 0, "", 0, 0, 0, 0, 0}
#define HIDDEN_OPTION C3DOPTION(C3dItem, BOOLEAN, hidden, "false", 0)

Tk_OptionTable C3dCreateOptionTable(Tcl_Interp *, Tk_OptionSpec *);

/*
 * These can be handy.
 */
#ifndef MAX
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif
#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
#endif

#endif


