/*
 *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 *%									   %
 *%	Copyright (C) 1991,1994 by WATCOM International Inc.		   %
 *%	All rights reserved.						   %
 *%									   %
 *%     Permission is granted to anyone to use this example program for	   %
 *%     any purpose on any computer system, subject to the following	   %
 *%	restrictions:							   %
 *%									   %
 *%     1. This example is provided on an "as is" basis, without warranty. %
 *%	   You indemnify, hold harmless and defend WATCOM from and against %
 *%	   any claims or lawsuits, including attorney's, that arise or	   %
 *%	   result from the use or distribution of this example, or any     %
 *%	   modification thereof.					   %
 *%									   %
 *%     2. You may not remove, alter or suppress this notice from this	   %
 *%        example program or any modification thereof.			   %
 *%									   %
 *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 *
 * PATTERNS.C
 *
 * Code to create the pattern menu, loading/saving patterns from/to disk
 *
 */
#include <stdlib.h>
#include <stdio.h>
#include <io.h>
#include <direct.h>
#include <malloc.h>
#include <sys/stat.h>
#include "life.h"

static HBITMAP		MenuBitMap = NULL;
static HMENU		PatternMenu = NULL;
static HDC		MenuDC;
static BITMAP		MenuBitInfo;
static HBITMAP		*MenuPatterns;
static char		**Patterns;
static char Pixie[] = { 1, 1, 1 };
static char *Cursor = Pixie;



extern BOOL ReadAPatternFile( char *name, int i )
/************************************************

    Read the pattern found in file "name", into pattern position "i"
*/
{
    FILE	*io;
    int		ch;
    char	*array;
    char	*pattern;
    int		xdim,ydim;
    struct stat status;

    Patterns = realloc( Patterns, (i+1)*sizeof( Patterns[0] ) );
    if( Patterns == NULL ) return( NoMemory() );
    MenuPatterns = realloc( MenuPatterns, (i+1)* sizeof( MenuPatterns[0] ) );
    if( MenuPatterns == NULL ) return( NoMemory() );
    stat( name, &status );
    if( status.st_size == 0 ) return( FALSE );
    pattern = malloc( status.st_size );
    if( pattern == NULL ) return( NoMemory() );
    Patterns[i] = pattern;
    io = fopen( name, "r" );
    if( io == NULL ) return( FALSE );
    array = pattern+2;
    xdim = 0;
    ydim = 0;
    for( ;; ) {
	ch = fgetc( io );
	if( ch == EOF ) break;
	if( ch == '\n' ) {
	    ++ydim;
	    pattern[0] = xdim;
	    xdim = 0;
	} else {
	    ++xdim;
	    if( ch == ' ' || ch == '_' ) {
		*array = FALSE;
	    } else {
		*array = TRUE;
	    }
	    ++array;
	}
    }
    fclose( io );
    pattern[1] = ydim;
    if( pattern[0] == 0 || ydim == 0 ) {
	free( Patterns[i] );
	return( FALSE );
    }
    return( TRUE );
}


extern BOOL ReadPatterns()
/*************************

    Read in all the pattern files from disk.
    (*.LIF in the same directory LIFE.EXE)
*/
{
    char	filename[_MAX_PATH];
    char	drive[_MAX_DRIVE];
    char	dir[_MAX_DIR];
    DIR		*dir_handle;
    struct dirent *dir_entry;
    int		i;

    GetModuleFileName( ThisInst, filename, _MAX_PATH );
    _splitpath( filename, drive, dir, NULL, NULL );
    _makepath( filename, drive, dir, "*", ".LIF" );
    dir_handle = opendir( filename );
    Patterns = malloc( sizeof( Patterns[0] ) );
    Patterns[0] = &Pixie;
    MenuPatterns = malloc( 1 * sizeof( MenuPatterns[0] ) );
    if( MenuPatterns == NULL ) return( NoMemory() );
    i = 1;
    if( dir_handle != NULL ) {
	while( dir_entry = readdir( dir_handle ) ) {
	    _makepath( filename, drive, dir, dir_entry->d_name, NULL );
	    if( !ReadAPatternFile( filename, i ) ) {
	        Error( "Error reading file" );
		return( FALSE );
	    }
	    ++i;
	}
    }
    NumberPatterns = i;
    return( TRUE );
}


extern void FreePatterns()
/*************************

    Free up the patterns array, and corresponding bit maps
*/
{
    int		i;

    if( MenuBitMap != NULL ) DeleteObject( MenuBitMap );
    for( i = 1; i < NumberPatterns; ++i ) {
	free( Patterns[i] );
    }
    free( Patterns );
}


extern void ReflectAboutXequalY( char *pattern )
/**********************************************

    Reflect an array "pattern" about the line x=y.
*/
{
    unsigned	x_dim,y_dim;
    unsigned	x,y;
    char	*new;

    x_dim = *pattern++;
    y_dim = *pattern++;
    new = alloca( x_dim * y_dim + 2 );
    *new++ = y_dim;
    *new++ = x_dim;
    for( x = 0; x < x_dim; x++ ) {
	for( y = 0; y < y_dim; y++ ) {
	    new[ x*y_dim + y ] = pattern[ y*x_dim + x ];
	}
    }
    new -= 2;
    pattern -= 2;
    for( x = 0; x < x_dim*y_dim + 2; ++x ) {
	pattern[ x ] = new[ x ];
    }
}


extern void ReflectAboutYAxis( char *pattern )
/********************************************

    Reflect an array about the Y axis.
*/
{
    unsigned	x_dim,y_dim;
    unsigned	x,y,xlate_x;
    char	temp;

    x_dim = *pattern++;
    y_dim = *pattern++;
    for( x = 0; x < x_dim / 2; ++x ) {
        xlate_x = x_dim - x - 1;
	for( y = 0; y < y_dim; ++y ) {
	    temp = pattern[ y * x_dim + x ];
	    pattern[ y * x_dim + x ] = pattern[ y * x_dim + xlate_x ];
	    pattern[ y * x_dim + xlate_x ] = temp;
	}
    }
}


extern void TransformPatterns( void (*rtn)(char *) )
/***************************************************

    Transform each pattern array.
    "Rtn" is ReflectAboutYAxis or ReflectAboutXEqualY.
*/
{
    int		i;

    for( i = 0; i < NumberPatterns; ++i ) {
	rtn( Patterns[i] );
    }
}


static void LoadPatternFile()
/****************************

    Load the pattern file named in "Buffer" into our pattern menu.
*/
{
    if( ReadAPatternFile( Buffer, NumberPatterns ) ) {
	++NumberPatterns;
	CreatePatternMenu();
    }
}


extern void WritePatternFile()
/*****************************

    Write the selected region to a pattern file (Prompt for name)
*/
{
    pixels	start_x, end_x;
    pixels	start_y, end_y;
    pixels	x, y;
    FILE	*io;
    int		rc;

    if( !GetFileName( "Save A Pattern File", TRUE,
    	"*.LIF", Buffer, BUFSIZ ) ) return;
    if( !access( Buffer, F_OK ) ) {

	char		buf[ _MAX_PATH + 100 ];

	sprintf( buf, "Overwrite file %s?", Buffer );
	rc = MessageBox( NULL, buf, "Save A Pattern File",
			 MB_YESNO | MB_ICONEXCLAMATION );
	if( rc != IDYES ) return;
    }
    GetSelectedCoords( &start_x, &end_x, &start_y, &end_y );
    io = fopen( Buffer, "w" );
    if( io != NULL ) {
	for( y = start_y; y < end_y; ++y ) {
	    for( x = start_x; x < end_x; ++x ) {
	        if( CellPointer( x, y )->alive ) {
		    fputc( 'X', io );
		} else {
		    fputc( '_', io );
		}
	    }
	    fputc( '\n', io );
	}
	fclose( io );
    }
    LoadPatternFile();
}


extern void LoadNewPattern()
/***************************

    Load a new pattern file into the menu (Prompt for name)
*/
{
    if( GetFileName( "Read A Pattern File", FALSE,
    	 	"*.LIF", Buffer, BUFSIZ ) ) {
	LoadPatternFile();
    }
}


static HBITMAP CreateAMenuBitMap( char *pattern, pixels *total_height )
/**********************************************************************

    Create a bit map suitable for use in the Pattern menu based on "pattern".
    Add the height of the pattern to *total_height
*/
{
    HDC		dc;
    HDC		memory_dc;
    HBITMAP	bit_map;
    pixels	x,y;
    char	dim_x, dim_y;
    pixels	grid_x, grid_y;
    pixels	size_x, size_y;

    dim_x = *pattern++;
    dim_y = *pattern++;
    size_x = MenuBitInfo.bmWidth * dim_x;
    size_y = MenuBitInfo.bmHeight * dim_y;
    if( size_x > MAX_MENU_SIZE_X ) size_x = MAX_MENU_SIZE_X;
    if( size_y > MAX_MENU_SIZE_Y ) size_y = MAX_MENU_SIZE_Y;
    grid_x = size_x / dim_x;
    grid_y = size_y / dim_y;
    if( grid_x == 0 ) grid_x = 1;
    if( grid_y == 0 ) grid_y = 1;
    if( size_x < MIN_MENU_SIZE_X ) size_x = MIN_MENU_SIZE_X;
    if( size_y < MIN_MENU_SIZE_Y ) size_y = MIN_MENU_SIZE_Y;

    dc = GetDC( WinHandle );
    memory_dc = CreateCompatibleDC( dc );
    bit_map = CreateCompatibleBitmap( dc, size_x, size_y );
    SelectObject( memory_dc, bit_map );
    PatBlt( memory_dc, 0, 0, size_x, size_y, WHITENESS );
    for( y = 0; y < dim_y; ++y ) {
	for( x = 0; x < dim_x; ++x ) {
	    if( *pattern++ ) {
		StretchBlt( memory_dc, x*grid_x, y*grid_y,
			    grid_x, grid_y, MenuDC,
			    0, 0,
			    MenuBitInfo.bmWidth, MenuBitInfo.bmHeight, SRCCOPY );
	    }
	}
    }
    DeleteDC( memory_dc );
    ReleaseDC( WinHandle, dc );
    *total_height += size_y;
    return( bit_map );
}



extern void CreatePatternMenu()
/****************************

    Create the "&Pattern" menu, based upon MenuPatterns[]
*/
{
    HDC		dc;
    int		i;
    HMENU	menu;
    pixels	menu_height;

    if( MenuBitMap == NULL ) {
	MenuBitMap = LoadBitmap( ThisInst, "MenuBitMap" );
	dc = GetDC( WinHandle );
	MenuDC = CreateCompatibleDC( dc );
	ReleaseDC( WinHandle, dc );
	SelectObject( MenuDC, MenuBitMap );
	GetObject( MenuBitMap, sizeof( BITMAP ), (LPSTR)&MenuBitInfo );
    }
    menu = CreateMenu();
    if( menu == NULL ) {
        Error( "No room to create a new menu\n" );
	return;
    }
    PatternMenu = menu;
    i = 0;
    menu_height = 0;
    for( ;; ) {
	MenuPatterns[i] = CreateAMenuBitMap( Patterns[i], &menu_height );
	AppendMenu( PatternMenu, MF_BITMAP+MF_CHECKED,
	            MENU_PATTERN+i, (LPSTR)(LONG)MenuPatterns[i] );
	if( (i+1) == NumberPatterns ) break;
	if( menu_height > ScreenHeight / 3 ) {
	    AppendMenu( PatternMenu, MF_MENUBARBREAK, 0, (LPSTR)(LONG)NULL );
	    menu_height = 0;
	} else {
	    AppendMenu( PatternMenu, MF_SEPARATOR, 0, (LPSTR)(LONG)NULL );
	}
	++i;
    }
    AppendMenu( PatternMenu, MF_MENUBARBREAK, 0, (LPSTR)(LONG)NULL );
    AppendMenu( PatternMenu, MF_STRING, MENU_ROTATE_P90, "&Rotate +90" );
    AppendMenu( PatternMenu, MF_STRING, MENU_ROTATE_M90, "&Rotate -90" );
    AppendMenu( PatternMenu, MF_STRING, MENU_ROTATE_180, "&Rotate 180" );
    AppendMenu( PatternMenu, MF_STRING, MENU_REFLECT_X,  "&Flip Top/Bottom" );
    AppendMenu( PatternMenu, MF_STRING, MENU_REFLECT_Y,  "&Flip Left/Right" );
    ModifyMenu( GetMenu( WinHandle ), 2, MF_BYPOSITION | MF_POPUP,
                (UINT)PatternMenu, "&Pattern" );
}

extern void SetCurrPattern( int i )
/**********************************

    Set the currently selected pattern to "i"
*/
{
    Cursor = Patterns[ i ];
}

extern BOOL IsCurrPattern( int i )
/*********************************

    Is "i" the currently selected pattern?
*/
{
    return( Cursor == Patterns[ i ] );
}

extern void DrawPattern( pixels pixel_x, pixels pixel_y, BOOL erase )
/********************************************************************

    Draw the currently selected pattern at screen location (pixel_x,pixel_y).
    If "erase" is true, we wipe out a rectangle the size of the pattern.
*/
{
    pixels	screen_x, screen_y;
    pixels	x, y;
    pixels	wrap_x, wrap_y;
    HDC		dc;
    cell_ptr	cell;
    char	dim_x, dim_y;
    char 	*pattern;
    BOOL	change;


    screen_x = pixel_x / BitInfo.bmWidth;
    screen_y = pixel_y / BitInfo.bmHeight;
    pattern = Cursor;
    dim_x = *pattern++;
    dim_y = *pattern++;
    change = FALSE;
    dc = GetDC( WinHandle );
    for( y = 0; y < dim_y; ++y ) {
	for( x = 0; x < dim_x; ++x ) {
	    wrap_x = screen_x + x;
	    wrap_y = screen_y + y;
	    WrapAround( &wrap_x, &wrap_y );
	    cell = CellPointer( wrap_x, wrap_y );
	    if( erase ) {
		change |= TurnOffCell( dc, cell, wrap_x, wrap_y );
	    } else if( *pattern ) {
		change |= TurnOnCell( dc, cell, wrap_x, wrap_y );
	    }
	    ++pattern;
	}
    }
    ReleaseDC( WinHandle, dc );
    if( change ) SetCaption();
}
