/**************************************
*                                     *
*   Jeff Molofee's Basecode Example   *
*          nehe.gamedev.net           *
*                2001                 *
*                                     *
**************************************/

#include <windows.h>											// Header File For Windows
#include <gl\gl.h>												// Header File For The OpenGL32 Library
#include <gl\glu.h>												// Header File For The GLu32 Library
#include <gl\glaux.h>											// Header File For The GLaux Library
#include <stdio.h>
#include <math.h>
#include "spcc-w.h"												// Header File For NeHeGL

#include "HPTime.h"
#include "som/som.h"
#include "SOMPCompress.h"

#pragma comment( lib, "opengl32.lib" )							// Search For OpenGL32.lib While Linking
#pragma comment( lib, "glu32.lib" )								// Search For GLu32.lib While Linking
#pragma comment( lib, "glaux.lib" )								// Search For GLaux.lib While Linking

#ifndef CDS_FULLSCREEN											// CDS_FULLSCREEN Is Not Defined By Some
#define CDS_FULLSCREEN 4										// Compilers. By Defining It This Way,
#endif				

//#define _DECOMPRESS

// We Can Avoid Errors
GL_Window*	g_window;
Keys*		g_keys;

GLenum g_SOMDrawMode = GL_LINE_STRIP;

int iMarkY=0,iMarkX=0;
int iMarkN = 0;

#define _MINDISTF .8f

nVec_var dt;
static long g_lFrameCount = 0;
static float g_fPreviewTime = 0;

GLuint texture[4];
unsigned char data[512][512][3];
unsigned char difference[512][512][3];
unsigned char normaldifference[512][512][3];

unsigned char weights[512][512][3];

CSOMPCompress g_SOMPCompress;

#define _MAXfErrorLines 500
#define _MAXPREVIEWSIZE 6

int GL_Window::saveBMP(int ixSize,int iySize,char *szExt,unsigned char *ucpData){
	char current_screenie_path[80];
	int bmp_width;
	int bmp_height,x;

	sprintf( current_screenie_path, "shots/shot-%s - %li.bmp", szExt,long(g_HPTime.getTime() & 0xffffffff));

	bmp_width = ixSize;
	bmp_height = iySize;

	GLubyte *bitmap_bits = new GLubyte[ ( bmp_width * bmp_height ) * 3 ];

	BITMAPFILEHEADER header;
	BITMAPINFOHEADER info;

	for(x=0; x < ixSize*iySize * 3; x+=3){
		// rgb to bgr
		bitmap_bits[x+0] = ucpData[x+2];
		bitmap_bits[x+1] = ucpData[x+1];
		bitmap_bits[x+2] = ucpData[x+0];
	}

	header.bfType = 'MB';
	header.bfOffBits = sizeof( header ) + sizeof( info );
	header.bfSize = sizeof( header ) + sizeof( info ) + ( bmp_width * bmp_height * 3 );
	header.bfReserved1 = 0;
	header.bfReserved2 = 0;

	info.biBitCount = 24;
	info.biClrImportant = 0;
	info.biClrUsed = 0;
	info.biCompression = 0;
	info.biHeight = bmp_height;
	info.biWidth = bmp_width;
	info.biPlanes = 1;
	info.biSize = sizeof( info );
	info.biSizeImage = bmp_width * bmp_height * 3;
	info.biXPelsPerMeter = 2952;
	info.biYPelsPerMeter = 2952;

	OFSTRUCT file_data;
	int file_handle;

	if( ( file_handle = OpenFile( current_screenie_path, &file_data, OF_WRITE | OF_CREATE ) ) == -1 )
	{
		//Error( "ERROR: cannot create new screenshot file." );
		return -1;
	}

	// write data to bitmap
	_lwrite( file_handle, (char*)&header, sizeof( header ) );
	_lwrite( file_handle, (char*)&info, sizeof( info ) );
	_lwrite( file_handle, (char*)bitmap_bits, bmp_width * bmp_height * 3 );

	_lclose( file_handle );

	delete [] bitmap_bits;

	// now update current_screenie_path for the next time this function is called
	//screenie_number++;
	//sprintf( current_screenie_path, "screenshots/shot%d.bmp", screenie_number );
	//last_screenie_time = GetTickCount();
	//LogInit( "Screenshot taken.\n" );

	return 1;
}

AUX_RGBImageRec *LoadBMP(char *Filename)				// Loads A Bitmap Image
{
	FILE *File=NULL;									// File Handle

	if (!Filename)										// Make Sure A Filename Was Given
	{
		return NULL;									// If Not Return NULL
	}

	File=fopen(Filename,"r");							// Check To See If The File Exists

	if (File)											// Does The File Exist?
	{
		fclose(File);									// Close The Handle
		return auxDIBImageLoad(Filename);				// Load The Bitmap And Return A Pointer
	}

	return NULL;										// If Load Failed Return NULL
}

void GL_Window::loadTextures(void){
	/*AUX_RGBImageRec *TextureImage[1];					// Create Storage Space For The Texture

	memset(TextureImage,0,sizeof(void *)*1);           	// Set The Pointer To NULL

	// Load The Bitmap, Check For Errors, If Bitmap's Not Found Quit
	if (TextureImage[0]=LoadBMP("pic03.bmp")){
	// Typical Texture Generation Using Data From The Bitmap
	glBindTexture(GL_TEXTURE_2D, texture[0]);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
	memcpy(origdata,TextureImage[0]->data,TextureImage[0]->sizeX * TextureImage[0]->sizeY * 3);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

	free (TextureImage[0]->data);
	}*/
}

BOOL GL_Window::Initialize (){					// Any GL Init Code & User Initialiazation Goes Here
	g_window	= this;
	g_keys		= this->keys;

	m_bRecalcBitmaps = true;
	m_bShowPattern = true;
	m_bShowAna = true;
	m_bShowSOM = true;
	m_bShowSOMExt = false;

	glClearColor (0.5f, 0.5f, 0.5f, 0.5f);						// Black Background
	glClearDepth (1.0f);										// Depth Buffer Setup
	glDepthFunc (GL_LEQUAL);									// The Type Of Depth Testing (Less Or Equal)
	glEnable (GL_DEPTH_TEST);									// Enable Depth Testing
	glEnable (GL_TEXTURE_2D);
	glShadeModel (GL_SMOOTH);									// Select Smooth Shading
	glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);			// Set Perspective Calculations To Most Accurate

	/* load texture */

	glGenTextures(4, &texture[0]);					// Create The Texture

#ifdef _DECOMPRESS
	g_SOMPCompress.loadCPicture("test.txt");
#else
	g_SOMPCompress.loadBMP("pic03.bmp");
#endif

	/* ----------------------------- */
	{
		// drawing 2dim picture of greyscale distribution of neighboring pictures
		SOMPattern pattern(2);
		qSOM2d som;
		som.SetSize(2,16,1);
		som.InitWeights(Init_Random);
		unsigned char ppC[256][256];
		int i1,i2;
		long lx,ly,lox,loy;
		float cd[2];

		pattern.SetSOM(&som);
		pattern.SetMaxPatternNum(256*256);
		for(i1=0; i1<256;i1++){for(i2=0; i2<256;i2++){ppC[i1][i2]=0;}}
		for(i1=0; i1 < 512; i1++){
			for(i2=0; i2 < 512; i2+=2){
				cd[0] = ((int)g_SOMPCompress.image_bmp->data[3*i1*g_SOMPCompress.image_bmp->sizeY+i2*3]+(int)g_SOMPCompress.image_bmp->data[3*i1*g_SOMPCompress.image_bmp->sizeY+i2*3+1]+(int)g_SOMPCompress.image_bmp->data[3*i1*g_SOMPCompress.image_bmp->sizeY+i2*3+2])/3;
				cd[1] = ((int)g_SOMPCompress.image_bmp->data[3*i1*g_SOMPCompress.image_bmp->sizeY+i2*3+3]+(int)g_SOMPCompress.image_bmp->data[3*i1*g_SOMPCompress.image_bmp->sizeY+i2*3+4]+(int)g_SOMPCompress.image_bmp->data[3*i1*g_SOMPCompress.image_bmp->sizeY+i2*3+5])/3;

				ppC[(int)cd[0]][(int)cd[1]] ++;

				pattern.AddPattern(cd);
			}
		}
		som.SetDistP(16);
		som.SetLRate(.025);
		/*while(som.GetDistP()>0.8){
			pattern.TeachEpoch(som.GetLRate(),som.GetDistP());
			som.SetDistP(0.8 * som.GetDistP());
		}
		for(i1=0; i1<256;i1++){
			for(i2=0; i2<256;i2++){
				cd[0] = i1;
				cd[1] = i2;
				som.SetInput(cd);
				som.GetWinner(lx,ly);
				if(i1 && i2){
					if(lx != lox || ly != loy){
						ppC[i1][i2] = 255;
					}
				}
				lox = lx;
				loy = ly;
			}
		}
		for(i2=0; i2<5;i2++){
			for(i1=0; i1<256;i1++){
				ppC[i1][i2] = i1;
			}
		}
		for(i2=0; i2<256;i2++){
			for(i1=0; i1<256;i1++){
				cd[0] = i1;
				cd[1] = i2;
				som.SetInput(cd);
				som.GetWinner(lx,ly);
				if(i1 && i2){
					if(lx != lox || ly != loy){
						ppC[i1][i2] = 255;
					}
				}
				lox = lx;
				loy = ly;
			}
		}
		for(i1=0; i1 <16; i1++){
			for(i2=0; i2 <1; i2++){
				ppC[(int)som.GetNWeights(i1,i2)[0]][(int)som.GetNWeights(i1,i2)[1]] = 255;
			}
		}*/
		FILE *fhd;
		fhd = fopen("text.raw","wb");
		for(i1=0; i1<256;i1++){for(i2=0; i2<256;i2++){
			fwrite(&(ppC[i1][i2]),1,1,fhd);
		}}
		fclose(fhd);
	}
	/* ----------------------------- */

	// init compression
	g_SOMPCompress.m_dDistPFactor = .85;
	g_SOMPCompress.m_fRetrainFactor = 1.5;			// when to retrain
	g_SOMPCompress.m_iClusterSize = 2;
	g_SOMPCompress.m_iCodeBookSize = 256;
	g_SOMPCompress.m_bLinear = true;

	g_SOMPCompress.m_iPatternQuality = 1;

	// Typical Texture Generation Using Data From The Bitmap
	glBindTexture(GL_TEXTURE_2D, texture[0]);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, g_SOMPCompress.image_bmp->sizeX, g_SOMPCompress.image_bmp->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, g_SOMPCompress.image_bmp->data);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

	memcpy(data,g_SOMPCompress.image_bmp->data,512*512*3);

	// Typical Texture Generation Using Data From The Bitmap
	glBindTexture(GL_TEXTURE_2D, texture[1]);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 512, 512, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

	glBindTexture(GL_TEXTURE_2D, texture[2]);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 512, 512, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);

	glBindTexture(GL_TEXTURE_2D, texture[3]);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 512, 512, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);


	// SOM init stuff
	g_SOMPCompress.initSOM();
	g_SOMPCompress.createPattern();

	return TRUE;												// Return TRUE (Initialization Successful)
}

void GL_Window::Deinitialize (void){							// Any User DeInitialization Goes Here
}

void Update (DWORD milliseconds)								// Perform Motion Updates Here
{
	g_keys->check();
	if (g_keys->keyDownNow [VK_ESCAPE] == TRUE)					// Is ESC Being Pressed?
	{
		g_window->TerminateApplication ();						// Terminate The Program
	}

	if (g_keys->keyDownNow [VK_F1] == TRUE)						// Is F1 Being Pressed?
	{
		g_window->ToggleFullscreen ();							// Toggle Fullscreen Mode
	}

	if (g_keys->keyDownNow [VK_SPACE] == TRUE)
	{
		if(g_SOMDrawMode == GL_LINE_STRIP){
			g_SOMDrawMode = GL_POINTS;
		}
		else{
			g_SOMDrawMode = GL_LINE_STRIP;
		}
	}

	if (g_keys->keyDownNow ['P'] == TRUE){
		g_window->m_bShowPattern =! g_window->m_bShowPattern;
	}
	if (g_keys->keyDownNow ['A'] == TRUE){
		g_window->m_bShowAna =! g_window->m_bShowAna;
	}

	if (g_keys->keyDownNow ['S'] == TRUE){
		g_window->m_bShowSOM = !g_window->m_bShowSOM;
	}
	if (g_keys->keyDownNow ['E'] == TRUE){
		g_window->m_bShowSOMExt = !g_window->m_bShowSOMExt;
	}

	if(g_keys->keyDownNow [VK_DOWN]){
		iMarkY --;
		if(iMarkY <0)
			iMarkY = 0;
	}
	if(g_keys->keyDownNow [VK_LEFT]){
		iMarkX --;
		if(iMarkX <0)
			iMarkX = 0;
	}
	if(g_keys->keyDownNow [VK_UP]){
		iMarkY ++;
		if(iMarkY >=sqrt(g_SOMPCompress.getCodebookSize()))
			iMarkY = sqrt(g_SOMPCompress.getCodebookSize())-1;
	}
	if(g_keys->keyDownNow [VK_RIGHT]){
		iMarkX ++;
		if(iMarkX >=sqrt(g_SOMPCompress.getCodebookSize()))
			iMarkX = sqrt(g_SOMPCompress.getCodebookSize())-1;
	}
	if(g_keys->keyDownNow[VK_RETURN])
		g_window->m_bRecalcBitmaps = true;

	if(g_keys->keyDownNow[VK_SHIFT])
		g_window->m_bSelect=!g_window->m_bSelect;

	dt = g_HPTime.getElapsed_d();
	g_HPTime.markTick();

	char szText[256];
	static nVec_var dLastSet = 0;
	static long lLastCount;
	if(fabs(dLastSet-g_HPTime.getTime_d()) > 1.){
		dLastSet=g_HPTime.getTime_d();
		float fTotalSize = log(g_SOMPCompress.getCodebookSize())/log(2)*512.*512./(float)g_SOMPCompress.m_iClusterSize/(float)g_SOMPCompress.m_iClusterSize + g_SOMPCompress.getCodebookSize()*3.*8.*g_SOMPCompress.m_iClusterSize*g_SOMPCompress.m_iClusterSize;
		float fTotalTime = float(g_HPTime.getTime() - g_HPTime.getTimerZero())/(float)g_HPTime.getFrequency();
		sprintf(szText,"%li/%.2ffps (%.4fLR/%.2fDistP) %.0fb %.0fkByte %.2fb/p %lipatt %.0fMCPS %liRetr %.0fmsPrev %.0ffS",g_lFrameCount - lLastCount,1./dt,g_SOMPCompress.GetLRate(),g_SOMPCompress.GetDistP(),log(g_SOMPCompress.getCodebookSize())/log(2), fTotalSize / 8. / 1024. ,fTotalSize / 512./512.,g_SOMPCompress.m_pPattern->lActPPos,(g_SOMPCompress.m_pPattern->lActPPos+g_SOMPCompress.m_pPattern->lRetrain) * g_SOMPCompress.getXSize() * g_SOMPCompress.getYSize() * g_SOMPCompress.getDimensionality()/dt/1000000.*(g_SOMPCompress.GetDistP() < .8f?0:1),g_SOMPCompress.m_pPattern->lRetrain,g_fPreviewTime*1000.0,fTotalTime);
		lLastCount = g_lFrameCount;
		SetWindowText(g_window->hWnd,szText);
	}
}

#define _INDEXCORR 8.f

void GL_Window::drawErrors(void){
	if(!g_SOMPCompress.m_pPattern)
		return;
	long lIndex,lschl;
	long lCount[_MAXfErrorLines];
	float fErrorLines;
	float fMax = 0,fTemp,fAv = 0;
	long lMax;
	for(lschl=0; lschl < _MAXfErrorLines; lschl ++) lCount[lschl] = 0;

	glPushMatrix();
	if(g_keys->keyDown['6']){
		glTranslatef(0,-1,3);
		fErrorLines = _MAXfErrorLines;
	}
	else{
		glTranslatef(0,1,-2);
		fErrorLines = _MAXfErrorLines<100?_MAXfErrorLines:100;
	}
	glBindTexture(GL_TEXTURE_2D, 0);
	glColor3f(1,1,1);

	for(lschl=0; lschl < g_SOMPCompress.m_pPattern->lActPPos; lschl ++){
		fTemp = g_SOMPCompress.m_pPattern->pFPattern[lschl].getLastError()/float(g_SOMPCompress.m_iClusterSize);
		lIndex = fTemp * fErrorLines  * _INDEXCORR;
		fAv += fTemp;

		if(lIndex < 0)
			lIndex = 0;
		if(lIndex >=fErrorLines)
			lIndex = fErrorLines-1;
		lCount[lIndex] ++;
	}

	fAv /= float(g_SOMPCompress.m_pPattern->lActPPos);

	for(lschl=0; lschl < fErrorLines; lschl ++)
		if(lCount[lschl] > fMax){
			fMax = lCount[lschl];
			lMax = lschl;
		}

	glBegin(GL_TRIANGLE_STRIP);
	for(lschl=0; lschl < fErrorLines; lschl ++){
		glColor3f(lschl/(float)fErrorLines,((float)fErrorLines-(float)lschl)/(float)fErrorLines,0);
		glVertex3f(-1,(float)lschl/fErrorLines*2,0);
		glColor3f(float(lschl)/(float)fErrorLines/(1.+(float)lCount[lschl]/fMax*2),((float)fErrorLines-(float)lschl)/(float)fErrorLines/(1.+(float)lCount[lschl]/fMax*2),0);
		glVertex3f(-1+(float)lCount[lschl]*2/fMax,(float)lschl/fErrorLines*2,0);
	}
	glEnd();

	// draw 10% line

	glBegin(GL_LINES);
	long l10p = g_SOMPCompress.m_pPattern->lActPPos*100/fErrorLines/10;
	glColor3f(0,0,1);
	glVertex3f(-1+(float)l10p*2/fMax,0,0);
	glVertex3f(-1+(float)l10p*2/fMax,2,0);

	// draw 100;10;1;0 count
	glVertex3f(-1+(float)100*2/fMax,2,0);
	glVertex3f(-1+(float)100*2/fMax,2.05,0);
	glVertex3f(-1+(float)100*2/fMax,2.05,0);
	glVertex3f(10,2.05,0);
	glVertex3f(-1+(float)10*2/fMax,2,0);
	glVertex3f(-1+(float)10*2/fMax,2.1,0);
	glVertex3f(-1+(float)10*2/fMax,2.1,0);
	glVertex3f(10,2.1,0);
	glVertex3f(-1+(float)1*2/fMax,2,0);
	glVertex3f(-1+(float)1*2/fMax,2.15,0);
	glVertex3f(-1+(float)1*2/fMax,2.15,0);
	glVertex3f(10,2.15,0);
	glColor3f(1,1,1);
	glVertex3f(-1,2,0);
	glVertex3f(-1,2.2,0);
	glEnd();

	// draw average

	glBegin(GL_LINES);
	lIndex = fAv * fErrorLines* _INDEXCORR;
	glColor3f(1,0,0);
	glVertex3f(1,(float)lIndex/fErrorLines*2,0);
	glVertex3f(-1,(float)lIndex/fErrorLines*2,0);
	// draw retrain mark
	lIndex = fAv * g_SOMPCompress.m_fRetrainFactor* fErrorLines * _INDEXCORR;
	glColor3f(0,0,1);
	glVertex3f(1,(float)lIndex/fErrorLines*2,0);
	glVertex3f(-1,(float)lIndex/fErrorLines*2,0);
	glEnd();

	// draw 10 unit mark
	glBegin(GL_LINES);
	glColor3f(1,0,0);
	glVertex3f(-1,.0/fErrorLines*2,0);
	glVertex3f(-1.2,0/fErrorLines*2,0);
	// draw 10 unit mark
	glVertex3f(-1,10.0/fErrorLines*2,0);
	glVertex3f(-1.2,10.0/fErrorLines*2,0);
	// draw 1 unit mark
	glVertex3f(-1,1.0/fErrorLines*2,0);
	glVertex3f(-1.2,1.0/fErrorLines*2,0);
	// draw max
	glVertex3f(-1,2,0);
	glVertex3f(-1.2,2,0);
	// draw max errormark

	glColor3f(1,1,1);
	glVertex3f(1,lMax/fErrorLines*2,0);
	glVertex3f(-1.25,lMax/fErrorLines*2,0);
	glEnd();

	glPopMatrix();
}

void GL_Window::drawSOM(void){
	nVec_var *dpWeight;
	glPushMatrix();
	if(g_keys->keyDown['5']){
		glTranslatef(0,0,4);
	}
	else
		glTranslatef(0,-.5,2);
	//glScalef(1.f/255.f,1.f/255.f,1.f/255.f);
	glRotatef(g_HPTime.getTime_d()*10,1,0,0);
	glTranslatef(-.5,-.5,-.5);
	long lx,ly,l;

	glBindTexture(GL_TEXTURE_2D, 0);
	glTexCoord2f(0,0);
	if(m_bShowSOM){
		for(lx = 0; lx < g_SOMPCompress.getXSize(); lx ++){
			glBegin(g_SOMDrawMode);
			for(ly = 0; ly < g_SOMPCompress.getYSize(); ly ++){
				dpWeight = g_SOMPCompress.GetNWeights(lx,ly);

				glColor3fv(dpWeight);
				glVertex3fv(dpWeight);
			}
			glEnd();
		}
		for(ly = 0; ly < g_SOMPCompress.getYSize(); ly ++){
			glBegin(g_SOMDrawMode);
			for(lx = 0; lx < g_SOMPCompress.getXSize(); lx ++){
				dpWeight = g_SOMPCompress.GetNWeights(lx,ly);

				glColor3fv(dpWeight);
				glVertex3fv(dpWeight);
			}
			glEnd();
		}
	}

	if(m_bShowSOMExt){
		for(lx = 0; lx < g_SOMPCompress.getXSize(); lx ++){
			for(ly = 0; ly < g_SOMPCompress.getYSize(); ly ++){
				glBegin(g_SOMDrawMode);
				dpWeight = g_SOMPCompress.GetNWeights(lx,ly);

				for(l=0; l < g_SOMPCompress.m_iClusterSize*g_SOMPCompress.m_iClusterSize *3; l +=3 ){
					glColor3fv(&dpWeight[l]);
					glVertex3fv(&dpWeight[l]);
				}
				glEnd();
			}
		}
	}
	if(g_window->m_bSelect){
		if(g_SOMPCompress.m_bLinear)
			dpWeight = g_SOMPCompress.GetNWeights(iMarkY*int(sqrt(g_SOMPCompress.getCodebookSize()))+iMarkX,0);
		else
			dpWeight = g_SOMPCompress.GetNWeights(iMarkY,iMarkX);

		glBegin(GL_LINES);
		glColor3fv(dpWeight);
		glVertex3fv(dpWeight);
		glVertex3f(0,0,0);
		glVertex3fv(dpWeight);
		glVertex3f(1,1,1);
		glVertex3fv(dpWeight);
		glVertex3f(0,1,0);
		glVertex3fv(dpWeight);
		glVertex3f(0,0,1);
		glVertex3fv(dpWeight);
		glVertex3f(1,0,0);
		glVertex3fv(dpWeight);
		glVertex3f(1,1,0);
		glVertex3fv(dpWeight);
		glVertex3f(1,0,1);
		glVertex3fv(dpWeight);
		glVertex3f(0,1,1);
		glEnd();
	}

	if(m_bShowPattern){
		glBegin(GL_POINTS);
		for(l = 0; l < g_SOMPCompress.m_pPattern->lActPPos; l++){
			dpWeight = g_SOMPCompress.m_pPattern->pFPattern[l].getData();
			glColor3fv(dpWeight);
			glVertex3fv(dpWeight);
		}
		glEnd();
		glColor3f(1,1,1);
	}

	// draw box
	glBegin(GL_LINE_STRIP);

	glColor3f(0,0,0);
	glVertex3f(0,0,0);
	glColor3f(0,0,1);
	glVertex3f(0,0,1);
	glColor3f(0,1,1);
	glVertex3f(0,1,1);
	glColor3f(1,1,1);
	glVertex3f(1,1,1);
	glColor3f(1,1,0);
	glVertex3f(1,1,0);
	glColor3f(1,0,0);
	glVertex3f(1,0,0);
	glColor3f(0,0,0);
	glVertex3f(0,0,0);
	glColor3f(0,1,0);
	glVertex3f(0,1,0);
	glColor3f(0,1,1);
	glVertex3f(0,1,1);

	glEnd();
	glBegin(GL_LINES);

	glColor3f(0,1,0);
	glVertex3f(0,1,0);
	glColor3f(1,1,0);
	glVertex3f(1,1,0);

	glColor3f(1,1,1);
	glVertex3f(1,1,1);
	glColor3f(1,0,1);
	glVertex3f(1,0,1);

	glColor3f(1,0,1);
	glVertex3f(1,0,1);
	glColor3f(0,0,1);
	glVertex3f(0,0,1);

	glColor3f(1,0,1);
	glVertex3f(1,0,1);
	glColor3f(1,0,0);
	glVertex3f(1,0,0);

	glColor3f(1,1,1);

	glEnd();
	glPopMatrix();
}

void GL_Window::drawPictures(void){
	// original
	glPushMatrix();
	if(g_keys->keyDown['1'])
		glTranslatef(0,0,3.5);
	else
		glTranslatef(-2,1,0);
	glBindTexture(GL_TEXTURE_2D, texture[0]);
	glBegin(GL_TRIANGLE_STRIP);

	glTexCoord2f(0,0);
	glVertex3f(-1,-1,0);

	glTexCoord2f(0,1);
	glVertex3f(-1,1,0);

	glTexCoord2f(1,0);
	glVertex3f(1,-1,0);

	glTexCoord2f(1,1);
	glVertex3f(1,1,0);

	glEnd();
	glPopMatrix();

	// changed stuff
	glPushMatrix();
	if(g_keys->keyDown['2'])
		glTranslatef(0,0,3.5);
	else
		glTranslatef(2,1,0);
	glBindTexture(GL_TEXTURE_2D, texture[1]);
	glBegin(GL_TRIANGLE_STRIP);

	glTexCoord2f(0,0);
	glVertex3f(-1,-1,0);

	glTexCoord2f(0,1);
	glVertex3f(-1,1,0);

	glTexCoord2f(1,0);
	glVertex3f(1,-1,0);

	glTexCoord2f(1,1);
	glVertex3f(1,1,0);

	glEnd();
	glPopMatrix();

	// weights
	glPushMatrix();
	if(g_keys->keyDown['3'])
		glTranslatef(0,0,3.5);
	else
		glTranslatef(2,-1.1,0);
	glBindTexture(GL_TEXTURE_2D, texture[2]);
	glBegin(GL_TRIANGLE_STRIP);

	glTexCoord2f(0,0);
	glVertex3f(-1,-1,0);

	glTexCoord2f(0,1);
	glVertex3f(-1,1,0);

	glTexCoord2f(1,0);
	glVertex3f(1,-1,0);

	glTexCoord2f(1,1);
	glVertex3f(1,1,0);

	glEnd();

	if(g_window->m_bSelect){
		glBindTexture(GL_TEXTURE_2D, 0);
		glBegin(GL_LINES);
		glTexCoord2f(0,0);
		glColor3f(0,0,1);

		glVertex3f( float(iMarkX) / sqrt(g_SOMPCompress.getCodebookSize())*2-1 + 1./sqrt(g_SOMPCompress.getCodebookSize()),
			float(iMarkY) / sqrt(g_SOMPCompress.getCodebookSize())*2-1 + 1./sqrt(g_SOMPCompress.getCodebookSize()),
			0.01);
		glVertex3f(-1,-1,0.01);
		glVertex3f( float(iMarkX) / sqrt(g_SOMPCompress.getCodebookSize())*2-1 + 1./sqrt(g_SOMPCompress.getCodebookSize()),
			float(iMarkY) / sqrt(g_SOMPCompress.getCodebookSize())*2-1 + 1./sqrt(g_SOMPCompress.getCodebookSize()),
			0.01);
		glVertex3f( -1,
			float(iMarkY) / sqrt(g_SOMPCompress.getCodebookSize())*2-1 + 1./sqrt(g_SOMPCompress.getCodebookSize()),
			0.01);
		glVertex3f( float(iMarkX) / sqrt(g_SOMPCompress.getCodebookSize())*2-1 + 1./sqrt(g_SOMPCompress.getCodebookSize()),
			float(iMarkY) / sqrt(g_SOMPCompress.getCodebookSize())*2-1 + 1./sqrt(g_SOMPCompress.getCodebookSize()),
			0.01);
		glVertex3f( float(iMarkX) / sqrt(g_SOMPCompress.getCodebookSize())*2-1 + 1./sqrt(g_SOMPCompress.getCodebookSize()),
			-1,
			0.01);

		glEnd();
	}
	glPopMatrix();

	// normalized difference
	glPushMatrix();
	glColor3f(1,1,1);
	if(g_keys->keyDown['4'])
		glTranslatef(0,0,3.5);
	else
		glTranslatef(-2,-1.1,0);
	glBindTexture(GL_TEXTURE_2D, texture[3]);
	glBegin(GL_TRIANGLE_STRIP);

	glTexCoord2f(0,0);
	glVertex3f(-1,-1,0);

	glTexCoord2f(0,1);
	glVertex3f(-1,1,0);

	glTexCoord2f(1,0);
	glVertex3f(1,-1,0);

	glTexCoord2f(1,1);
	glVertex3f(1,1,0);

	glEnd();
	glPopMatrix();
}

void GL_Window::Draw (void){
	float fSleep;
	long lIndex;
	double dPreviewS,dPreviewE;
	fSleep = 1./30.-dt;
	if(fSleep > 0.001)
		Sleep(fSleep * 1000.);
	g_lFrameCount ++;
	glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);		// Clear Screen And Depth Buffer
	glLoadIdentity ();											// Reset The Modelview Matrix
	glTranslatef (0.0f, 0.0f, -6.0f);							// Translate 6 Units Into The Screen

	nVec_var *dpWeight;
	// do some stuff to data
	if(g_window && g_SOMPCompress.m_pPattern){
		if(g_window->m_bRecalcBitmaps){
#ifndef _DECOMPRESS
			if(g_SOMPCompress.GetDistP() > _MINDISTF)
				g_SOMPCompress.iterate();
#endif
			int x,y,iStep = sqrt(g_SOMPCompress.GetDistP())*10,xg,yg,i,xo,yo,
				iYSize = g_SOMPCompress.image_bmp->sizeY,
				iXSize = g_SOMPCompress.image_bmp->sizeX;
			long lWx,lWy;
			nVec_var *dpPattern = new nVec_var[g_SOMPCompress.getDimensionality()];

			if(iStep > _MAXPREVIEWSIZE)
				iStep = _MAXPREVIEWSIZE;
			if(g_SOMPCompress.GetDistP() < _MINDISTF){
				iStep = 1;
			}
			if(g_keys->keyDown['G']){
				g_window->m_bShowAna = false;
				iStep = 1;
			}
			if(g_window->m_bShowAna || iStep == 1){
				dPreviewS = g_HPTime.getTime_d();

				// preview compression

				for(x=0; x < g_SOMPCompress.image_bmp->sizeX+1-g_SOMPCompress.m_iClusterSize; x+=iStep*g_SOMPCompress.m_iClusterSize){
					for(y=0; y < g_SOMPCompress.image_bmp->sizeY+1-g_SOMPCompress.m_iClusterSize; y+=iStep*g_SOMPCompress.m_iClusterSize){
						for(xo=0; xo < g_SOMPCompress.m_iClusterSize; xo ++){
							for(yo=0; yo < g_SOMPCompress.m_iClusterSize; yo ++){
								for(i=0; i<3; i++){
									dpPattern[i + (yo + xo * g_SOMPCompress.m_iClusterSize)*3] = ((nVec_var)(((unsigned char *)(g_SOMPCompress.image_bmp->data))[(x+xo)*3 + (y+yo)*3*iXSize + i]))/256.;
								}
							}
						}

						g_SOMPCompress.SetInput(dpPattern);
						g_SOMPCompress.GetWinner(lWx,lWy);

						dpWeight = g_SOMPCompress.GetNWeights(lWx,lWy);

						lIndex = lWx * g_SOMPCompress.lYSize + lWy;

						bool bMark;
						if(g_SOMPCompress.m_bLinear)
							bMark = (iMarkX+iMarkY*int(sqrt(g_SOMPCompress.getCodebookSize())) == lWx) && g_window->m_bSelect;
						else
							bMark = ((iMarkX == lWy ) && (iMarkY == lWx)) && g_window->m_bSelect;

						for(xo=0; xo < g_SOMPCompress.m_iClusterSize; xo ++){
							for(yo=0; yo < g_SOMPCompress.m_iClusterSize; yo ++){
								for(i=0; i<3; i++){
									if(!bMark){
										data[y+yo][x+xo][i] = (unsigned char)(dpWeight[i + (yo + xo * g_SOMPCompress.m_iClusterSize)*3]*256.);
										//data[y+yo][x+xo][i] = lIndex;
									}
									else{
										data[y+yo][x+xo][i] = 255-(unsigned char)(dpWeight[i + (yo + xo * g_SOMPCompress.m_iClusterSize)*3]*256.);
									}
								}
							}
						}
						if(iStep > 1){
							for(xg=0; xg < iStep*g_SOMPCompress.m_iClusterSize; xg++){
								for(yg=0; yg < iStep*g_SOMPCompress.m_iClusterSize; yg++){
									if(x+xg<512 && y + yg < 512){
										for(i=0; i<3; i++){
											data[y+yg][x+xg][i] = data[y][x][i];
										}
									}
								}
							}
						}
					}
				}
				glBindTexture(GL_TEXTURE_2D, texture[1]);
				glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 512, 512, 0, GL_RGB, GL_UNSIGNED_BYTE, data);

				unsigned char usmax = 0;
				int iStepD = iStep * g_SOMPCompress.m_iClusterSize;
				if(iStep == 1)
					iStepD = 1;
				for(x=0; x < g_SOMPCompress.image_bmp->sizeX; x+=iStepD){
					for(y=0; y < g_SOMPCompress.image_bmp->sizeY; y+=iStepD){
						for(i=0; i < 3; i++){
							if(((unsigned char *)(g_SOMPCompress.image_bmp->data))[(x)*3*g_SOMPCompress.image_bmp->sizeY + (y)*3 + i] > data[x][y][i])
								difference[x][y][i] = ((unsigned char *)(g_SOMPCompress.image_bmp->data))[(x)*3*g_SOMPCompress.image_bmp->sizeY + (y)*3 + i] - data[x][y][i];
							else
								difference[x][y][i] = -((unsigned char *)(g_SOMPCompress.image_bmp->data))[(x)*3*g_SOMPCompress.image_bmp->sizeY + (y)*3 + i] + data[x][y][i];

							if(usmax < difference[x][y][i]){
								usmax = difference[x][y][i];
							}
						}


						if(iStepD > 1){
							for(xg=0; xg < iStepD; xg++){
								for(yg=0; yg < iStepD; yg++){
									if(x+xg<g_SOMPCompress.image_bmp->sizeX && y + yg < g_SOMPCompress.image_bmp->sizeY){
										difference[x+xg][y+yg][0] = difference[x][y][0];
										difference[x+xg][y+yg][1] = difference[x][y][1];
										difference[x+xg][y+yg][2] = difference[x][y][2];
									}
								}
							}
						}
					}
				}
				for(x=0; x < g_SOMPCompress.image_bmp->sizeX; x++){
					for(y=0; y < 10; y++){
						for(i=0; i < 3; i++){
							difference[x][y][i] = float(x)*256./float(g_SOMPCompress.image_bmp->sizeX);
						}
					}
				}
				/*glBindTexture(GL_TEXTURE_2D, texture[2]);
				glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 512, 512, 0, GL_RGB, GL_UNSIGNED_BYTE, difference);*/
				float fFactor = 255.f/(float)usmax;
				for(x=0; x < g_SOMPCompress.image_bmp->sizeX; x++){
					for(y=0; y < g_SOMPCompress.image_bmp->sizeY; y++){
						for(i=0; i < 3; i++){
							normaldifference[x][y][i] = difference[x][y][i] * fFactor;
						}
					}
				}
				glBindTexture(GL_TEXTURE_2D, texture[3]);
				glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 512, 512, 0, GL_RGB, GL_UNSIGNED_BYTE, normaldifference);

				// create picture of weights
				int iRows = sqrt(g_SOMPCompress.m_iCodeBookSize);
				int iSquareSize = 512 / iRows - 1;
				int iCompSquare = iSquareSize / (g_SOMPCompress.m_iClusterSize + 1);
				int iW,ix,iy,ix2,iy2,ix3,iy3,ixO,iyO;
				nVec_var *dpWeight;
				unsigned int temp;

				ixO = (512- iSquareSize*iRows) / 2 + iCompSquare / 2;
				iyO = (512- iSquareSize*iRows) / 2 + iCompSquare / 2;

				for(iW=0; iW < g_SOMPCompress.m_iCodeBookSize; iW++){
					dpWeight = g_SOMPCompress.GetNWeights(iW/g_SOMPCompress.lYSize,iW%g_SOMPCompress.lYSize);
					if(!dpWeight)
						continue;

					ix = iW / iRows;
					iy = iW % iRows;

					// draw square
					for(ix2 = 0; ix2 < g_SOMPCompress.m_iClusterSize; ix2++){
						for(iy2 = 0; iy2 < g_SOMPCompress.m_iClusterSize; iy2++){
							for(i=0; i < 3; i++){
								temp = dpWeight[(iy2*g_SOMPCompress.m_iClusterSize + ix2)*3 + i] * 256;
								//temp = iW;
								for(ix3 =0; ix3 < iCompSquare; ix3++){
									for(iy3 =0; iy3 < iCompSquare; iy3++){
										weights[ix*iSquareSize+ix2*iCompSquare+ix3+ixO][iy*iSquareSize+iy2*iCompSquare+iy3+iyO][i] = temp;
									}
								}
							}
						}
					}

				}
				glBindTexture(GL_TEXTURE_2D, texture[2]);
				glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 512, 512, 0, GL_RGB, GL_UNSIGNED_BYTE, weights);

				dPreviewE = g_HPTime.getTime_d();
				g_fPreviewTime = dPreviewE - dPreviewS;

				if(iStep == 1){
					char szText[80];
					char szExt[80];
					sprintf(szExt,"%i %i %lf",g_SOMPCompress.getCodebookSize(),g_SOMPCompress.m_iClusterSize,g_SOMPCompress.GetDistP());
					sprintf(szText,"compressed %s",szExt);
					g_window->saveBMP(512,512,szText,&(data[0][0][0]));
					sprintf(szText,"diff %s",szExt);
					g_window->saveBMP(512,512,szText,&(difference[0][0][0]));
					sprintf(szText,"nordiff %s",szExt);
					g_window->saveBMP(512,512,szText,&(normaldifference[0][0][0]));
					sprintf(szText,"weights %s",szExt);
					g_window->saveBMP(512,512,szText,&(weights[0][0][0]));
					/*sprintf(szText,"%s.som",szExt);
					g_SOMPCompress.Save(szText);*/
#ifndef _DECOMPRESS
					/*sprintf(szText,"shots/%s.vqp",szExt);
					g_SOMPCompress.saveCPicture(szText);
					g_SOMPCompress.saveCPicture("test.txt");*/
#endif
				}
			}
			delete [] dpPattern;
		}

		if(g_SOMPCompress.GetDistP() < _MINDISTF){
			g_window->m_bRecalcBitmaps = false;
		}
	}

	// draw all this

	// draw errors

	if(!g_keys->keyDown['5'])
		drawErrors();

	//return;

	// draw SOM

	if(!g_keys->keyDown['6'])
	drawSOM();

	if(!g_keys->keyDown['5']
	&& !g_keys->keyDown['6']){
		drawPictures();
	}

	glFlush ();													// Flush The GL Rendering Pipeline
}

