#include "antiglut.h"
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#ifndef	M_2PI
#define	M_2PI 6.28318530717958
#endif	/* M_2PI */

#define	WWIDTH	512
#define	WHEIGHT	512

#define	NCOLORS 3
static GLfloat rgb[NCOLORS+1][3] =
{
	{1,0,0.5f},
	{0.5f,1,0},
	{0,0.5f,1},
	{.5f,.5f,.5f} /* color of origin */
};
#define	NVERT 12 /* must be multiple of NCOLORS */
static GLfloat vert[NVERT+1][2];

static unsigned char idx = 0;
static GLfloat rotz = 0;

static void draw()
{
	int i;
	glClear(GL_COLOR_BUFFER_BIT);
	glLoadIdentity();
	glScalef(0.7f,0.7f,0.7f);
	glRotatef(rotz,0,0,1);
	/* draw flat - shaded polygon */
	glBegin(GL_TRIANGLE_FAN);
		glColor3fv(rgb[NCOLORS]);
		glVertex2fv(vert[NVERT]);
		for(i = 0;i < NVERT;i++)
		{
			glColor3fv(rgb[(idx + i) % NCOLORS]);
			glVertex2fv(vert[i]);
		}
		glColor3fv(rgb[idx % NCOLORS]);
		glVertex2fv(vert[0]);
	glEnd();
	/* draw points near the vertices of the polygon */
	glPointSize(10);
	glColor4f(1,1,1,0.5f);
	glBegin(GL_POINTS);
		for(i = 0;i < NVERT;i++)
			glVertex2fv(vert[i]);
	glEnd();
	/* draw wireframe polygon */
	glScalef(1.1f,1.1f,1.1f);
	glBegin(GL_LINE_LOOP);
		for(i = 0;i < NVERT;i++)
			glVertex2fv(vert[i]);
	glEnd();
	agSwap();
}

static void handle_key(ui_key_event_p key,unsigned mods)
{
	GLfloat *buffer, *row, *ptr;
	unsigned int r, c;
	static unsigned int outcount = 0;
	FILE *out;
	char outname[256];
	switch(key->scan)
	{
		case sc_wheelup:
		case sc_up:
			if(mods & UI_SHIFT_MASK)
				rotz += (mods & UI_CONTROL_MASK) ? 5 : 1;
			else
				idx--;
			break;
		case sc_wheeldown:
		case sc_down:
			if(mods & UI_SHIFT_MASK)
				rotz -= (mods & UI_CONTROL_MASK) ? 5 : 1;
			else
				idx++;
			break;
		case sc_escape:
		case sc_space:
			rotz = 0;
			idx = 0;
			break;
		case sc_s:
#define		BITS	16
#define		MAXCVAL	((1 << BITS) - 1)
			buffer = (GLfloat*)malloc(3 * WWIDTH * WHEIGHT * sizeof(GLfloat));
			if(!buffer)
				break;
			glReadPixels(0, 0, WWIDTH, WHEIGHT, GL_RGB, GL_FLOAT, buffer);
			sprintf(outname, "shot%03u.ppm", outcount++);
			out = fopen(outname, "wb");
			if(!out)
			{
				free(buffer);
				break;
			}
			fprintf(out, "P3\n");
			fprintf(out, "%u %u\n%u\n", WWIDTH, WHEIGHT, MAXCVAL);
			/* the image was stored bottom - to - top */
			row = buffer + 3 * WWIDTH * WHEIGHT;
			for(r = 0;r < WHEIGHT;++r, row -= 3 * WWIDTH)
			{
				ptr = row;
				for(c = 0;c < WWIDTH;++c, ptr += 3)
					fprintf(out, "%u %u %u ",
						(unsigned int)(MAXCVAL * *ptr),
						(unsigned int)(MAXCVAL * ptr[1]),
						(unsigned int)(MAXCVAL * ptr[2]));
				fputc('\n', out);
			}
			fclose(out);
			free(buffer);
			return; /* nothing changed; don't redraw window */			
		default:
			return; /* nothing changed; don't redraw window */
	}
	draw();
}

#define	GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX          0x9047
#define	GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX    0x9048
#define	GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX  0x9049
#define	GPU_MEMORY_INFO_EVICTION_COUNT_NVX            0x904A
#define	GPU_MEMORY_INFO_EVICTED_MEMORY_NVX            0x904B

int main()
{
	int i;
	ui_event_t	ev;
	char *estart, *es;
	agWindowConfig("Minimalistic demo (use [SHIFT+CTRL]+MWHEELUP/DOWN)",
		WWIDTH,WHEIGHT,GL_FALSE);
	if(!agInit(AG_REQUIRED_1_1,GL_TRUE,GL_FALSE,GL_FALSE))
		return -1;
	printf("Extensions:\n\t");
	es = estart = (char*)glGetString(GL_EXTENSIONS);
	while(*es)
	{
		if(*es != ' ')
			fputc(*es,stdout);
		else
			printf("\n\t");
		es++;
	}
	puts("");
	if(strstr(estart,"GL_NVX_gpu_memory_info"))
	{
		GLint val;
		puts("GL_NVX_gpu_memory_info present. GPU memories:");
		glGetIntegerv(GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, &val);
		printf("GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX\t%i\n", val);
		glGetIntegerv(GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX, &val);
		printf("GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX\t%i\n", val);
		glGetIntegerv(GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &val);
		printf("GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX\t%i\n", val);
		/*
		glGetIntegerv(GPU_MEMORY_INFO_EVICTION_COUNT_NVX, &val);
		printf("GPU_MEMORY_INFO_EVICTION_COUNT_NVX\t%i\n", val);
		glGetIntegerv(GPU_MEMORY_INFO_EVICTED_MEMORY_NVX, &val);
		printf("GPU_MEMORY_INFO_EVICTED_MEMORY_NVX\t%i\n", val);
		*/
	}	
	fflush(stdout);
	/*
	 * After agInit() we can be sure that:
	 *  - viewport is set to match entire window;
	 *  - projection is set to orthogonal;
	 *  - viewing "frustum" is a cube (with edge of 2) centered at origin.
	 */

	/*
	 * We don't use display lists for the sake of clarity;
	 * just compute coords of polygon's vertices.
	 */
	for(i = 0;i < NVERT;i++)
	{
		vert[i][0] = (GLfloat)cos(i * M_2PI / NVERT);
		vert[i][1] = (GLfloat)sin(i * M_2PI / NVERT);
	}
	vert[NVERT][0] = vert[NVERT][1] = 0; /* origin */

	glClearColor(0,0.2f,0.2f,0);
	/*
	 * Draw antialised points and lines
	 */
	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_BLEND);
	glEnable(GL_LINE_SMOOTH);
	glEnable(GL_POINT_SMOOTH);
	glLineWidth(3);
	
	for(;;)
	{
		uiNextEvent(&ev);
		if(ev.type == ui_wm_event && ev.configure.wm_event == ui_wm_close)
			break;
		switch(ev.type)
		{
			case ui_key_event:
				if(ev.key.press)
					handle_key(&ev.key,ev.state);
				break;
			case ui_window_expose_event:
				draw();
				break;
			default:
				break;
		}
	}
	agShutdown();
	return 0;
}
