#include <GL/gl.h>
#include <GL/glu.h>
#include <stdio.h>
#include <math.h>
#include "tool.h"
#include <windows.h>

// OpenGLを使って長方形描画.
//	ベタ塗りの長方形.
void box( float x1, float y1, float x2, float y2)
{
	glBegin(GL_QUADS);
	glVertex2f( x1, y1 );
	glVertex2f( x2, y1 );
	glVertex2f( x2, y2 );
	glVertex2f( x1, y2 );
	glEnd();
}
//	全体テクスチャ座標付き長方形.
void texbox( float x1, float y1, float x2, float y2)
{
	glBegin(GL_QUADS);
	glTexCoord2f( 0, 0 );
	glVertex2f( x1, y1 );
	glTexCoord2f( 1, 0 );
	glVertex2f( x2, y1 );
	glTexCoord2f( 1, 1 );
	glVertex2f( x2, y2 );
	glTexCoord2f( 0, 1 );
	glVertex2f( x1, y2 );
	glEnd();
}
//	テクスチャ座標指定付き長方形.
void texbox2( float pos[2][2], float tex[2][2] )
{
	glBegin(GL_QUADS);
	glTexCoord2f( tex[0][0], tex[0][1] );
	glVertex2f( pos[0][0], pos[0][1] );
	glTexCoord2f( tex[1][0], tex[0][1] );
	glVertex2f( pos[1][0], pos[0][1] );
	glTexCoord2f( tex[1][0], tex[1][1] );
	glVertex2f( pos[1][0], pos[1][1] );
	glTexCoord2f( tex[0][0], tex[1][1] );
	glVertex2f( pos[0][0], pos[1][1] );
	glEnd();
}

// BMPテクスチャ読み込み関数.
//  32ビットカラーと、8ビットモノクロ(グレースケール) にのみ対応.
int bmp_read( GLuint texid, const char *file, GLuint mode)
{
	FILE *fp = fopen( file, "rb" );
	if( fp == NULL ) {
fprintf( stderr, "%s:can't open file.\n", file);
		return 1;
	}
	BITMAPFILEHEADER	bmfh;
	BITMAPINFOHEADER	bmih;
	size_t rd=0;
	rd+= fread( (char*)&bmfh.bfType, 1, sizeof(bmfh.bfType), fp);
	rd+= fread( (char*)&bmfh.bfSize, 1, sizeof(bmfh.bfSize), fp);
	rd+= fread( (char*)&bmfh.bfReserved1, 1, sizeof(bmfh.bfReserved1), fp);
	rd+= fread( (char*)&bmfh.bfReserved2, 1, sizeof(bmfh.bfReserved2), fp);
	rd+= fread( (char*)&bmfh.bfOffBits, 1, sizeof(bmfh.bfOffBits), fp);
	size_t bmfhsize = sizeof(bmfh.bfType) + sizeof(bmfh.bfSize)
		+ sizeof(bmfh.bfReserved1) + sizeof(bmfh.bfReserved2)
		+ sizeof(bmfh.bfOffBits);
	if( rd != bmfhsize ) {
		fprintf(stderr, "%s: bmfh read fail.\n", file);
		return false;
	}

	if( memcmp( (char*)&bmfh.bfType, "BM", 2) != 0 ) {
fprintf( stderr, "%s:this file is not BMP.\n", file);
		return 1;
	}
#ifdef DEBUG
	fprintf(stderr,"file: %s\n", file);
	fprintf(stderr,"bfType: %d\n", bmfh.bfType);
	fprintf(stderr,"bfSize: %d\n", bmfh.bfSize);
	fprintf(stderr,"bfReserved1: %d\n", bmfh.bfReserved1);
	fprintf(stderr,"bfReserved2: %d\n", bmfh.bfReserved2);
	fprintf(stderr,"bfOffBits: %d\n", bmfh.bfOffBits);
#endif

	rd= 0;
	rd+=fread( (char*)&bmih.biSize, 1, sizeof(bmih.biSize), fp);
	rd+=fread( (char*)&bmih.biWidth, 1, sizeof(bmih.biWidth), fp);
	rd+=fread( (char*)&bmih.biHeight, 1, sizeof(bmih.biHeight), fp);
	rd+=fread( (char*)&bmih.biPlanes, 1, sizeof(bmih.biPlanes), fp);
	rd+=fread( (char*)&bmih.biBitCount, 1, sizeof(bmih.biBitCount), fp);
	rd+=fread( (char*)&bmih.biCompression, 1, sizeof(bmih.biCompression), fp);
	rd+=fread( (char*)&bmih.biSizeImage, 1, sizeof(bmih.biSizeImage), fp);
	rd+=fread( (char*)&bmih.biXPelsPerMeter, 1, sizeof(bmih.biXPelsPerMeter), fp);
	rd+=fread( (char*)&bmih.biYPelsPerMeter, 1, sizeof(bmih.biYPelsPerMeter), fp);
	rd+=fread( (char*)&bmih.biClrUsed, 1, sizeof(bmih.biClrUsed), fp);
	rd+=fread( (char*)&bmih.biClrImportant, 1, sizeof(bmih.biClrImportant), fp);
	size_t bmihsize = sizeof(bmih.biSize)
			+ sizeof(bmih.biWidth)
			+ sizeof(bmih.biHeight)
			+ sizeof(bmih.biPlanes)
			+ sizeof(bmih.biBitCount)
			+ sizeof(bmih.biCompression)
			+ sizeof(bmih.biSizeImage)
			+ sizeof(bmih.biXPelsPerMeter)
			+ sizeof(bmih.biYPelsPerMeter)
			+ sizeof(bmih.biClrUsed)
			+ sizeof(bmih.biClrImportant);
	if( rd != bmihsize ) {
fprintf( stderr, "%s:bmih read fail.\n", file);
		return 1;
	}
#ifdef DEBUG
	fprintf(stderr, "biSize: %d\n", bmih.biSize);
	fprintf(stderr, "biWidth: %d\n", bmih.biWidth);
	fprintf(stderr, "biHeight: %d\n", bmih.biHeight);
	fprintf(stderr, "biPlanes: %hd\n", bmih.biPlanes);
	fprintf(stderr, "biBitCount: %hd\n", bmih.biBitCount);
	fprintf(stderr, "biCompression: %d\n", bmih.biCompression);
	fprintf(stderr, "biSizeImage: %d\n", bmih.biSizeImage);
	fprintf(stderr, "biXPelsPerMeter: %d\n", bmih.biXPelsPerMeter);
	fprintf(stderr, "biYPelsPerMeter: %d\n", bmih.biYPelsPerMeter);
	fprintf(stderr, "biClrUsed: %d\n", bmih.biClrUsed);
	fprintf(stderr, "biClrImportant: %d\n", bmih.biClrImportant);
#endif
	if( bmih.biCompression != BI_RGB ) {
fprintf( stderr, "%s:Compression mode(%d) is not supported.\n", file, bmih.biCompression);
		return 1;
	}
	int ri;
	ri = fseek( fp, bmfh.bfOffBits, SEEK_SET );
	if( ri != 0 ) {
fprintf( stderr, "%s:seek fail.\n", file);
		return 1;
	}
	size_t size = bmih.biWidth * bmih.biHeight * (bmih.biBitCount >>3);
	char *buf;
	buf = new char[size];
	if( buf == NULL ) {
fprintf( stderr, "%s:buf is NULL.\n", file);
		return 1;
	}
	rd = fread( buf, 1, size, fp );
	if( rd != size ) {
fprintf( stderr, "%s:size illegal rd(%d) != size(%d)\n", file, rd, size );
		return 1;
	}

	if( bmih.biBitCount == 24 ) {
		// BGR to RGB
		size_t	scanlinebytes = bmih.biWidth * 3;
		if( (scanlinebytes % 4) > 0 ) {
			scanlinebytes += 4 - (scanlinebytes % 4);
		}
		for( size_t y=0; y< (size_t)bmih.biHeight; y++ ) {
			size_t base =y*scanlinebytes;
			for( size_t x=0; x< (size_t)bmih.biWidth; x++ ) {
				size_t pos = base + x * 3;
				char c = buf[pos + 0];
				buf[pos + 0] = buf[pos + 2];
				buf[pos + 2] = c;
			}
		}
	}
	else if (bmih.biBitCount == 8 && mode == GL_RGB) {
		mode = GL_LUMINANCE;
	}

	glBindTexture( GL_TEXTURE_2D, texid );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
	glTexImage2D( GL_TEXTURE_2D, 0, mode, bmih.biWidth, bmih.biHeight, 0,
				  mode, GL_UNSIGNED_BYTE, buf );
	fprintf(stderr, "file   : %s\n", file );
	fprintf(stderr, "size   : %d, %d\n", bmih.biWidth, bmih.biHeight );
	fprintf(stderr, "bufsize: %d\n", size );
	fprintf(stderr, "texmode: %08x\n", mode );
// 	gluBuild2DMipmaps( GL_TEXTURE_2D, mode, bmih.biWidth, bmih.biHeight,
// 				  mode, GL_UNSIGNED_BYTE, buf );
// 	glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, bmih.biWidth, bmih.biHeight, 0,
// 				  GL_RGB, GL_UNSIGNED_BYTE, buf );
// 	gluBuild2DMipmaps( GL_TEXTURE_2D, GL_RGB, bmih.biWidth, bmih.biHeight,
// 					   GL_RGB, GL_UNSIGNED_BYTE, buf );

// 	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
// 	gluBuild2DMipmaps( GL_TEXTURE_2D, mode, bmih.biWidth, bmih.biHeight,
// 					   mode, GL_UNSIGNED_BYTE, buf );
	delete[] buf;
	fclose(fp);
	return 0;
}
/*	画像上の座標を画面座標に変換.
 *
 *	pos		: OUT 画面座標.
 *	bmp		: IN  画像上の座標.
 *	length	: IN  画像の一辺の長さ.
 */
void bmp2pos( float pos[2][2], const size_t bmp[2][2], size_t length )
{
	length--;
	pos[0][0] = bmp[0][0];
	pos[0][1] = length - bmp[1][1];
	pos[1][0] = bmp[1][0];
	pos[1][1] = length - bmp[0][1];
}
/*	画面座標をテクスチャ座標に変換.
 *
 *	tex		: OUT テクスチャ座標.
 *	pos		: IN  画面座標.
 *	length	: IN  画像の一辺の長さ.
 */
void pos2tex( float tex[2][2], const float pos[2][2], size_t length )
{
	length--;
	tex[0][0] = pos[0][0] / (float)length;
	tex[0][1] = pos[0][1] / (float)length;
	tex[1][0] = pos[1][0] / (float)length;
	tex[1][1] = pos[1][1] / (float)length;
}
/*	画像上の座標をテクスチャ座標に変換.
 *
 *	画像上の座標は左上原点で、整数値.
 *	テクスチャ座標は左下原点で、0-1.0の実数値.
 *
 *	tex		: OUT テクスチャ座標.
 *	bmp		: IN  画像上の座標.
 *	length	: IN  画像の一辺の長さ.
 */
void bmp2tex( float tex[2][2], const size_t bmp[2][2], size_t length )
{
	length--;
	tex[0][0] = bmp[0][0] / (float)length;
	tex[0][1] = (length - bmp[1][1]) / (float)length;
	tex[1][0] = bmp[1][0] / (float)length;
	tex[1][1] = (length - bmp[0][1]) / (float)length;
}
/* pos座標を中央化する.
 *
 *	base	: OUT    中央座標
 *	pos		: IN/OUT 画面座標(IN) -> 中央座標からの相対座標(OUT)
 */
void centerize_pos( float base[2], float pos[2][2] )
{
	base[0] = pos[0][0] + (pos[1][0] - pos[0][0]) * 0.5;
	base[1] = pos[0][1] + (pos[1][1] - pos[0][1]) * 0.5;
	pos[0][0] -= base[0];
	pos[1][0] -= base[0];
	pos[0][1] -= base[1];
	pos[1][1] -= base[1];
}
// 画面座標を拡大縮小(スケーリング)する.
void multiply_pos( float pos[2][2], float rate )
{
	pos[0][0] *= rate;
	pos[0][1] *= rate;
	pos[1][0] *= rate;
	pos[1][1] *= rate;
}
// ベクトルの正規化.
void normalize( float ret[3], float val[3] )
{
	float rate = 1.0 / sqrt( val[0] * val[0] + val[1] * val[1] + val[2] * val[2] );
	ret[0] = val[0] * rate;
	ret[1] = val[1] * rate;
	ret[2] = val[2] * rate;
}
// ベクトルの乗算.
void multiply( float val[3], float rate )
{
	val[0] *= rate;
	val[1] *= rate;
	val[2] *= rate;
}
// ベクトルの外積.
void crossproduct( float axis[3], float dir1[3], float dir2[3] )
{
	axis[0] = dir1[1] * dir2[2] - dir1[2] * dir2[1];
	axis[1] = dir1[2] * dir2[0] - dir1[0] * dir2[2];
	axis[2] = dir1[0] * dir2[1] - dir1[1] * dir2[0];
}
