/*

				Show Your Face
				
*/

#include "netfone.h"

char faceFileName[MAX_PATH] = "";		// Face image file name
int faceShow = TRUE;					// Show faces ?

HFILE faceFile = HFILE_ERROR; 			// Face image file handle

//	CLOSEFACEFILE  --  Close face file (if open)

void closeFaceFile(void)
{
	if (faceFile != HFILE_ERROR) {
		_lclose(faceFile);
		faceFile = HFILE_ERROR;
	} 
}

/*	OPENFACEFILE  --  Open face file and validate it's
					  in a compatible format.  */

int openFaceFile(HWND hwnd)
{
	closeFaceFile();
	if (faceFileName[0] != 0) {
		faceFile = _lopen(faceFileName, OF_READ);
		if (faceFile == HFILE_ERROR) {
	        MsgBox(hwnd, MB_ICONSTOP | MB_OK, Format(65), (LPSTR) faceFileName);
		} else {
    		BITMAPFILEHEADER bfh;
    		BITMAPINFOHEADER bmi;

			_lread(faceFile, &bfh, sizeof bfh);
			if (strncmp((char *) &bfh, "GIF87a", 6) == 0 ||
				strncmp((char *) &bfh, "GIF89a", 6) == 0) {
				return TRUE;
			}
			if (strncmp((char *) &bfh, "BM", 2) == 0) { 
    			_lread(faceFile, &bmi, sizeof(BITMAPINFOHEADER));
    			if (bmi.biBitCount == 8 && bmi.biCompression == BI_RGB &&
    				bmi.biClrUsed <= 256 && bmi.biPlanes == 1) {
    				return TRUE;
    			}
    		}
    		MsgBox(hwnd, MB_ICONSTOP | MB_OK, Format(66), (LPSTR) faceFileName);
    		closeFaceFile();
		}
		return FALSE;	
	}
	return TRUE;
}

/*	PROCESSFACEREQUEST  --  Process a face request packet and place
							the reply in the same packet.  */
							
void processFaceRequest(soundbuf *d)
{
	long l;

	// Request for face data

	if (faceFile != HFILE_ERROR) {
		_llseek(faceFile, d->buffer.buffer_len, 0);
		*((long *) d->buffer.buffer_val) = htonl(d->buffer.buffer_len);
		l = _lread(faceFile, d->buffer.buffer_val + sizeof(long),
				512 - (sizeof(long) + (sizeof(soundbuf) - BUFL)));
		d->compression = fProtocol | fFaceData | faceReply;
		l += sizeof(long);
	} else {
		// No face file.  Shut down requestor.
		d->compression = fProtocol | fFaceData | faceLess;
		l = 0;
	}
#ifdef TRACE_FACE	
	{
		char s[132];
		
		wsprintf(s, "Face data request for %ld, returned %ld\r\n",
			ntohl(*((long *) d->buffer.buffer_val)), l);
		OutputDebugString(s); 
	}
#endif	
	d->buffer.buffer_len = l;
}							

//	FACEDLGPROC  --  Face dialogue procedure

BOOL CALLBACK faceDlgProc(HWND hwnd, UINT nMessage,
							WPARAM wParam, LPARAM lParam)
{
	switch (nMessage) {
    	case WM_INITDIALOG:
			SetDlgItemText(hwnd, IDC_FA_FILENAME, faceFileName);
			CheckDlgButton(hwnd, IDC_FA_SHOW, faceShow); 
	    	return TRUE;
	    	
	    case WM_CLOSE:
	    	DestroyWindow(hwnd);
	    	return TRUE;

        case WM_COMMAND:
        	switch ((short) WM_COMMAND_ID(wParam)) {
        		case IDC_FA_CLEAR:
        			SetDlgItemText(hwnd, IDC_FA_FILENAME, "");
        			break;
        	
                case IDC_FA_BROWSE:
			     	{
			     		OPENFILENAME ofn;
			     		char szString[MAX_PATH];
			
			            memset(&ofn, 0, sizeof(ofn));
						ofn.lStructSize = sizeof(OPENFILENAME);
						ofn.hwndOwner = hwnd;
						ofn.lpstrFilter = rfilter(IDS_T_FACE_FILE_FILTER);
						ofn.lpstrCustomFilter = NULL;
						strcpy(szString, faceFileName);
						ofn.lpstrFile = (LPSTR) szString;
						ofn.nMaxFile = sizeof(szString);
						ofn.lpstrInitialDir = NULL;
						ofn.lpstrTitle = rstring(IDS_T_FACE_OPEN_TITLE);
						ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_SHOWHELP;
						fileHelpKey = rstring(IDS_HELP_FACE);
						if (GetOpenFileName((LPOPENFILENAME) &ofn)) {
							SetDlgItemText(hwnd, IDC_FA_FILENAME, szString);
						}
					}
					break;
        	
		    	case IDOK:
		    		{
		    			char sff[MAX_PATH];
		    			
		    			strcpy(sff, faceFileName);
			    		GetDlgItemText(hwnd, IDC_FA_FILENAME,
			                				faceFileName, sizeof faceFileName);
			            if (!openFaceFile(hwnd)) {
			            	/*	Don't let user exit until a valid (or no)
			            		face file is specified.  */
			            	strcpy(faceFileName, sff);
			            	break;
			            }
			    		faceShow = IsDlgButtonChecked(hwnd, IDC_FA_SHOW);
		                EndDialog(hwnd, TRUE);
		            }
                    break;

                case IDCANCEL:
                    EndDialog(hwnd, FALSE);
                    break;
		        	
                case ID_HELP:
                	displayHelpTopic(IDS_HELP_FACE);
                	break;
		    }
        	return FALSE;
        	
        default:
        	if (nMessage == fileOpenHelpButton && fileHelpKey != NULL) {
            	displayHelpTopicString(fileHelpKey);
        	}
        	break;	    	
    }
    return FALSE;
}

//	FACEDIALOGUE  --  Face configuration dialogue

int faceDialogue(HWND hWndParent)
{
    int result;

    result = DialogBox(hInst, MAKEINTRESOURCE(IDD_FACE), hWndParent, faceDlgProc);
    return result;
}

/*	PROCESSFACEDATA  --  Process a buffer-full of face image data
						 received for a connection.  */
						 
void processFaceData(HWND hwnd, LPCLIENT_DATA c, soundbuf *d)
{
	if (d->compression & faceReply) {

		// Face data packet received from remote server
		
		if (d->buffer.buffer_len > sizeof(long)) {
			long lp =  ntohl(*((long *) d->buffer.buffer_val));
			BITMAPFILEHEADER *bfh;

			if (lp == c->face_address) {
				if (lp == 0) {
					
					if (c->face_bmp != NULL) {
						GlobalFreePtr(c->face_bmp);
					}
					c->face_bmp = NULL;
					bfh = (BITMAPFILEHEADER *) (d->buffer.buffer_val + sizeof(long));
					if (strncmp((LPSTR) &bfh->bfType, "GIF87a", 6) == 0 ||
						strncmp((LPSTR) &bfh->bfType, "GIF89a", 6) == 0) {
						/* GIF files don't tell us up-front how long they're
						   going to be.  We initially allocate a 16K block (big
						   enough to hold the majority of face GIFs), then expand
						   it in increments of 16K if more data than that arrives. */
						   
						c->face_file_length = 16384L;
						c->face_is_gif = TRUE;						
					} else {
						if (strncmp((LPSTR) &bfh->bfType, "BM", 2) != 0) {
#ifdef TRACE_FACE
							char s[132];
								
		                    wsprintf(s, "Face image is not a Windows bitmap or GIF file\r\n");
							OutputDebugString(s);
#endif
						}
						if ((DWORD) (d->buffer.buffer_len - sizeof(long)) > bfh->bfSize) {
#ifdef TRACE_FACE
							char s[132];
								
		                    wsprintf(s, "Bogus size %ld in face bitmap\r\n", bfh->bfSize);
							OutputDebugString(s);
#endif
							c->face_stat = FSabandoned;
							return;
						}
						c->face_file_length = bfh->bfSize;
						c->face_is_gif = FALSE; 
					}
					c->face_bmp = GlobalAllocPtr(GPTR, c->face_file_length);
					if (c->face_bmp == NULL) { 
#ifdef TRACE_FACE
						char s[132];
							
	                    wsprintf(s, "Cannot allocate %ld bytes for face bitmap\r\n", c->face_file_length);
						OutputDebugString(s);
#endif
						c->face_stat = FSabandoned;
						return;						
					}
#ifdef TRACE_FACE
					{
						char s[132];
							
	                    wsprintf(s, "Receiving %ld byte face bitmap\r\n", c->face_file_length);
						OutputDebugString(s);
					}	
#endif
					memcpy(c->face_bmp, d->buffer.buffer_val + sizeof(long),
						(int) d->buffer.buffer_len - sizeof(long));
					c->face_address += d->buffer.buffer_len - sizeof(long);
					c->face_stat = FSreply;
					c->face_retry = 0;					
	                return;
				}
				bfh = (BITMAPFILEHEADER *) c->face_bmp;
				
				if ((c->face_address + (d->buffer.buffer_len - sizeof(long))) >
					(unsigned long) (c->face_file_length)) {
					if (c->face_is_gif) {
#ifdef TRACE_FACE
						char s[132];
									
	                    wsprintf(s, "Expanding GIF buffer to %ld bytes.\r\n",
	                    	c->face_file_length + 16384L);
						OutputDebugString(s);
#endif						
						c->face_bmp = GlobalReAllocPtr(c->face_bmp, c->face_file_length +
							16384L, 0);
						if (c->face_bmp == NULL) {
							//	Couldn't expand face bitmap buffer
							c->face_stat = FSabandoned;
#ifdef TRACE_FACE
							{
								char s[132];
										
								wsprintf(s, "Out of memory trying to expand GIF buffer to %ld bytes.\r\n",
		                    		c->face_file_length);
								OutputDebugString(s);
							}
#endif
							return;
						}
						c->face_file_length += 16384L; 
					} else {
#ifdef TRACE_FACE
						char s[132];
								
	                    wsprintf(s, "Address %ld plus buffer length %ld overflows %ld byte bitmap\r\n",
	                    	c->face_address, d->buffer.buffer_len - sizeof(long), bfh->bfSize);
						OutputDebugString(s);
#endif
						GlobalFreePtr(c->face_bmp);
						c->face_bmp = NULL;
						c->face_stat = FSabandoned;
						return;
					}						
				} 
#ifdef TRACE_FACE
				{ 	char s[132];
					
                    wsprintf(s, "Storing %ld bytes at %ld in face bitmap\r\n",
						d->buffer.buffer_len - sizeof(long), lp);
					OutputDebugString(s);
				}
#endif
				memcpy(c->face_bmp + c->face_address,
					d->buffer.buffer_val + sizeof(long),
					(int) (d->buffer.buffer_len - sizeof(long)));
				c->face_address += d->buffer.buffer_len - sizeof(long);
				/* Timeout will make next request after the
				   configured interval. */
				c->face_stat = FSreply;
				c->face_retry = 0;
			} else {
#ifdef TRACE_FACE
				{	char s[132];
					
                    wsprintf(s, "Discarded %ld bytes for %ld in face bitmap, expected data for %ld\r\n",
						d->buffer.buffer_len - sizeof(long),
						lp, c->face_address);
					OutputDebugString(s);
				}
#endif					
			}
		} else {
			BITMAPINFOHEADER *bmi;
			int bx, by;
		    RECT cr, wr;

#ifdef TRACE_FACE
			{	char s[132];
					
                wsprintf(s, "Face bitmap complete\r\n");
				OutputDebugString(s);
			}
#endif

			/*	If the file we received is in GIF format, convert it
				to a BMP file. */
				
			if (c->face_is_gif && (c->face_bmp != NULL)) {
				LPBYTE cbmp = GIFtoBMP((LPBYTE) c->face_bmp, 1);
				
				GlobalFreePtr(c->face_bmp);
				if (cbmp != NULL) {
					c->face_bmp = (LPSTR) cbmp;
				} else {
					c->face_bmp = NULL;
					c->face_stat = FSabandoned;
					return;
				}
			}

      /* Oops, Mr. Walker forgot to check for NULL face pointer here.  Thank God for
         debuggers. :)  -BCW 03/22/1998 */
      if(c->face_bmp == NULL)
      {
#ifdef TRACE_FACE
				OutputDebugString("Face bitmap: NULL pointer, discarded.\r\n");				
#endif
				c->face_stat = FSabandoned;
				return;
      }

      /*	Now let's perform some rudimentary verification
				of the format of this bitmap and ditch it if it's
				bogus.  */
				
            bmi = (BITMAPINFOHEADER *) (c->face_bmp + sizeof(BITMAPFILEHEADER));
            if (bmi->biPlanes != 1 || bmi->biBitCount != 8 ||
            	bmi->biCompression != BI_RGB || bmi->biClrUsed > 256) {
#ifdef TRACE_FACE
				OutputDebugString("Face bitmap: invalid format, discarded.\r\n");				
#endif
				GlobalFreePtr(c->face_bmp);
				c->face_bmp = NULL;
				c->face_stat = FSabandoned;
				return;
	        }
            
			c->face_stat = FScomplete;
			
			//	Resize window so that client area fits bitmap
            			
		    GetWindowRect(hwnd, &wr);
		    GetClientRect(hwnd, &cr);
                        
			bx = (int) bmi->biWidth;
			by = (int) bmi->biHeight;
			if (bx != cr.right || by != cr.bottom) {
				int bw = bx + ((wr.right - wr.left) - cr.right),
					bh = by + ((wr.bottom - wr.top) - cr.bottom);
            					
				SetWindowPos(hwnd, HWND_TOP,
					0, 0, bw, bh, SWP_NOMOVE | SWP_NOZORDER);	
		    	GetClientRect(hwnd, &cr);
#ifdef TRACE_FACE 
				{	char s[132];
							
					wsprintf(s, "Resizing connection window to %dx%d (W=%dx%d)\r\n",
						bx, by, bw, bh);	
					OutputDebugString("Resizing connection window.\r\n");
				}
#endif												    	
			}
			InvalidateRect(hwnd, NULL, FALSE);
		}		
	} else if (d->compression & faceLess) {
		if (c->face_bmp != NULL) {
			GlobalFreePtr(c->face_bmp);
			c->face_bmp = NULL;
		}		
		c->face_stat = FSabandoned;
#ifdef TRACE_FACE
		{	char s[MAX_HOST + 80];
					
            wsprintf(s, "No face image available from %s\r\n", c->szHost);
			OutputDebugString(s);
		}
#endif					
	}
}						 
					
