/* 
   mkhuricn.c
	
   Changes by Harald Deischinger to compile with cthugha-L under Linux.
   Changes:
       Comments
       short -> short
       Random

       short -> tint
       unsigned tint -> utint

       const buff
*/

/*
//
// Mix two Cthugha TAB files tinto one.
// The two tables can be completely independent, or a certain amount of
// "leakage" can be created in either or both ways.
//
// Late discovery: Cthugha mixes neighbouring pixels anyway while calculating
// the next image, so mixing with zero leakage will not appear as two
// independent, superimposed tables.
//
// Compiled using Borland C 3.1.
//
// By Ofer Faigon, Sep 1994.
//     ofer@brm.co.il
//     (or oferf@itexjct.jct.ac.il)
//
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define CONST_BUFF
#include "../src/cthugha.h"
#include "../src/cth_buffer.h"

tint Random(tint range) {
    if ( range > 0)
	return rand() % range;
    else
	return 0;
}

typedef enum {FALSE=0, TRUE=1} bool;

/*
// A binary map for selecting which input affects the output at each pixel
// position.
*/
static unsigned char mixmap[BUFF_HEIGHT][(BUFF_WIDTH+7)/8];

#define SET_INPUT_AT(x,y)    mixmap[y][(x) >> 3] |= 1 << ((x) & 7)
#define UNSET_INPUT_AT(x,y)  mixmap[y][(x) >> 3] &= ~(1 << ((x) & 7))
#define INPUT_AT(x,y)        ((mixmap[y][(x) >> 3] >> ((x) & 7)) & 1)

/*
// Some macros for converting between pixel coordinates and offsets in
// a map file.
*/
#define MAP_OFFSET_OF(x,y) ((y) * BUFF_WIDTH + (x))
#define Y_OF(mapOffset)    ((mapOffset) / BUFF_WIDTH)
#define X_OF(mapOffset)    ((mapOffset) % BUFF_WIDTH)

/*
// Fill the mixing map with 0 and 1 bits.  A 0 bit selects the 1st input
// file for the corresponding screen coordinates, and a 1 bit selects the
// 2nd input file.
*/
static void fillMixMap (tint tablesRatio, tint Randomness) {
	tint  x, y, bit, pastRatio;

	if (Randomness == 0) {
		/* Completely ordered mix (perfect shuffle): the inputs are mixed
		// in a chessboard pattern; white squares correspond to one input
		// file, black ones to the other. */
		for (y = 0; y < BUFF_HEIGHT; y++)
			for (x = 0; x < (BUFF_WIDTH+7)/8; x++)
				mixmap[y][x] = (y & 1) ? 0x55 : 0xaa;
		return;
	}

	printf ("Filling a %d%%-Random mix map...\n", Randomness);
	for (y = 0; y < BUFF_HEIGHT; y++)
		for (x = 0; x < (BUFF_WIDTH+7)/8; x++)
			mixmap[y][x] = 0;
	pastRatio = 50;
	for (y = 0; y < BUFF_HEIGHT; y++) {
		for (x = 0; x < BUFF_WIDTH; x++) {
			if (Random(200) > Randomness)
				bit = (Random(100) > 2 * tablesRatio - pastRatio);
			else
				bit = Random(2);
			pastRatio = (5 * pastRatio + (bit ? 0 : 100)) / 6;
			if (bit)
				SET_INPUT_AT(x,y);
		}
	}
}

/*
// Look for a pixel that is as close as possible to (*x,*y) that belongs
// to the given table. If there is no such pixel in close proximity, then
// leave *x and *y unchanged.
// Returns 1 if found a good pixel, or 0 if not.
*/
static tint findWantedSrc (tint wantedTable, tint *x, tint *y) {
	tint   dx, dy, i;
	static tint d[20][2] = {{ 0,-1},{ 1, 0},{ 0, 1},{-1, 0},
	                       {-1,-1},{ 1,-1},{ 1, 1},{-1, 1},
	                       { 0,-2},{ 2, 0},{ 0, 2},{-2, 0},
	                       {-1,-2},{ 1,-2},{ 2,-1},{ 2, 1},
	                       { 1, 2},{-1, 2},{-2, 1},{-2,-1}}; 

	if (INPUT_AT(*x,*y) == wantedTable)
		return 1;

	/*
	// The pixel at (*x,*y) is not what we want. See if any neighbouring
	// pixel is.
	*/
	for (i = 0; i < 20; i++) {
		dx = d[i][0];
		dy = d[i][1];
		if (*x + dx >= 0  &&  *x + dx < BUFF_WIDTH  &&
		    *y + dy >= 0  &&  *y + dy < BUFF_HEIGHT  &&
		    INPUT_AT(*x + dx, *y + dy) == wantedTable) {

			*x += dx;
			*y += dy;
			return 1;
		}
	}

	/* No matching pixel found. Leave *x and *y unchanged. */
	return 0;
}

/*
// Look for a given command-line switch and remove it from argc/argv if
// found.  *val is set to potint to the string that follows the switch
// letter, or to NULL if not found.
// Decrement argc if a switch was found and removed.
// If the switch appears more than once, then all occurences are removed
// but only the last is taken tinto account.
*/
static void eatSwitch (tint *argc, char **argv, char switchLetter, char **val) {
	tint  i, j;

	*val = NULL;
	i = 1;
	while (i < *argc) {
		if (argv[i][0] == '-'  &&  argv[i][1] == switchLetter) {
			*val = &argv[i][2];
			for (j = i+1; j < *argc; j++)
				argv[j-1] = argv[j];
			(*argc)--;
		} else
			i++;
	}
}

tint main (tint argc, char **argv) {
	char   *p, *opName;
	char   *tab1Name, *tab2Name, *outName;
	FILE   *tab1, *tab2, *out;
	bool   avgMode;
	tint    x, y;
	tint    tablesRatio, leak1, leak2, Randomness;
	utint  outMap, inMap1, inMap2;
	tint    fromX, fromY, wantedSrcTab;
	tint    rc;
	bool   wantToLeak;
	long   nTab1, nTab2, nLeaks1, nLeaks2;
	tint    retVal;

	eatSwitch (&argc, argv, 'a', &p);
	avgMode = (p != NULL);
	opName = avgMode ? "Averaging" : "Mixing";

	eatSwitch (&argc, argv, 't', &p);
	tablesRatio = p ? min(max(atoi(p),1),99) : 50;

	eatSwitch (&argc, argv, 'l', &p);
	leak1 = p ? min(max(atoi(p),0),100) : 0;
	if (p)
		p = strchr (p, ':');
	if (p)
		p++;
	leak2 = p ? min(max(atoi(p),0),100) : 0;

	eatSwitch (&argc, argv, 'r', &p);
	Randomness = p ? min(max(atoi(p),0),100) : 50;

	if (tablesRatio != 50  &&  Randomness == 0)
		Randomness = 1;

	if (argc != 4) {
		char  *p = strrchr(argv[0], '\\');
		p = p ? p+1 : argv[0];
		printf ("Table mixer for CTHUGHA.\n");
		printf ("Takes two tables and generates a mixture of them.\n");
		printf ("\n");
		printf ("Usage: %s [-a] [-t#] [-l#:#] [-r#] input1.tab input2.tab output.tab\n", p);
		printf ("  -a    - Average the input tables (each pixel's movement is the average\n");
		printf ("          of moves of the corresponding pixels in both tables.\n");
		printf ("          Default is to mix the input tables (each pixel is taken from one\n");
		printf ("          table only).\n");
		printf ("  -t#   - The percentage of input1 pixels in the result if mixing,\n");
		printf ("          or the weight of input1 in the sum, if averaging (default -t50).\n");
		printf ("  -l#:# - The percentage of leakage from input1 to input2 and back\n");
		printf ("          (default is -l0:0). This is ignored when averaging.\n");
		printf ("  -r#   - The percentage of Randomness in the pixels' locations (default -r50).\n");
		printf ("          This too is ignored when averaging.\n");
		return 1;
	}

	tab1Name = argv[1];
	tab2Name = argv[2];
	outName = argv[3];

	if ((tab1 = fopen(tab1Name, "rb")) == NULL) {
		printf ("*** Failed to open 1st input file %s\n", tab1Name);
		return 2;
	}
	if ((tab2 = fopen(tab2Name, "rb")) == NULL) {
		printf ("*** Failed to open 2nd input file %s\n", tab2Name);
		fclose (tab1);
		return 2;
	}
	if ((out = fopen(outName, "wb")) == NULL) {
		printf ("*** Failed to create output file %s\n", outName);
		fclose (tab1);
		fclose (tab2);
		return 2;
	}

	printf ("%s %d%% of %s with %d%% of %s tinto %s\n"
	        "Randomness:              %3d%%\n",
	        opName,
	        tablesRatio, tab1Name, 100 - tablesRatio, tab2Name,
	        outName, Randomness);
	if (! avgMode)
		printf ("Leakage from 1st to 2nd: %3d%%\n"
		        "Leakage from 2nd to 1st: %3d%%\n",
		        leak1, leak2);

	if (! avgMode)
		fillMixMap (tablesRatio, Randomness);

	retVal = 1; /* Be pessimistic until all is over. */
	nLeaks1 = nLeaks2 = 0;
	nTab1 = nTab2 = 0;

	for (y = 0; y < BUFF_HEIGHT; y++) {
		printf ("%s line %d of %d...\r", opName, y + 1, BUFF_HEIGHT);

		for (x = 0; x < BUFF_WIDTH; x++) {
			if (fread (&inMap1, sizeof(inMap1), 1, tab1) != 1) {
				printf ("\n*** Premature end of file on %s\n", tab1Name);
				goto fin;
			}
			if (fread (&inMap2, sizeof(inMap2), 1, tab2) != 1) {
				printf ("\n*** Premature end of file on %s\n", tab2Name);
				goto fin;
			}

			if (avgMode) {

				long  dx1, dy1, dx2, dy2;
				tint   dx, dy;

				dx1 = X_OF(inMap1) - x;
				dy1 = Y_OF(inMap1) - y;
				dx2 = X_OF(inMap2) - x;
				dy2 = Y_OF(inMap2) - y;

				dx = (tint)(tablesRatio * dx1 / 100 + (100-tablesRatio) * dx2 / 100) / 2;
				dy = (tint)(tablesRatio * dy1 / 100 + (100-tablesRatio) * dy2 / 100) / 2;

				fromX = x + dx;
				fromY = y + dy;

				while (fromX < 0) fromX += BUFF_WIDTH;
				fromX = fromX % BUFF_WIDTH;
				while (fromY < 0) fromY += BUFF_HEIGHT;
				fromY = fromY % BUFF_HEIGHT;

			} else {

				if (INPUT_AT(x,y) == 0) {
					/*
					// This pixel should come from the 1st input file.
					// Use a Random function of leak2 to determine if the pixel
					// should be copied from a pixel belonging to tab1 or tab2.
					// The source pixel is selected to be as close as possible
					// to the original source pixel.
					*/
					nTab1 ++;
					fromX = X_OF(inMap1);
					fromY = Y_OF(inMap1);
					wantedSrcTab = (Random(100) < leak2);
					wantToLeak = (wantedSrcTab == 1);
				} else {
					/*
					// This pixel should come from the 2nd input file.
					// This is just like the above, only symmetric.
					*/
					nTab2 ++;
					fromX = X_OF(inMap2);
					fromY = Y_OF(inMap2);
					wantedSrcTab = (Random(100) >= leak1);
					wantToLeak = (wantedSrcTab == 0);
				}

				/*
				// If (fromX,fromY) does not belong to the table we want, try to
				// adjust fromX or fromY a little so that it does. Note that if
				// there is no suitable pixel near by, then we use the unmodified
				// (fromX,fromY), causing a leak where it was not requested, but
				// who cares - it is all for fun, isn't it?
				*/
				rc = findWantedSrc (wantedSrcTab, &fromX, &fromY);
				if (wantToLeak && rc == 1  ||  ! wantToLeak && rc == 0) {
					if (INPUT_AT(x,y) == 0)
						nLeaks1 ++;
					else
						nLeaks2 ++;
				}
			}

			outMap = MAP_OFFSET_OF (fromX, fromY);

			if (fwrite (&outMap, sizeof(outMap), 1, out) != 1) {
				printf ("\n*** Error while writing to output file (disk full?)\n");
				goto fin;
			}
		}
	}
	printf ("Done.%26s\n", "");

	if (! avgMode) {
		printf ("Actual tables ratio: %ld.\n", nTab1 * 100L / BUFF_SIZE);
		if (nTab1 == 0) nTab1++;
		if (nTab2 == 0) nTab2++;
		printf ("Actual leak percentage: %ld, %ld.\n",
		        nLeaks1 * 100L / nTab1, nLeaks2 * 100L / nTab2);
	}

	retVal = 0;

fin:
	fclose (out);
	fclose (tab1);
	fclose (tab2);

	return retVal;
}
