#define __OPTIMIZE__		1
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <ctype.h>
#include <sys/mman.h>
#include "gltk.h"
#include <Pt.h>
#include <photon/PdGL.h>

#ifndef BOOL
#define BOOL int
#endif

#ifndef TRUE
#define TRUE	1
#endif

#ifndef FALSE
#define FALSE	0
#endif

uint64_t attrib[220];

PdOpenGLContext_t *Cur=NULL;

static struct _WINDOWINFO {
    int x, y;
    int width, height;
    GLenum type;
    int ipfd;
    BOOL bDefPos;
} windInfo = {
    0, 0, 300, 300, TK_INDEX | TK_SINGLE, 0, TRUE
};

static void (*ExposeFunc)(int, int);
static void (*ReshapeFunc)(GLsizei, GLsizei);
static void (*DisplayFunc)(void);
static GLenum (*KeyDownFunc)(int, GLenum);
//static GLenum (*MouseDownFunc)(int, int, GLenum);
//static GLenum (*MouseUpFunc)(int, int, GLenum);
//static GLenum (*MouseMoveFunc)(int, int, GLenum);
static void (*IdleFunc)(void);
static void *buffer;
static void *video;
static unsigned size;
static struct _Ph_ctrl	*channel=NULL;
static struct termios termio_save;
static int termio_saved = 0;
static unsigned video_width = 1024;

typedef struct _PhKeyBoardMap {
unsigned char isdown;
unsigned short tk_key;
}PhKeyBoardMap;

int key;

int PhHandleKeys(PtWidget_t *widget, void *src, PtCallbackInfo_t *cbinfo)
{
	PhKeyEvent_t *KeyData;
	
	KeyData=PhGetData(cbinfo->event);
	if (KeyData->key_flags & (Pk_KF_Key_Down | Pk_KF_Key_Repeat))
	{
		switch (KeyData->key_sym)
		{
			case Pk_Left : key = TK_LEFT; break;
			case Pk_Right : key= TK_RIGHT; break;
			case Pk_Up : key=TK_UP; break;
			case Pk_Down : key = TK_DOWN; break;
			case Pk_s : key = TK_s; break;
			case Pk_Z : key = TK_Z; break;
			case Pk_z : key = TK_z; break;
			case Pk_Escape : key = TK_ESCAPE; break;
			case Pk_Return : key = TK_RETURN; break;
			default : key=KeyData->key_sym; break;
		}
	}
	else
	{
		key=0;	
	}
}

void tkInitPosition(int x, int y, int width, int height) {
	windInfo.bDefPos = FALSE;
	windInfo.x = x ;
	windInfo.y = y ;
	windInfo.width = width;
	windInfo.height = height;

}

void tkInitDisplayMode(GLenum type) 
{
    windInfo.type = type;

/*
	case Pg_IMAGE_PALETTE_BYTE:	OSMESA_COLOR_INDEX;
	case Pg_IMAGE_DIRECT_8888:	OSMESA_ARGB;
	case Pg_IMAGE_DIRECT_888:	OSMESA_RGB;
	case Pg_IMAGE_DIRECT_565:	OSMESA_NONE;
	case Pg_IMAGE_DIRECT_555:	OSMESA_NONE;
*/
}

void tkExposeFunc(void (*Func)(int width, int height)) {
	ExposeFunc = Func;
}

void tkReshapeFunc(void (*Func)(int width, int height)) {
	ReshapeFunc = Func;
}

void tkIdleFunc(void (*Func)(void)) {
	IdleFunc = Func;
}

void tkDisplayFunc(void (*Func)(void)) {
	DisplayFunc = Func;
} 

void tkKeyDownFunc(GLenum (*Func)(int k, GLenum mask)) {
	KeyDownFunc = Func;
}

#define EVENT_SIZE	sizeof(PhEvent_t) + 1000

unsigned char cevent[2048];

void tkExec(void) {
//	int key;
	GLenum mask = 0;
	unsigned char state = 0;
	PhEvent_t *event=&cevent;

    /*
     *  WM_SIZE gets delivered before we get here!
     */

    if(ReshapeFunc) {
    	(*ReshapeFunc)(windInfo.width, windInfo.height);
    }

    while(GL_TRUE) 
    {
        /*
         *  If an idle function was defined, call it
         */

		// Handle Photon Events
		switch (PhEventPeek(event,EVENT_SIZE))
		{
			case Ph_EVENT_MSG :
				PtEventHandler(event);
			break;
			case -1:
				fprintf(stderr,"PhEventPeek failed\n");
			break;
		}

#if 0
		if(KeyDownFunc && tcischars(STDIN_FILENO) > 0) {
	 		key = getchar();
#else
		if (KeyDownFunc)
		{
#endif
			switch(state) 
			{
				case 0:
					if(key == TK_ESCAPE) 
					{
						state = 1;
						key = 0;
					} 
#if 0
					else if(key != TK_RETURN && key != TK_SPACE && !isalnum(key)) 
					{
						fprintf(stderr,"Key Set 0...1\n");
						key = 0;
					}
#endif
				break;
				case 1:
					if(key == '[') 
					{
						state = 2;
						key = 0;
					} 
					else
					{
						state = 0;
						key = TK_ESCAPE;
					}
				break;
			case 2:
				switch(key) 
				{
					case 'A':
						key = TK_UP;
						break;
					case 'B':
						key = TK_DOWN;
						break;
					case 'C':
						key = TK_RIGHT;
						break;
					case 'D':
						key = TK_LEFT;
						break;
					default:
						key = TK_ESCAPE;
						break;
				}
				state = 0;
				break;
			}

			if(key && KeyDownFunc) 
			{
				(*KeyDownFunc)(key, mask);
				key = 0;
			}
		}

       	if(IdleFunc) {
            (*IdleFunc)();
        }
        if(DisplayFunc) {
        	(*DisplayFunc)();
        }
    }
}

PtWidget_t	*window = NULL;
PtArg_t arg[20];
PhDim_t dim;
PtAppContext_t app;
PhPoint_t pos;
static int inited;
PhImage_t phimage;

GLenum tkInitWindow(char *title) 
{
	PhPoint_t pos = {0,0};
	int n=0;

	dim.w=windInfo.width;
	dim.h=windInfo.height;
#if 0
	if (!(channel = PhAttach(0,0)))
	{
		return GL_FALSE;
	}
#else
	if (window==NULL)
	{
		PtInit(0);
	}
#endif

	if (windInfo.type & TK_DEPTH)
	{
		attrib[n++]=PHOGL_ATTRIB_DEPTH_BITS;
		attrib[n++]=16;
	}
	if (windInfo.type & TK_DIRECT)
	{
		attrib[n++]= PHOGL_ATTRIB_FULLSCREEN;
		attrib[n++]= PHOGL_ATTRIB_DIRECT;
		attrib[n++]= PHOGL_ATTRIB_FULLSCREEN_BEST;
		attrib[n++]= PHOGL_ATTRIB_FULLSCREEN_CENTER;
	}
	attrib[n]=PHOGL_ATTRIB_NONE;
	
	if (Cur)
	{
		PhDCSetCurrent(NULL);
		PhDCRelease(Cur);
		Cur=NULL;
	}

	if((Cur = PdCreateOpenGLContext (2, &dim, 0, &attrib)) == 0) 
	{
		return GL_FALSE;
	}

	PhDCSetCurrent(Cur);

	n=0;
	if (windInfo.type & TK_DIRECT)
	{
		PtSetArg(&arg[n++], Pt_ARG_WINDOW_RENDER_FLAGS,Pt_FALSE,~0);
	    PtSetArg(&arg[n++], Pt_ARG_WINDOW_MANAGED_FLAGS,Pt_TRUE,
                              Ph_WM_FFRONT |
                              Ph_WM_CLOSE |
                              Ph_WM_TOFRONT |
                              Ph_WM_CONSWITCH);

		PtSetArg(&arg[n++], Pt_ARG_WINDOW_STATE, Pt_TRUE, Ph_WM_STATE_ISFRONT |
                    Ph_WM_STATE_ISFOCUS);
	}
	PtSetArg(&arg[n++], Pt_ARG_WINDOW_TITLE,title,0);
	PtSetArg(&arg[n++], Pt_ARG_DIM, &dim,0);
	PtSetArg(&arg[n++], Pt_ARG_RESIZE_FLAGS, Pt_FALSE, Pt_RESIZE_XY_AS_REQUIRED);
	PtSetArg(&arg[n++], Pt_ARG_FILL_COLOR, Pg_BLACK, 0);
	PtSetArg(&arg[n++], Pt_ARG_POS, &pos,0);

	if (window!=NULL)
	{
		PtUnrealizeWidget(window);
		PtDestroyWidget (window);	
	}

	window=PtCreateWidget(PtWindow,NULL, n-1, arg);

	PtRealizeWidget(window);
	PtAddEventHandler(window, Ph_EV_KEY, &PhHandleKeys, NULL);

	if (windInfo.type & TK_DIRECT)
	{
    	PhRegion_t region_info;

        region_info.cursor_type = Ph_CURSOR_NONE;
        region_info.rid = PtWidgetRid(window);
		PhRegionChange (Ph_REGION_CURSOR, 0, &region_info, NULL, NULL);
	}
	
	PtFlush();

/*
	if (ReshapeFunc)
		*ReshapeFunc(windInfo.width, windInfo.height); */

	return GL_TRUE;
}

void tkSwapBuffers(void) 
{
	PgSetRegion(PtWidgetRid(window));
	PdOpenGLContextSwapBuffers(Cur);
}

void tkQuit(void) 
{
	PtDestroyWidget( window );
	PhDCSetCurrent(NULL);
	PhDCRelease( Cur );
	exit(0);
}
