/*
 
 Copyright (C) 2009 Id Software, Inc.
 
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 */

/*
mac wolf wall mapping:
cp 000.5551 ../../../base/walls/000.5551
cp 001.5551 ../../../base/walls/010.5551
cp 002.5551 ../../../base/walls/004.5551
cp 003.5551 ../../../base/walls/006.5551
cp 004.5551 ../../../base/walls/022.5551
cp 005.5551 ../../../base/walls/018.5551
cp 006.5551 ../../../base/walls/020.5551
cp 007.5551 ../../../base/walls/044.5551
cp 008.5551 ../../../base/walls/014.5551
cp 009.5551 ../../../base/walls/008.5551
cp 010.5551 ../../../base/walls/032.5551
cp 011.5551 ../../../base/walls/038.5551
cp 014.5551 ../../../base/walls/040.5551
cp 015.5551 ../../../base/walls/041.5551
cp 016.5551 ../../../base/walls/030.5551
cp 017.5551 ../../../base/walls/028.5551
cp 018.5551 ../../../base/walls/026.5551
cp 019.5551 ../../../base/walls/046.5551
cp 020.5551 ../../../base/walls/050.5551
cp 021.5551 ../../../base/walls/078.5551
cp 022.5551 ../../../base/walls/066.5551
cp 025.5551 ../../../base/walls/056.5551
cp 026.5551 ../../../base/walls/058.5551
cp 027.5551 ../../../base/walls/086.5551
cp 028.5551 ../../../base/walls/090.5551
cp 029.5551 ../../../base/walls/068.5551
cp 030.5551 ../../../base/walls/126.5551
cp 031.5551 ../../../base/walls/132.5551
cp 032.5551 ../../../base/walls/133.5551
cp 033.5551 ../../../base/walls/130.5551
cp 034.5551 ../../../base/walls/128.5551

 // SOD? cp 012.5551 ../../../base/walls/000.5551
 // SOD? cp 013.5551 ../../../base/walls/000.5551
 // SOD? cp 023.5551 ../../../base/walls/000.5551
 // SOD? cp 024.5551 ../../../base/walls/000.5551
 
 All odd numbers except 31, 41, 43, and 133 can be just darkened down
 

cp 000.tga ../../newWalls/000.tga
cp 001.tga ../../newWalls/010.tga
cp 002.tga ../../newWalls/004.tga
cp 003.tga ../../newWalls/006.tga
cp 004.tga ../../newWalls/022.tga
cp 005.tga ../../newWalls/018.tga
cp 006.tga ../../newWalls/020.tga
cp 007.tga ../../newWalls/044.tga
cp 008.tga ../../newWalls/014.tga
cp 009.tga ../../newWalls/008.tga
cp 010.tga ../../newWalls/032.tga
cp 011.tga ../../newWalls/038.tga
cp 014.tga ../../newWalls/040.tga
cp 015.tga ../../newWalls/041.tga
cp 016.tga ../../newWalls/030.tga
cp 017.tga ../../newWalls/028.tga
cp 018.tga ../../newWalls/026.tga
cp 019.tga ../../newWalls/046.tga
cp 020.tga ../../newWalls/050.tga
cp 021.tga ../../newWalls/078.tga
cp 022.tga ../../newWalls/066.tga
cp 025.tga ../../newWalls/056.tga
cp 026.tga ../../newWalls/058.tga
cp 027.tga ../../newWalls/086.tga
cp 028.tga ../../newWalls/090.tga
cp 029.tga ../../newWalls/068.tga
cp 030.tga ../../newWalls/126.tga
cp 031.tga ../../newWalls/132.tga
cp 032.tga ../../newWalls/133.tga
cp 033.tga ../../newWalls/130.tga
cp 034.tga ../../newWalls/128.tga
 
 mac wolf weapon mapping:
cp weapon00.5551 ../../../base/sprites/514.5551
cp weapon01.5551 ../../../base/sprites/515.5551
cp weapon02.5551 ../../../base/sprites/516.5551
cp weapon03.5551 ../../../base/sprites/517.5551
cp weapon04.5551 ../../../base/sprites/518.5551
cp weapon05.5551 ../../../base/sprites/519.5551
cp weapon06.5551 ../../../base/sprites/520.5551
cp weapon07.5551 ../../../base/sprites/521.5551
cp weapon08.5551 ../../../base/sprites/522.5551
cp weapon09.5551 ../../../base/sprites/523.5551
cp weapon10.5551 ../../../base/sprites/524.5551
cp weapon11.5551 ../../../base/sprites/525.5551
cp weapon12.5551 ../../../base/sprites/526.5551
cp weapon13.5551 ../../../base/sprites/527.5551
cp weapon14.5551 ../../../base/sprites/528.5551
cp weapon15.5551 ../../../base/sprites/529.5551
 
 
mac wolf sprite mapping

brown shirt corpse
cp 039.5551 ~/dev/iphone/wolf3d/base/sprites/099.5551
 
brown plant
cp 136.5551 ~/dev/iphone/wolf3d/base/sprites/013.5551
 
 */

#include "../wolfiphone.h"

currentMap_t currentMap;

vec3_t	vnull = { 0, 0, 0 };

int		iphoneFrameNum;
int		intermissionTriggerFrame;
int		slowAIFrame;

// console mode
int consoleActive;

// the native iPhone code should set the following each frame:
int	numTouches;
int	touches[5][2];	// [0] = x, [1] = y in landscape mode, raster order with y = 0 at top
float	tilt;		// -1.0 to 1.0
float	tiltPitch;

#define MAX_TILT_HISTORY 64
float	tiltHistory[MAX_TILT_HISTORY][4];
int		tiltHistoryNum;

// so we can detect button releases
int	numPrevTouches;
int prevTouches[5][2];

texture_t *numberPics[10];

char *mugshotnames[ NUM_MUGSHOTS ] =
{
"iphone/FACE1APIC.tga",
"iphone/FACE1BPIC.tga",
"iphone/FACE1CPIC.tga",

"iphone/FACE2APIC.tga",
"iphone/FACE2BPIC.tga",
"iphone/FACE2CPIC.tga",

"iphone/FACE3APIC.tga",
"iphone/FACE3BPIC.tga",
"iphone/FACE3CPIC.tga",

"iphone/FACE4APIC.tga",
"iphone/FACE4BPIC.tga",
"iphone/FACE4CPIC.tga",

"iphone/FACE5APIC.tga",
"iphone/FACE5BPIC.tga",
"iphone/FACE5CPIC.tga",

"iphone/FACE6APIC.tga",
"iphone/FACE6BPIC.tga",
"iphone/FACE6CPIC.tga",

"iphone/FACE7APIC.tga",
"iphone/FACE7BPIC.tga",
"iphone/FACE7CPIC.tga",

"iphone/FACE8APIC.tga",
"iphone/GOTGATLINGPIC.tga"		
};

int damageflash;
int bonusFrameNum;
int attackDirTime[2];

logTime_t	loggedTimes[MAX_LOGGED_TIMES];	// indexed by iphoneFrameNum

/*
 ==================
 iphoneActivateConsole
 
 ==================
 */
void iphoneActivateConsole() {
	extern float scr_conlines;
	scr_conlines = 0.5f;
	consoleActive = 1;
	
	// draw a frame immediately, because the UIKit takes
	// a long time to bring up the keyboard
	iphoneFrame();
}

/*
 ==================
 iphoneDeactivateConsole
 
 ==================
 */
void iphoneDeactivateConsole() {
	extern float scr_conlines;
	scr_conlines = 0.0f;
	consoleActive = 0;
}

/*
 ==================
 iphoneExecuteConsoleCommand
 
 ==================
 */
void iphoneExecuteCommandLine() {
	char buf[256];
	strcpy( buf, SysIPhoneGetConsoleTextField() );
	
	Com_Printf( "%s\n", buf );
	Cmd_ExecuteString( buf );
	SysIPhoneSetConsoleTextField( "" );
}



/*
 ==================
 iphoneSavePrevTouches
 
 ==================
 */
void iphoneSavePrevTouches() {
	numPrevTouches = numTouches;
	memcpy( prevTouches, touches, sizeof( prevTouches ) );
#if 0	
	// display the touch locations
	for ( int i = 0 ; i < numTouches ; i++ ) {
		int	w = 32;
		iphoneDrawPic( touches[i][0] - w/2, touches[i][1] - w/2, w, w, "iphone/diractional_02.tga" );
	}
#endif
}

/*
 ==================
 iphoneCenterText
 
 Returns the width in pixels
 ==================
 */
extern font_t *myfonts[ 1 ];
int iphoneCenterText( int x, int y, const char *str ) {
	int l = strlen( str );
	int	i;
	font_t *myfont = myfonts[0];
	int		scale;
	int		step = 10;
	
	scale = 16;
	
	x -= l * step / 2;
	
	R_Bind( myfont->texfont->texnum );
	pfglBegin( GL_QUADS );

	for ( i = 0 ; i < l ; i++, x += step ) { 
		int	row, col; 
		float frow, fcol;
		int	num = str[i];
		
		if ( num == ' ' ) {
			continue;
		}
		
		row = (num >> 4) - 2;
		col = num & 15;
		
		frow = row * myfont->hFrac;
		fcol = col * myfont->wFrac;
	
		pfglTexCoord2f( fcol, frow );							
		pfglVertex2i( x, y );
		
		pfglTexCoord2f( fcol+myfont->wFrac, frow );					
		pfglVertex2i( x+scale, y );
		
		pfglTexCoord2f( fcol+myfont->wFrac, frow+myfont->hFrac );	
		pfglVertex2i( x+scale, y+scale );
		
		pfglTexCoord2f( fcol, frow+myfont->hFrac );
		pfglVertex2i( x, y+scale );			
	}
	
	pfglEnd();
	
	return l * step;
}


/*
 ==================
 TouchDown
 
 Checks all touches against a square
 ==================
 */
int	TouchDown( int x, int y, int w, int h ) {
	int	i;
	for ( i = 0 ; i < numTouches ; i++ ) {
		if ( touches[i][0] >= x && touches[i][1] >= y
			&& touches[i][0] < x + w && touches[i][1] < y + h ) {
			return 1;
		}
	}
	return 0;
}

/*
 ==================
 TouchReleased
 
 Perform an action when released in the box.
 If not down this frame, but down the previous frame, it is released
 ==================
 */
int	TouchReleased( int x, int y, int w, int h ) {
	int	i;
	int	downPrev = 0;
	int downNow = 0;
	
	for ( i = 0 ; i < numPrevTouches ; i++ ) {
		if ( prevTouches[i][0] >= x && prevTouches[i][1] >= y
			&& prevTouches[i][0] < x + w && prevTouches[i][1] < y + h ) {
			downPrev = 1;
			break;
		}
	}
	
	// see if not down this frame
	for ( i = 0 ; i < numTouches ; i++ ) {
		if ( touches[i][0] >= x && touches[i][1] >= y
			&& touches[i][0] < x + w && touches[i][1] < y + h ) {
			downNow = 1;
			break;
		}
	}
	
	if ( !downPrev ) {
		if ( downNow ) {
			Sound_StartLocalSound( "iphone/bdown_01.wav" );
		}
		// wasn't down the previous frame
		return 0;
	}
	
	if ( downNow ) {
		// still down
		return 0;
	}
	
	if ( numTouches == numPrevTouches ) {
		// finger dragged off
		Sound_StartLocalSound( "iphone/baborted_01.wav" );
		return 0;
	}
	
	// released
	Sound_StartLocalSound( "iphone/baction_01.wav" );
	return 1;
}

/*
 ==================
 iphoneRotateForLandscape
 
 ==================
 */
void iphoneRotateForLandscape() {
	if ( revLand->value ) {
		// reverse landscape mode
		pfglRotatef( -90, 0, 0, 1 );
	} else {
		pfglRotatef( 90, 0, 0, 1 );
	}
}

/*
 ==================
 iphoneSet2D
 
 ==================
 */
void iphoneSet2D( void ) {
	pfglViewport( 0,0, 480, 320 );
	pfglMatrixMode( GL_MODELVIEW );
    pfglLoadIdentity();
	pfglDisable( GL_DEPTH_TEST );
	pfglDisable( GL_CULL_FACE );
	pfglEnable( GL_BLEND );
	pfglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
	pfglDisable( GL_ALPHA_TEST );
	pfglColor4f( 1,1,1,1 );
	
	pfglMatrixMode( GL_PROJECTION );
    pfglLoadIdentity();
	iphoneRotateForLandscape();
	pfglOrtho( 0, 480, 320, 0, -99999, 99999 );
}


/*
 ==================
 DeadBandAdjust
 
 Compresses the 0.0 - 1.0 range into deadband - 1.0
 ==================
 */
float DeadBandAdjust( float f, float deadBand ) {
	if ( f < 0 ) {
		return -DeadBandAdjust( -f, deadBand );
	}
	if ( f > 1.0 ) {
		return 1.0;
	}
	if ( f < deadBand ) {
		return 0;
	}
	return (f-deadBand) / (1.0 - deadBand);
}

/*
==================
AxisHit

Returns a -1 to 1 range

If activeFraction is less than 1.0, the range will clamp
to the limits before the edge of the box is hit.
==================
*/
float	AxisHit( hudPic_t *hud ) {
	int x = hud->x;
	int y = hud->y;
	int width = hud->width;
	int height = hud->width;
	float activeFraction = 0.8f;
	int	i;
	int isXaxis = ( hud != &huds.forwardStick );
	
	if ( hud->hudFlags & HF_DISABLED ) {
		return 0;
	}
	
	// allow the touches to go outside the indicated bounds with clamping
	x += width / 2;
	y += height / 2;
	width *= 2;
	activeFraction *= 0.5f;
	
	// if the axis is not paired with another, leave it skinnier along the axis
	if ( hud == &huds.sideStick && huds.forwardStick.x == hud->x && huds.forwardStick.y == hud->y ) {
		height *= 2;
	}
	if ( hud == &huds.turnStick && huds.forwardStick.x == hud->x && huds.forwardStick.y == hud->y ) {
		height *= 2;
	}
	if ( hud == &huds.forwardStick ) {
		// don't bother checking for a skinny forwardStick
		height *= 2;
	}

	x -= width / 2;
	y -= height / 2;
	
	for ( i = 0 ; i < numTouches ; i++ ) {
		if ( touches[i][0] >= x && touches[i][1] >= y
			&& touches[i][0] < x + width && touches[i][1] < y + height ) {
				float	f;
				if ( isXaxis ) {
					f = (float)( touches[i][0] - x ) / width * 2.0f - 1.0f;
				} else {
					f = (float)( touches[i][1] - y ) / height * 2.0f - 1.0f;
				}
				f /= activeFraction;
				if ( f > 1.0f ) {
					f = 1.0f;
				} else if ( f < -1.0f ) {
					f = -1.0f;
				}
				if ( f > -stickDeadBand->value && f < stickDeadBand->value ) {
					f = 0;
				}
				return f;
		}
	}
	return 0;
}

void iphoneTouchEvent( int _numTouches, int _touches[16] ) {
	numTouches = _numTouches;
	for ( int i = 0 ; i < numTouches ; i++ ) {
		if ( revLand->value ) {
			touches[i][0] = _touches[i*2+1];
			touches[i][1] = 319 - _touches[i*2+0];
		} else {
			touches[i][0] = 479 - _touches[i*2+1];
			touches[i][1] = _touches[i*2+0];
		}
	}
}

void iphoneCheckForLandscapeReverse() {
	static int reverseCount;
	
	// if we stay significantly negative for half a second, flip orientation
	if ( tiltPitch < -0.35 ) {
		if ( ++reverseCount > 10 ) {
			Cvar_SetValue( revLand->name, !revLand->value );
			SysIPhoneSetUIKitOrientation( revLand->value );
		}
	} else {
		reverseCount = 0;
	}
}

void iphoneTiltEvent( float *tilts ) {
	int		i;
	int		j;
	int		c;
	float	sum[3];
	static float prevTime;

	if ( revLand->value ) {
		tilts[1] = -tilts[1];
		tilts[0] = -tilts[0];
	}

	c = tiltAverages->value;
	if ( c < 1 ) {
		c = 1;
	} else if ( c > MAX_TILT_HISTORY ) {
		c = MAX_TILT_HISTORY;
	}
	
	// acc[0] - [2] are accelerometer values, ax[3] is the timestamp
	for ( i = 0 ; i < 3 ; i++ ) {
		tiltHistory[tiltHistoryNum&(MAX_TILT_HISTORY-1)][i] = tilts[i];
		sum[i] = 0;
		for ( j = 0 ; j < c ; j++ ) {
			sum[i] += tiltHistory[(tiltHistoryNum-j)&(MAX_TILT_HISTORY-1)][i];
		}
		sum[i] /= c;
	}
	// save the timestamp for analysis
	tiltHistory[tiltHistoryNum&(MAX_TILT_HISTORY-1)][3] = tilts[3] - prevTime;	
	prevTime = tilts[3];
	tiltHistoryNum++;
	
	tilt = sum[1];
	tiltPitch = sum[0];
//	Com_Printf( "%4.2f %4.2f %4.2f\n", tilts[0], tilts[1], tilts[2] );
}

void ShowTilt() {
	int	i;
	int	axis = (int)showTilt->value;
	colour4_t fillColor = { 255, 0, 0, 255 };
	colour4_t whiteColor = { 255, 255, 255, 255 };
	colour4_t nowColor = { 0, 255, 0, 255 };
	float	x;
	
	if ( axis < 0 || axis > 2 ) {
		return;
	}
	for ( i = 0 ; i < MAX_TILT_HISTORY ; i++ ) {
		x = tiltHistory[(tiltHistoryNum-1-i)&(MAX_TILT_HISTORY-1)][axis] * ( 10 / 0.018168604 );
		if ( x < 0 ) {
			R_Draw_Fill( 240 + x, i*4, -x, 4, fillColor );
		} else if ( x > 0 ) {
			R_Draw_Fill( 240, i*4, x, 4, fillColor );
		}
	}
	x = tilt * ( 10 / 0.018168604 );
	if ( x < 0 ) {
		R_Draw_Fill( 240 + x, i*4, -x, 4, nowColor );
	} else if ( x > 0 ) {
		R_Draw_Fill( 240, i*4, x, 4, nowColor );
	}
	R_Draw_Fill( 240, 0, 1, MAX_TILT_HISTORY*4, whiteColor );
}

void ShowTime() {
	int	i;
	if ( !showTime->value ) {
		return;
	}
	colour4_t frameColor = { 0, 0, 255, 255 };
	colour4_t swapColor = { 255, 0, 0, 255 };
	colour4_t activeColor = { 0, 255, 0, 255 };
	
	for ( i = 1 ; i < 30 ; i++ ) {
		logTime_t *lt = &loggedTimes[(iphoneFrameNum - i ) & (MAX_LOGGED_TIMES-1)];
		logTime_t *ltp = &loggedTimes[(iphoneFrameNum - i - 1 ) & (MAX_LOGGED_TIMES-1)];
		int	frameTime = lt->enterFrame - ltp->enterFrame;
		int	swapTime = lt->afterSwap - lt->beforeSwap;
		int	activeTime = lt->beforeSwap - lt->enterFrame;
		R_Draw_Fill( 0, i * 4, frameTime * 8, 2, frameColor );
		R_Draw_Fill( frameTime * 8 - swapTime * 8, i * 4, swapTime * 8, 2, swapColor );
		R_Draw_Fill( 0, i * 4, activeTime * 8, 2, activeColor );
	}
}


/*
==================
CreateIphoneUserCmd

Build the movement, angles, and buttons for a frame of action:

Player.position.angle
Player.cmd.buttons
Player.cmd.forwardMove
Player.cmd.sideMove
==================
*/
PRIVATE void CreateIphoneUserCmd()
{
	float	stickTurnValue;
	float	stickMoveValue;
	
	stickTurnValue = stickTurnBase->value + stickTurnScale->value * sensitivity->value;
	stickMoveValue = stickMoveBase->value + stickMoveScale->value * sensitivity->value;
	
	usercmd_t *cmd = &Player.cmd;
	memset( cmd, 0, sizeof( *cmd ) );
		   
	if ( TouchDown( huds.fire.x, huds.fire.y, huds.fire.width, huds.fire.height ) ) {
		cmd->buttons |= BUTTON_ATTACK;
	}
	if ( tiltFire->value > 0 && tiltPitch < tiltFire->value ) {
		cmd->buttons |= BUTTON_ATTACK;
	}

	// tapping the weapon issues the nextWeapon impulse
	if ( TouchReleased( 240 - 40, 320 - 80 - 64, 80, 64 ) ) {
		cmd->buttons |= BUTTON_CHANGE_WEAPON;
	}
	
	cmd->forwardmove = -stickMoveValue * AxisHit( &huds.forwardStick );
	cmd->sidemove = stickMoveValue * AxisHit( &huds.sideStick );
	Player.position.angle += -stickTurnValue * AxisHit( &huds.turnStick );

	// accelerometer tilting
	cmd->sidemove += tiltMove->value * DeadBandAdjust( tilt, tiltDeadBand->value );
	Player.position.angle -= tiltTurn->value * DeadBandAdjust( tilt, tiltDeadBand->value );
	
	// always use
	if ( iphoneFrameNum & 1 ) {
		cmd->buttons |= BUTTON_USE;
	}
}


/*
==================
iphoneHighlightPicWhenTouched

Draw transparent except when touched
=================
*/
void iphoneHighlightPicNumWhenTouched( int x, int y, int w, int h, int glTexNum ) {
	if ( TouchDown( x, y, w, h ) ) {
		pfglColor4f(1,1,1,1);
	} else {
		pfglColor4f(1,1,1,0.5);
	}
	iphoneDrawPicNum( x, y, w, h, glTexNum );
	pfglColor4f(1,1,1,1);
}

/*
 ==================
 iphoneDrawWeapon
 
 ==================
 */
void iphoneDrawWeapon( void ) {
	char name[ 32 ];
	texture_t *tex;	
	static int w = 128;
	static int h = 128;
	int x = (viddef.width - w ) >> 1;
	int y = viddef.height - 80 - h;
	int	frame;
	
	if ( gunFrame->value ) {
		// screenshots look better with the muzzle flash
		frame = Player.weapon * 4 + gunFrame->value + SPR_KNIFEREADY;
	} else {
		frame = Player.weapon * 4 + Player.weaponframe + SPR_KNIFEREADY;
	}
	
	my_snprintf( name, sizeof( name ), "%s/%d.tga", spritelocation, frame);
	tex = TM_FindTexture( name, TT_Pic );

	R_Bind( tex->texnum );

	pfglBegin( GL_QUADS );
	
	pfglTexCoord2f( 0.01f, 0.01f );	pfglVertex2i( x, y );
	pfglTexCoord2f( 0.99f, 0.01f );	pfglVertex2i( x + w, y );
	pfglTexCoord2f( 0.99f, 0.99f );	pfglVertex2i( x + w, y + h );
	pfglTexCoord2f( 0.01f, 0.99f );	pfglVertex2i( x, y + h );
	
	pfglEnd();
}

/*
 ==================
 iphoneDrawNumber
 
 x is the midpoint, y is the top
 ==================
 */
void iphoneDrawNumber( int x, int y, int number, int charWidth, int charHeight ) {	
	texture_t *tex;
	int i;
	char string[ 20 ];
	W32 length;
	float charStep = charWidth * 0.8;	// trim off extra width

	if ( number < 0 ) {
		number = 0;
	}
	my_snprintf( string, sizeof( string ), "%d", number );
	length = strlen( string );
	
	x -= length * charStep / 2;
	
	for( i = 0 ; i < length ; i++ ) {
		int digit = string[i] - '0';
		tex = numberPics[digit];
		R_Bind( tex->texnum );
		pfglBegin( GL_QUADS );
		
		pfglTexCoord2f( 0, 0 );	pfglVertex2i( x, y );
		pfglTexCoord2f( 1, 0 );	pfglVertex2i( x+charWidth, y );
		pfglTexCoord2f( 1, 1 );	pfglVertex2i( x+charWidth, y+charHeight );
		pfglTexCoord2f( 0, 1 );	pfglVertex2i( x, y+charHeight );
		
		pfglEnd();
		x += charStep;
	}
}


/*
 ==================
 iphoneDrawFace
 
 ==================
 */
void iphoneDrawFace() {
	int i;
	int w = 64;
	int h = 80;
	int x = (viddef.width - w ) >> 1;
	int y = viddef.height - h;
	const char *pic;

	// solid background
	iphoneDrawPic( 240 - 64, 320 - 80, 128, 80, "iphone/status_hud.tga" );

	// keys on the side
	if( Player.items & ITEM_KEY_1 ) {
		iphoneDrawPic( 240-64, 320-72, 32, 64, "iphone/gold_key.tga" );
	}
	
	if( Player.items & ITEM_KEY_2 ) {
		iphoneDrawPic( 240+32, 320-72, 32, 64, "iphone/silver_key.tga" );
	}
	
	
	// decide on the face
	Player.facecount += tics;
	if ( Player.face_gotgun && Player.facecount > 0 ) {
		// gotgun will set facecount to a negative number initially, go back
		// to normal face with random look after expired.
		Player.face_gotgun = false;
	}
	if( Player.facecount > US_RndT() )
	{
		Player.face_gotgun = Player.face_ouch = false;
		Player.faceframe = US_RndT() >> 6;
		if( Player.faceframe == 3 )
		{
			Player.faceframe = 0;
		}
		
		Player.facecount = 0;
	}

	if( Player.health )
	{
		if( Player.face_gotgun )
		{
			pic = mugshotnames[ 22 ];
		}
		else
		{
			int h = Player.health;
			if ( h > 100 ) {
				h = 100;
			}
			if ( h < 0 ) {
				h = 0;
			}
			pic = mugshotnames[ 3*((100-h)/16)+Player.faceframe ];
		}
	}
	else
	{
		pic = mugshotnames[ 21 ];
	}

	iphoneDrawPic( x, y, w, h, pic );
		
	// blend the right / left damage indicators on the side
	for ( i = 0 ; i < 2 ; i++ ) {
		float	f;
		if ( attackDirTime[i] == 0 ) {
			continue;
		}
		f = iphoneFrameNum - attackDirTime[i];
		if ( f > 30 ) {
			attackDirTime[i] = 0;
			continue;
		}
		if ( f < 20 ) {
			f = 1.0;
		} else {
			f = ( 30 - f ) * 0.1;
		}
		pfglColor4f( 1, 1, 1, f );
		if ( i == 0 ) {
			iphoneDrawPic( 240 - 64, 320 - 80, 40, 80, "iphone/L_damage.tga" );
		} else {
			iphoneDrawPic( 240 + 64 - 40, 320 - 80, 40, 80, "iphone/R_damage.tga" );
		}
		pfglColor4f( 1, 1, 1, 1 );
	}
	
	// draw the tiny health numbers on top of the face
	iphoneDrawNumber( 240, 304, Player.health, 16, 16 );
}

/*
 ==================
 iphoneSetNotifyText
 
 Notify text is a single centered line for "got a key", "found a secret", etc
 ==================
 */
char	notifyText[128];
int		notifyFrameNum;
void iphoneSetNotifyText( const char *str, ... ) {
	va_list		argptr;

	va_start( argptr, str );
	(void)vsnprintf( notifyText, sizeof( notifyText )-1, str, argptr );
	va_end( argptr );

	notifyFrameNum = iphoneFrameNum;
}

void iphoneDrawNotifyText() {
	if ( notifyFrameNum == 0 ) {
		return;
	}
	// display for three seconds, then fade over 0.3
	float f = iphoneFrameNum - notifyFrameNum - 80;
	if ( f < 0 ) {
		f = 1.0;
	} else {
		f = 1.0 - f * 0.1f;
		if ( f < 0 ) {
			notifyFrameNum = 0;
			return;
		}
	}
	
	pfglColor4f( 1, 1, 1, f );
	iphoneCenterText( 240, 5, notifyText );
	pfglColor4f( 1, 1, 1, 1 );
}

void iphoneStartBonusFlash() {
	bonusFrameNum = iphoneFrameNum;
}

void iphoneStartDamageFlash( int damage ) {
	damageflash += damage;
	if ( damageflash > 64 ) {
		damageflash = 64;
	}
	if ( damageflash < 10 ) {
		damageflash = 10;
	}
}

/*
 ==================
 iphoneSetAttackDirection
 
 +1 = attacked from the left
 -1 = attacked from the right
 ==================
 */
void iphoneSetAttackDirection( int dir ) {
	if ( dir > 0 ) {
		attackDirTime[0] = iphoneFrameNum;
	}
	if ( dir < 0 ) {
		attackDirTime[1] = iphoneFrameNum;
	}
}

void iphoneDrawHudControl( hudPic_t *hud ) {
	if ( hud->hudFlags & HF_DISABLED ) {
		return;
	}
	iphoneHighlightPicNumWhenTouched( hud->x, hud->y, hud->width, hud->height, hud->glTexNum );
}

int iphoneDrawHudButton( hudPic_t *hud ) {
	if ( hud->hudFlags & HF_DISABLED ) {
		return 0;
	}
	iphoneDrawHudControl( hud );
	return TouchReleased( hud->x, hud->y, hud->width, hud->height );
}

void iphoneUpdateMapView() {
#ifdef ALLOW_MAP_VIEW_HUD		
	if ( huds.mapView.hudFlags & HF_DISABLED ) {
		return;
	}
	R_Bind( huds.mapView.glTexNum );
	int playerx = Player.position.origin[0] / (float)TILEGLOBAL;
	int playery = Player.position.origin[1] / (float)TILEGLOBAL;
	
	for ( int j = 0; j < 16; j++ ) {
		int y = ( ( iphoneFrameNum << 4 )  + j ) & 63;
		int disty = playery - y;
		GLubyte row[ 64 * 4 ];
		for( int x = 0 ; x < 64; ++x ) {
			int distx = playerx - x;
			if ( distx >= -1 && distx <= 1 && disty == 0 ) {
				row[ x * 4 + 0 ] = 255;
				row[ x * 4 + 1 ] = 255;
				row[ x * 4 + 2 ] = 255;
				row[ x * 4 + 3 ] = 255;
			} else if ( disty >= -1 && disty <= 1 && distx == 0 ) {
				row[ x * 4 + 0 ] = 255;
				row[ x * 4 + 1 ] = 255;
				row[ x * 4 + 2 ] = 255;
				row[ x * 4 + 3 ] = 255;
			} else if ( r_world->tilemap[ x ][ y ] & ( WALL_TILE | PUSHWALL_TILE ) || ! r_world->tileEverVisible[ x ][ y ] ) {
				row[ x * 4 + 0 ] = 0;
				row[ x * 4 + 1 ] = 0;
				row[ x * 4 + 2 ] = 0;
				row[ x * 4 + 3 ] = 0;
			} else {
				row[ x * 4 + 0 ] = 0;
				row[ x * 4 + 1 ] = 0;
				row[ x * 4 + 2 ] = 0;
				row[ x * 4 + 3 ] = 255;
			}
		}
		pfglTexSubImage2D(GL_TEXTURE_2D, 0, 0, 63 - y, 64, 1, GL_RGBA, GL_UNSIGNED_BYTE, row );
	}
#endif	
}

void iphoneDrawMapView() {
#ifdef ALLOW_MAP_VIEW_HUD		
	iphoneDrawHudControl( &huds.mapView );
#endif
}

/*
==================
iphoneFrame

==================
*/
void iphoneFrame() {
	unsigned char blendColor[4];
	
	iphoneFrameNum++;
	loggedTimes[iphoneFrameNum&(MAX_LOGGED_TIMES-1)].enterFrame = Sys_Milliseconds();

	// check for delayed intermission trigger after boss kill
	if ( intermissionTriggerFrame > 0 && iphoneFrameNum >= intermissionTriggerFrame ) {
		iphoneStartIntermission( 0 );
	}
	
	iphoneUpdateMapView();
	
	// toggle / scroll down the console
	Client_Screen_RunConsole();

	// check for flipping the phone orientation
	iphoneCheckForLandscapeReverse();
	
	// fixed frame timing, assume we go 30hz
	tics = 2;		// wolf's global rate counter
	
	Sound_Update( vnull, vnull, vnull, vnull );
	
	if ( consoleActive ) {	
		iphoneSet2D();	

		Client_Screen_DrawConsole();	
		
		iphoneSavePrevTouches();
		SysIPhoneSwapBuffers();
		return;
	}
	if ( menuState != IPM_GAME ) {
		iphoneSet2D();
		
		iphoneDrawMenus();
	
		iphoneSavePrevTouches();
		SysIPhoneSwapBuffers();
		return;
	}

	//------------------
	// normal gameplay
	//------------------

	if( Player.playstate != ex_dead )
	{
		CreateIphoneUserCmd();
		Player.position.angle = NormalizeAngle( Player.position.angle );

		PL_Process( &Player, r_world );	// Player processing
		if ( !slowAI->value || --slowAIFrame < 0 ) {
			// slowAIframe is for slow-motion screenshot capture
			slowAIFrame += slowAI->value;
			ProcessGuards();
		}
		PushWall_Process();
		Door_ProcessDoors_e( &r_world->Doors, tics );
		
		levelstate.time += tics;
	}
	
	// clear depth buffer
	qglDepthMask( GL_TRUE );
	pfglClear( GL_DEPTH_BUFFER_BIT );

	// fill the floor and ceiling
	pfglDisable( GL_BLEND );
	pfglDisable( GL_DEPTH_TEST );
	pfglMatrixMode( GL_MODELVIEW );
    pfglLoadIdentity();
	pfglMatrixMode( GL_PROJECTION );
    pfglLoadIdentity();
	iphoneRotateForLandscape();
	pfglOrtho( 0, 480, 320, 0, -99999, 99999 );
	R_Draw_Fill( 0, 0, viddef.width, viddef.height >> 1, r_world->ceilingColour );
	R_Draw_Fill( 0, viddef.height >> 1, viddef.width, viddef.height, r_world->floorColour );
	
	// setup to draw the 3D world 
	float cur_x_fov, cur_y_fov; // x & y field of view (in degrees)
	float ratio; // viewport width/height
	ratio = (float) viddef.width / (float)viddef.height; // FIXME: move somewhere
	cur_x_fov = 75;
	cur_y_fov = CalcFov( cur_x_fov, (float)viddef.width, (float)viddef.height );
	
	pfglMatrixMode( GL_PROJECTION );
	pfglLoadIdentity();
	iphoneRotateForLandscape();
	MYgluPerspective( cur_y_fov - 2.0f, ratio, 0.2f, 64.0f );	// tweak fov in to avoid edge tile clips
	pfglMatrixMode( GL_MODELVIEW );
	pfglLoadIdentity();
	
	pfglRotatef( (float)(90 - FINE2DEG( Player.position.angle )), 0, 1, 0 );
	pfglTranslatef( -Player.position.origin[ 0 ] / FLOATTILE, 0, Player.position.origin[ 1 ] / FLOATTILE );

	pfglCullFace( GL_BACK );
	
	pfglEnable( GL_DEPTH_TEST );
	pfglEnable( GL_CULL_FACE );
	pfglDisable( GL_BLEND );
	pfglDisable( GL_ALPHA_TEST );
	
	// find and draw all the walls
	R_RayCast( Player.position, r_world );

	// blend in the sprites
	R_DrawSprites();

	// draw 2D overlays
	iphoneSet2D();	
	
	// do a full screen blend for damage, death, and bonus pickup
	if( Player.playstate == ex_dead ) {
		static int deathFadeIntensity;
		blendColor[0] = 255;
		blendColor[1] = 0;
		blendColor[2] = 0;
		blendColor[3] = deathFadeIntensity;
		deathFadeIntensity += 2;
		if( deathFadeIntensity >= 240 ) {
			deathFadeIntensity = 0;
			PL_NewGame( &Player );
			iphoneStartMap( currentMap.episode, currentMap.map, currentMap.skill );
		}
	} else {
		iphoneDrawWeapon();
		if( damageflash ) {
			blendColor[0] = 255;
			blendColor[1] = 0;
			blendColor[2] = 0;
			blendColor[3] = damageflash >= 64 ? 255 : damageflash * 4;
			
			if( (damageflash -= 1) < 0 ) {
				damageflash = 0;
			}
		} else if ( bonusFrameNum ) {
			float	f = ( iphoneFrameNum - bonusFrameNum ) * 0.1;
			if ( f > 1.0 ) {
				bonusFrameNum = 0;
			} else {
				blendColor[0] = 255;
				blendColor[1] = 255;
				blendColor[2] = 128;
				blendColor[3] = ( 1.0 - f ) * 64;
			}
		}
	}
	if ( blendColor[3] != 0 && blends->value != 0 ) {
		R_DrawBox( 0, 0, viddef.width, viddef.height, *(int *)blendColor );
	}
	
	iphoneDrawNotifyText();

	iphoneDrawMapView();

	iphoneDrawFace();	

	iphoneDrawNumber( huds.ammo.x + huds.ammo.width / 2, huds.ammo.y, Player.ammo[AMMO_BULLETS], 48, 48 );
	
	if ( hideControls->value != 1 ) {
		iphoneDrawHudControl( &huds.forwardStick );
		iphoneDrawHudControl( &huds.sideStick );
		iphoneDrawHudControl( &huds.turnStick );
		iphoneDrawHudControl( &huds.fire );
	}
	
	if ( iphoneDrawHudButton( &huds.menu ) ) {
		menuState = IPM_MAIN;
	}
	if ( iphoneDrawHudButton( &huds.map ) ) {
		iphoneOpenAutomap();
	}
		
	Client_Screen_DrawConsole();	

	ShowTilt();		// debug tool
	ShowTime();		// debug tool
	
	iphoneSavePrevTouches();
	
	SysIPhoneSwapBuffers();	// do the swapbuffers
}
