/***********************************************************************/
/* Open Visualization Data Explorer                                    */
/* (C) Copyright IBM Corp. 1989,1999                                   */
/* ALL RIGHTS RESERVED                                                 */
/* This code licensed under the                                        */
/*    "IBM PUBLIC LICENSE - Open Visualization Data Explorer"          */
/***********************************************************************/

#define String dxString
#define Object dxObject
#define Angle dxAngle
#define Matrix dxMatrix
#define Screen dxScreen
#define Boolean dxBoolean

#include <dxconfig.h>


#include <dx/dx.h>

#undef String
#undef Object
#undef Angle
#undef Matrix
#undef Screen
#undef Boolean

#include <X11/Xlib.h>

#include <math.h>

#include "hwDeclarations.h"
#include "hwWindow.h"
#include "hwStereo.h"


/************** Default Stereo Camera Mode ********************
 * The arguments of the default camera define a asymmetric frustum
 * and ocular separation
 ****************************************************************/

typedef struct
{
    float overlap;
    float separation;
    float aspect;
    float lp[16];
    float rp[16];
} StereoArgs;
    

static void *defInitializeStereoCameraMode0(void *data, dxObject arg);
static int  defExitStereoCameraMode0(void *data);
static int  defCreateStereoCameras0(void *,
					int, float, float,
					float *, float *, float *, float, float,
					float *, float *, float *, float **,
					float *, float *, float *, float **);
static int  defMapStereoXY0(void *, Window, Window, WindowInfo,
					int, int, int*, int*);

static StereoCameraMode _defaultStereoCameraModes[1];

int
DXEntry(int *nModes, StereoCameraMode **scms)
{
    *scms = _defaultStereoCameraModes;

    (*scms)[0].initializeStereoCameraMode = defInitializeStereoCameraMode0;
    (*scms)[0].exitStereoCameraMode       = defExitStereoCameraMode0;
    (*scms)[0].createStereoCameras        = defCreateStereoCameras0;
    (*scms)[0].mapStereoXY 		  = defMapStereoXY0;
    
    *nModes = 1;

    return OK;
}

static void *
defInitializeStereoCameraMode0(void *data, dxObject arg)
{
    if (!data) 
	data = (void *)DXAllocate(sizeof(StereoArgs));
    
    if (!arg || !DXExtractParameter(arg, TYPE_FLOAT, 3, 1, (Pointer)data))
    {
	DXSetError(ERROR_BAD_PARAMETER, "bad args to default stereo camera model");
	goto error;
    }

    return data;

error:
    DXFree((Pointer)data);
    return NULL;
}

static int
defExitStereoCameraMode0(void *data)
{
    DXFree((Pointer)data);
    return OK;
}

static int
defCreateStereoCameras0(void *data,
		    int p, float fov, float width,
		    float *to, float *from, float *up, float n, float f,
		    float *lto, float *lfrom, float *lup, float **lProj,
		    float *rto, float *rfrom, float *rup, float **rProj)
{
    StereoArgs *args = (StereoArgs *)data;
    float vx, vy, vz;
    float ux, uy, uz;
    float ax, ay, az;
    float d;
    int i, j;
    float l, r, b, t;
    float *lp = args->lp;
    float *rp = args->rp;

    vx = from[0] - to[0];
    vy = from[1] - to[1];
    vz = from[2] - to[2];
    d = sqrt(vx*vx + vy*vy + vz*vz);
    vx = vx / d;
    vy = vy / d;
    vz = vz / d;

    ux = up[0];
    uy = up[1];
    uz = up[2];
    d = sqrt(ux*ux + uy*uy + uz*uz);
    ux = ux / d;
    uy = uy / d;
    vz = vz / d;

    ax = uy*vz - uz*vy;
    ay = uz*vx - ux*vz;
    az = ux*vy - uy*vx;

    lfrom[0] = from[0] - args->separation*ax;
    lfrom[1] = from[1] - args->separation*ay;
    lfrom[2] = from[2] - args->separation*az;
    lto[0]   = to[0]   - args->separation*ax;
    lto[1]   = to[1]   - args->separation*ay;
    lto[2]   = to[2]   - args->separation*az;
    lup[0]   = up[0];
    lup[1]   = up[1];
    lup[2]   = up[2];

    rfrom[0] = from[0] + args->separation*ax;
    rfrom[1] = from[1] + args->separation*ay;
    rfrom[2] = from[2] + args->separation*az;
    rto[0]   = to[0]   + args->separation*ax;
    rto[1]   = to[1]   + args->separation*ay;
    rto[2]   = to[2]   + args->separation*az;
    rup[0]   = up[0];
    rup[1]   = up[1];
    rup[2]   = up[2];

    {
	float t = n;
	n = f;
	f = t;
    }

    r = args->overlap;
    l = r - 1;
    t = (0.5 * args->aspect);
    b = -t;

    if (p)
    {
	r *= fov;
	l *= fov;
	t *= fov;
	b *= fov;
    }
    else
    {
	r *= width;
	l *= width;
	t *= width;
	b *= width;
    }

    lp[ 0] = 2/(r-l); lp[ 4] =     0.0; lp[ 8] = (r+l)/(r-l); lp[12] =         0.0;
    lp[ 1] = 0.0;     lp[ 5] = 2/(t-b); lp[ 9] = (t+b)/(t-b); lp[13] =         0.0;
    lp[ 2] = 0.0;     lp[ 6] =     0.0; lp[10] = (f+n)/(f-n); lp[14] = 2*f*n/(f-n);
    lp[ 3] = 0.0;     lp[ 7] =     0.0; lp[11] =        -1.0; lp[15] =         0.0;

    *lProj = lp;

    l = -args->overlap;
    r = l + 1;
    t = (0.5 * args->aspect);
    b = -t;

    if (p)
    {
	r *= fov;
	l *= fov;
	t *= fov;
	b *= fov;
    }
    else
    {
	r *= width;
	l *= width;
	t *= width;
	b *= width;
    }

    rp[ 0] = 2/(r-l); rp[ 4] =     0.0; rp[ 8] = (r+l)/(r-l); rp[12] =         0.0;
    rp[ 1] = 0.0;     rp[ 5] = 2/(t-b); rp[ 9] = (t+b)/(t-b); rp[13] =         0.0;
    rp[ 2] = 0.0;     rp[ 6] =     0.0; rp[10] = (f+n)/(f-n); rp[14] = 2*f*n/(f-n);
    rp[ 3] = 0.0;     rp[ 7] =     0.0; rp[11] =        -1.0; rp[15] =         0.0;

    *rProj = rp;

    return OK;
}

static int
defMapStereoXY0(void *data, Window frame, Window w, WindowInfo wi,
			int xi, int yi, int *xo, int *yo)
{
    *xo = xi;
    *yo = yi;
}
