#define		STRICT
#include	<windows.h>
#include	<commdlg.h>
#include	<tchar.h>
#include	"conslib.h"

TCHAR		AppName[] = TEXT("Img2gray") ;

typedef unsigned char	uchar ;

#define		COLUMNS		512
#define		LINES		243
#define		IMAGESIZE	((long) COLUMNS * (long) LINES)
#define		LEFT_MARGIN	2
#define		RIGHT_MARGIN	10
#define		TOP_MARGIN	1
#define		BOTTOM_MARGIN	1
#define		NET_COLUMNS	(COLUMNS - LEFT_MARGIN - RIGHT_MARGIN)
#define		NET_LINES	(LINES - TOP_MARGIN - BOTTOM_MARGIN)
#define		NET_PIXELS	((long) NET_COLUMNS * (long) NET_LINES)

#define		CMT_HEADER_SIZE		128
#define		IMG_THUMBNAIL_SIZE	5120
#define		CAMERA_HEADER_SIZE	512

#define		BRIGHTNESS	0

int		Ftype = 0 ;
int		F_mac = 0 ;
int		Brightness = BRIGHTNESS ;
int		F_small = 0 ;
int		F_sharpen = 0 ;
TCHAR	g_szImgFn[ MAX_PATH ] = TEXT("") ;

#define		BMP_HEADER_SIZE	54
#define		BMP_DATA_POS	(54+256*4)

unsigned char	Buf2[ COLUMNS * 2 ] ;
unsigned char	BmpBuf[ 512 ] ;
unsigned char	LineBuf[ 3 ][ 512 ] ;

void
seek_ccd( HANDLE hfile, int line )
{
	long	pos ;

	if ( !Ftype ) {
		pos = (long) (IMG_THUMBNAIL_SIZE + CAMERA_HEADER_SIZE) ;
	} else {
		pos = (long) (CMT_HEADER_SIZE + CAMERA_HEADER_SIZE) ;
	}
	if ( F_mac ) {
		pos += 128L ;
	}
	pos += (long) line * (long) COLUMNS ;
	SetFilePointer( hfile, pos, NULL, FILE_BEGIN ) ;
}

unsigned char	bmpGS332x241[] = {
	0x42, 0x4D, 0xC2, 0x3C, 0x01, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x36, 0x04, 0x00, 0x00, 0x28, 0x00,
	0x00, 0x00, 0x4C, 0x01, 0x00, 0x00, 0xF1, 0x00,
	0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x8C, 0x38, 0x01, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
	0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
} ;

unsigned char	bmpGS500x361[] = {
	0x42, 0x4D, 0x4A, 0xC5, 0x02, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x36, 0x04, 0x00, 0x00, 0x28, 0x00,
	0x00, 0x00, 0xF4, 0x01, 0x00, 0x00, 0x69, 0x01,
	0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x14, 0xC1, 0x02, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
	0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
} ;

#if 0
unsigned char	bmpGS250x241[] = {
    0x42, 0x4D, 0x72, 0xF1, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x36, 0x04, 0x00, 0x00, 0x28, 0x00,
    0x00, 0x00, 0xFA, 0x00, 0x00, 0x00, 0xF1, 0x00,
    0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x3C, 0xED, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
    0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
} ;

unsigned char	bmpGS500x241[] = {
    0x42, 0x4D, 0xEA, 0xDA, 0x01, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x36, 0x04, 0x00, 0x00, 0x28, 0x00,
    0x00, 0x00, 0xF4, 0x01, 0x00, 0x00, 0xF1, 0x00,
    0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00,
    0x00, 0x00, 0xB4, 0xD6, 0x00, 0x01, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
    0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
} ;
#endif

long	BmpHeaderPos = 0L ;

void
write_gs_palette( HANDLE hfile )
{
	int				i ;
	DWORD			nWrite ;
	unsigned char	buf[ 4 ] ;

	buf[3] = 0 ;
	for ( i = 0 ; i < 256 ; i ++ ) {
		buf[0] = buf[1] = buf[2] = i ;
		WriteFile( hfile, buf, sizeof buf, &nWrite, NULL ) ;
	}
	BmpHeaderPos = SetFilePointer( hfile, 0, NULL, FILE_CURRENT ) ;
}

uchar	BmpLine1[512] ;
uchar	BmpLine2[512] ;
uchar	BmpLine3[512] ;
int	BmpWidth = 1 ;
long	BmpPos = 0L ;

void
init_bmp_pos( int cols, int lines )
{
	BmpWidth = cols ;
	BmpPos = BmpHeaderPos + (long) cols * (long) lines ;
}

void
data_two_third( uchar s1, uchar s2, uchar *data1, uchar *data2 )
{
	int		data, tmp, d1, d2 ;

	/* (5/6)up + (1/6)down */
	data  = s1 ;
	data *= 5 ;
	data /= 6 ;
	d1 = data ;
	tmp   = s2 ;
	tmp  /= 6 ;
	d2 = tmp ;
	data += tmp ;
	if ( data < 0 ) {
		data = 0 ;
	} else if ( data > 255 ) {
		data = 255 ;
	}
	*data1 = data ;
	/* (1/6)up + (5/6)down */
	data = s1 - d1 + s2 - d2 ;
	if ( data < 0 ) {
		data = 0 ;
	} else if ( data > 255 ) {
		data = 255 ;
	}
	*data2 = data ;
}

void
data_one_second( uchar s1, uchar s2, uchar *data1 )
{
	int		data ;

	/* (1/2)up + (1/2)down */
	data  = s1 ;
	data += s2 ;
	data /= 2 ;
	if ( data < 0 ) {
		data = 0 ;
	} else if ( data > 255 ) {
		data = 255 ;
	}
	*data1 = data ;
}

void
write_bmp( HANDLE hfile, unsigned char *buf, int size )
{
	DWORD	nWrite ;

	static int	mode = 0 ;
	uchar	*sp1, *sp2, *dp1, *dp2, s1, s2, d1, d2 ;
	int		count = size ;

	if ( !mode ) {
		mode ++ ;
		memcpy( BmpLine1, buf, size ) ;
	} else if ( mode == 1 ) {
		mode ++ ;
		sp1 = BmpLine1, sp2 = buf ;
		dp1 = BmpLine2, dp2 = BmpLine3 ;
		do {
			s1 = *sp1++, s2 = *sp2++ ;
			data_two_third( s1, s2, &d1, &d2 ) ;
			*dp1++ = d1 ;
			*dp2++ = d2 ;
		} while ( -- count ) ;
		BmpPos -= (long) (BmpWidth * 2) ;
		SetFilePointer( hfile, BmpPos, NULL, FILE_BEGIN ) ;
		WriteFile( hfile, BmpLine3, BmpWidth, &nWrite, NULL ) ;
		WriteFile( hfile, BmpLine2, BmpWidth, &nWrite, NULL ) ;
		memcpy( BmpLine1, buf, size ) ;
	} else if ( mode == 2 ) {
		mode = 1 ;
		sp1 = BmpLine1, sp2 = buf ;
		dp1 = BmpLine2 ;
		do {
			s1 = *sp1++, s2 = *sp2++ ;
			data_one_second( s1, s2, &d1 ) ;
			*dp1++ = d1 ;
		} while ( -- count ) ;
		BmpPos -= (long) BmpWidth ;
		SetFilePointer( hfile, BmpPos, NULL, FILE_BEGIN ) ;
		WriteFile( hfile, BmpLine2, BmpWidth, &nWrite, NULL ) ;
		memcpy( BmpLine1, buf, size ) ;
	}
}

void
flush_bmp( HANDLE hfile, int size )
{
	DWORD	nWrite ;

	BmpPos = BmpHeaderPos ;
	SetFilePointer( hfile, BmpPos, NULL, FILE_BEGIN ) ;
	WriteFile( hfile, BmpLine1, size, &nWrite, NULL ) ;
}

void
ccd_three_second( const uchar *src, uchar *dst, int srcsize )
{
	int		count = srcsize / 3, data, tmp ;
	uchar	s1, s2, s3, s4 ;

	do {
		s1 = *src++, s2 = *src++, s3 = *src++, s4 = *src ;
		data  = s1 ;
		data += s2 ;
		data /= 2 ;
		*dst++ = data ;
		data  = s2 ;
		data /= 4 ;
		tmp   = s3 ;
		tmp  /= 2 ;
		data += tmp ;
		tmp   = s4 ;
		tmp  /= 4 ;
		data += tmp ;
		*dst++ = data ;
	} while ( -- count ) ;
}

void
sharpen_image( uchar *up, uchar *cur, uchar *down, uchar *dst, int sz )
{
	int		count = sz - 2, data, tmp ;
	uchar	*left = cur, *right = cur + 2 ;

	*dst++ = *cur++ ;
	do {
		tmp   = *cur++ ;
		data  = tmp ;
		data += data ;
		data += data ;
		data += tmp ;
		data -= *up++ ;
		data -= *down++ ;
		data -= *left++ ;
		data -= *right++ ;
		if ( data < 0 ) {
			data = 0 ;
		} else if ( data > 255 ) {
			data = 255 ;
		}
		*dst++ = data ;
	} while ( -- count ) ;
	*dst = *cur ;
}

void
ccd_to_small( HANDLE hfileIn, HANDLE hfileOut )
{
	int			i ;
	DWORD		nRead, nWrite ;
	unsigned char	ccdbuf[ COLUMNS ] ;
	int			width = NET_COLUMNS / 3 * 2 ;
	int			write_width = (width + 3) / 4 * 4 ;
	long		pos ;

	WriteFile( hfileOut, bmpGS332x241, BMP_HEADER_SIZE, &nWrite, NULL ) ;
	write_gs_palette( hfileOut ) ;
	pos  = SetFilePointer( hfileOut, 0, NULL, FILE_CURRENT ) ;
	pos += (long) NET_LINES * (long) write_width ;

	/* Get three lines from top */
	seek_ccd( hfileIn, TOP_MARGIN - 1 ) ;
	for ( i = 0 ; i < 3 ; i ++ ) {
		ReadFile( hfileIn, ccdbuf, sizeof (ccdbuf), &nRead, NULL ) ;
		memset( LineBuf[i], 0, sizeof LineBuf[i] ) ;
		ccd_three_second( ccdbuf, LineBuf[i], NET_COLUMNS ) ;
	}
	for ( i = 0 ; i < NET_LINES ; i ++ ) {
		if ( F_sharpen ) {
			/* Sharpen the line */
			sharpen_image( LineBuf[0], LineBuf[1], LineBuf[2],
				   BmpBuf, width ) ;
		} else {
			memcpy( BmpBuf, LineBuf[1], width ) ;
		}
		/* Write to the file */
		pos -= write_width ;
		SetFilePointer( hfileOut, pos, NULL, FILE_BEGIN ) ;
		WriteFile( hfileOut, BmpBuf, write_width, &nWrite, NULL ) ;
		/* Scroll up */
		memcpy( LineBuf[0], LineBuf[1], width ) ;
		memcpy( LineBuf[1], LineBuf[2], width ) ;
		/* Get next line */
		ReadFile( hfileIn, ccdbuf, sizeof (ccdbuf), &nRead, NULL ) ;
		memset( LineBuf[2], 0, sizeof LineBuf[2] ) ;
		ccd_three_second( ccdbuf, LineBuf[2], NET_COLUMNS ) ;
	}
}

void
ccd_neiboradd( const unsigned char *src, unsigned char *dst, int srcsize )
{
	int		data ;
	const unsigned char	*p2 = src + 1 ;

	do {
		data  = (unsigned) (*src++) ;
		data += (unsigned) (*p2++) ;
		data /= 2 ;
		if ( Brightness != BRIGHTNESS ) {
			data += Brightness ;
			if ( data < 0 ) {
			data = 0 ;
			} else if ( data > 255 ) {
			data = 255 ;
			}
		}
		*dst++ = data ;
	} while ( -- srcsize ) ;
}

void
ccd_to_large( HANDLE hfileIn, HANDLE hfileOut )
{
	DWORD		nRead, nWrite ;
	int			i, width = NET_COLUMNS ;
	unsigned char	ccdbuf[ COLUMNS ] ;

	WriteFile( hfileOut, bmpGS500x361, BMP_HEADER_SIZE, &nWrite, NULL ) ;
	write_gs_palette( hfileOut ) ;
	init_bmp_pos( (NET_COLUMNS + 3) / 4 * 4, NET_LINES * 3 / 2 ) ;
	/* Get three lines from top */
	seek_ccd( hfileIn, TOP_MARGIN - 1 ) ;
	for ( i = 0 ; i < 3 ; i ++ ) {
		ReadFile( hfileIn, ccdbuf, sizeof (ccdbuf), &nRead, NULL ) ;
		memset( LineBuf[i], 0, sizeof LineBuf[i] ) ;
		ccd_neiboradd( &ccdbuf[ LEFT_MARGIN - 1 ], LineBuf[i], width ) ;
	}
	for ( i = 0 ; i < NET_LINES ; i ++ ) {
		if ( F_sharpen ) {
			/* Sharpen the line */
			sharpen_image( LineBuf[0], LineBuf[1], LineBuf[2],
				   BmpBuf, width ) ;
		} else {
			memcpy( BmpBuf, LineBuf[1], width ) ;
		}
		/* Write to the file */
		write_bmp( hfileOut, BmpBuf, width ) ;
		/* Scroll up */
		memcpy( LineBuf[0], LineBuf[1], width ) ;
		memcpy( LineBuf[1], LineBuf[2], width ) ;
		/* Get next line */
		ReadFile( hfileIn, ccdbuf, sizeof (ccdbuf), &nRead, NULL ) ;
		memset( LineBuf[2], 0, sizeof LineBuf[2] ) ;
		ccd_neiboradd( &ccdbuf[ LEFT_MARGIN - 1 ], LineBuf[2], width ) ;
	}
	flush_bmp( hfileOut, width ) ;
}

void
usage( void )
{
	TCHAR	szMessageBuf[ 512 ], *ptr ;

	ptr = szMessageBuf ;
	wsprintf( ptr,
			  TEXT("usage: %s [options] img_file [bmp_file]\n"), AppName ) ;
	ptr += _tcslen( ptr ) ;
	wsprintf( ptr,
			  TEXT("\nOptions:\n") ) ;
	ptr += _tcslen( ptr ) ;
	wsprintf( ptr,
			  TEXT("\t -v \t (v)erbose flag\n") ) ;
	ptr += _tcslen( ptr ) ;
	wsprintf( ptr,
			  TEXT("\t -s \t generate (s)mall image\n") ) ;
	ptr += _tcslen( ptr ) ;
	wsprintf( ptr,
			  TEXT("\t -B??? \t Brightness(-255..255 default=%d)\n"), Brightness ) ;
	ptr += _tcslen( ptr ) ;
	wsprintf( ptr,
			  TEXT("\t -S \t (S)harpen image\n") ) ;
	ptr += _tcslen( ptr ) ;
	wsprintf( ptr,
			  TEXT("\t -m \t use (m)ac-Binary for input\n") ) ;
	ptr += _tcslen( ptr ) ;
	MessageBox( NULL, szMessageBuf, AppName, MB_OK|MB_ICONINFORMATION ) ;
}

BOOL
GetOption( LPCTSTR szArgs )
{
	TCHAR		c ;
	DWORD		len ;
	LPCTSTR		str = szArgs ;

	if ( !szArgs && !*szArgs ) {
		return FALSE ;
	}
	/* 先頭の空白を除去する */
	while ( (c = *str++) == TEXT(' ') ) {
	}
	/* オプションを解析する */
	while ( c ) {
		if ( c != TEXT(' ') ) {
			break ;
		} else if ( c == TEXT('/') ) {
			c = *str++ ;
			if ( c == TEXT('s') ) {
				F_small = 1 ;
			} else if ( c == TEXT('B') ) {
				Brightness = _ttol( str ) ;
				while ( *str ) {
					if ( *str == TEXT(' ') ) {
						break ;
					}
					str ++ ;
				}
			} else if ( c == TEXT('S') ) {
				F_sharpen = 1 ;
			} else if ( c == TEXT('m') ) {
				F_mac = 1 ;
			} else if ( !c ) {
				break ;
			}
		}
		c = *str++ ;
	}
	/* 前後のダブルクオートを除去する */
	if ( c == TEXT('"') ) {
		c = *str++ ;
	}
	if ( c ) {
		_tcscpy( g_szImgFn, str ) ;
		len = _tcslen( g_szImgFn ) ;
		if ( len && g_szImgFn[ len - 1 ] == '"' ) {
			g_szImgFn[ len - 1 ] = 0 ;
		}
	}
	return TRUE ;
}

BOOL
OpenDialog( HINSTANCE hinst, LPTSTR szPath )
{
	OPENFILENAME	ofn ;
	TCHAR			buf1[ MAX_PATH ] ;

	memset( &ofn, 0, sizeof ofn ) ;
	memset( buf1, 0, sizeof buf1 ) ;
	ofn.lStructSize = sizeof ofn ;
    ofn.hwndOwner = NULL ;
	ofn.hInstance = hinst ;
	ofn.lpstrFilter = TEXT("Image Files (*.cmt;*.img;*.dat)\0*.cmt;*.img;*.dat\0All Files (*.*)\0*.*\0\0") ;
	ofn.lpstrDefExt = TEXT("CMT") ;
	ofn.lpstrCustomFilter = NULL ;
	ofn.nMaxCustFilter = 0 ;
	ofn.nFilterIndex = 1 ;
	ofn.lpstrFile = buf1 ;
	ofn.nMaxFile = MAX_PATH ;
	ofn.lpstrFileTitle = NULL ;
	ofn.nMaxFileTitle = 0 ;
	ofn.lpstrInitialDir = NULL ;
	ofn.lpstrTitle = NULL ;
	ofn.Flags = 0 ;
	ofn.nFileOffset = 0 ;
	ofn.nFileExtension = 0 ;
	ofn.lCustData = 0 ;
	ofn.lpfnHook = NULL ;
	ofn.lpTemplateName = NULL ;
	if ( !GetOpenFileName( &ofn ) ) {
		return FALSE ;
	}
	_tcscpy( szPath, buf1 ) ;
	return TRUE ;
}

int WINAPI
#ifdef	_WIN32_WCE
WinMain( HINSTANCE hThisInst, HINSTANCE hPrevInst,
		 LPTSTR lpszArgs, int nWinMode )
#else	/* _WIN32_WCE */
WinMain( HINSTANCE hThisInst, HINSTANCE hPrevInst,
		 LPSTR lpszArgs, int nWinMode )
#endif	/* _WIN32_WCE */
{
	int		len ;
	HANDLE	hfileImg, hfileBmp ;
	TCHAR	szMessageBuf[ 256 ] ;
	TCHAR	szBmpFn[ MAX_PATH ], *ptr ;

	if ( !GetOption( lpszArgs ) ) {
		usage() ;
		return 0 ;
	}
	if ( !*g_szImgFn ) {
		if ( !OpenDialog( hThisInst, g_szImgFn ) ) {
			return 0 ;
		}
	}
	len = _tcslen( g_szImgFn ) ;
	if ( !_tcsicmp( &g_szImgFn[ len - 4 ], TEXT(".IMG") ) ) {
		Ftype = 0 ;
	} else if ( !_tcsicmp( &g_szImgFn[ len - 4 ], TEXT(".CMT") ) ) {
		Ftype = 1 ;
	} else if ( !_tcsicmp( &g_szImgFn[ len - 4 ], TEXT(".DAT") ) ) {
		Ftype = 1 ;
	}
	/* Make output filename */
	_tcscpy( szBmpFn, g_szImgFn ) ;
	ptr = _tcsrchr( szBmpFn, TEXT('.') ) ;
	if ( ptr ) {
		_tcscpy( ptr, TEXT(".BMP") ) ;
	} else {
		_tcscat( szBmpFn, TEXT(".BMP") ) ;
	}
	/* Open source image file */
	hfileImg = CreateFile( g_szImgFn, GENERIC_READ, FILE_SHARE_READ, NULL,
						   OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 ) ;
	if ( hfileImg == INVALID_HANDLE_VALUE ) {
		wsprintf( szMessageBuf,
				  TEXT("Can't open %s\r\n\r\nError=%d\n"),
				  g_szImgFn, GetLastError() ) ;
		MessageBox( NULL, szMessageBuf, AppName, MB_OK|MB_ICONINFORMATION ) ;
		return 0 ;
	}
	/* Open destination image file */
	hfileBmp = CreateFile( szBmpFn, GENERIC_WRITE, FILE_SHARE_READ, NULL,
						   CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 ) ;
	if ( hfileBmp == INVALID_HANDLE_VALUE ) {
		wsprintf( szMessageBuf,
				  TEXT("Can't open %s\r\n\r\nError=%d\n"),
				  szBmpFn, GetLastError() ) ;
		MessageBox( NULL, szMessageBuf, AppName, MB_OK|MB_ICONINFORMATION ) ;
		CloseHandle( hfileImg ) ;
		return 0 ;
	}
	SetCursor( LoadCursor( NULL, IDC_WAIT ) ) ;
	if ( F_small ) {
		ccd_to_small( hfileImg, hfileBmp ) ;
	} else {
		ccd_to_large( hfileImg, hfileBmp ) ;
	}
	SetCursor( NULL ) ;
	CloseHandle( hfileBmp ) ;
	CloseHandle( hfileImg ) ;
	return 1 ;
}
