/****************************************************
-----------------------------------------------------

	flickwerk
	picture mosaic generator
	
	by bifat / TEK neoscientists

	version 0.0.1
	
	note: the sources look really awful, sorry.
	this software is considered a quick hack,
	for fun and experimentation :-)

-----------------------------------------------------
****************************************************/

#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include <dos/dosextens.h>
#include <dos/dos.h>
#include <dos/rdargs.h>
#include <exec/lists.h>
#include <exec/memory.h>
#include <guigfx/guigfx.h>

#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/guigfx.h>

#include <clib/macros.h>


#include "defs.h"
#include "global.h"
#include "debug.h"
#include "tools.h"
#include "flickwerk.h"
#include "filelist.h"
#include "scan.h"
#include "picturekey.h"



/*
**	internal prototypes
*/

BOOL mosaic(char **sourcepatterns, char *destfile, int xseg, int yseg, char *outfile, BOOL update, int partwidth, int partheight, BOOL usekeys, BOOL allowx, BOOL allowy);
BOOL SavePPM(char *filename, PICTURE *pic);




/*********************************************************************

	return = main()

*********************************************************************/

ULONG main (void)
{
	ULONG result = RETURN_ERROR;

	if (InitGlobal())
	{
		long opts[OPT_COUNT];
		struct RDArgs *rdargs;
	
		memset((char *) opts, 0, sizeof(opts));
		rdargs = ReadArgs(RDARGSTEMPLATE, opts, NULL);
		
		if (rdargs)
		{
			char **sourcepatterns;
			char *destfile;
			char *outfile;
			BOOL update, usekeys, allowx, allowy;
			int xseg, yseg, partwidth, partheight;
	
			sourcepatterns = (char **) opts[OPT_SOURCEFILES];
			destfile = (char *) opts[OPT_DESTFILE];
			outfile = (char *) opts[OPT_OUTFILE];
			update = (BOOL) opts[OPT_UPDATE];
			usekeys = (BOOL) opts[OPT_USEKEYS];
			allowx = (BOOL) opts[OPT_FLIPX];
			allowy = (BOOL) opts[OPT_FLIPY];

			xseg = opts[OPT_XSEGMENTS] ? *((LONG *) opts[OPT_XSEGMENTS]) : XSEGMENTS_DEFAULT;
			yseg = opts[OPT_YSEGMENTS] ? *((LONG *) opts[OPT_YSEGMENTS]) : YSEGMENTS_DEFAULT;

			partwidth = opts[OPT_PARTWIDTH] ? *((LONG *) opts[OPT_PARTWIDTH]) : PARTWIDTH_DEFAULT;
			partheight = opts[OPT_PARTHEIGHT] ? *((LONG *) opts[OPT_PARTHEIGHT]) : PARTHEIGHT_DEFAULT;
	
			if (usekeys && update)
			{
				printf("*** the combination of USEKEYS and UPDATE makes no sense.\n");
				result = RETURN_ERROR;
			}
			else
			{
				if (mosaic(sourcepatterns, destfile, xseg, yseg, outfile, update, partwidth, partheight, usekeys, allowx, allowy))
				{
					result = RETURN_OK;
				}
			}
	
			FreeArgs(rdargs);
		}
		else
		{
			printf("\nmosaic picture generator " __VERSION__ "\nwritten by bifat / TEK neoscientists\n\n");
			
			printf("WITH=SOURCEFILES/A/M  ... any number of directories, files, patterns\n");
			printf("FROM=REFFILE/A        ... reference file to be approximated\n");
			printf("TO=OUTFILE/A          ... filename of resulting picture (PPM format)\n");
			printf("UPDATE/S              ... recalculate all keys (ignore existing keys)\n");
			printf("USEKEYS/S             ... use existing keys (do not calculate new keys)\n");
			printf("X=XSEGMENTS/N         ... number of horizontal segments (default %ld)\n", XSEGMENTS_DEFAULT);
			printf("Y=YSEGMENTS/N         ... number of vertical segments (default %ld)\n", YSEGMENTS_DEFAULT);
			printf("W=SEGMENTWIDTH/N      ... width of a mosaic segment (default %ld)\n", PARTWIDTH_DEFAULT);
			printf("H=SEGMENTHEIGHT/N     ... height of a mosaic segment (default %ld)\n", PARTHEIGHT_DEFAULT);
			printf("FX=ALLOWFLIPX/S       ... allow horizontal mirroring\n");
			printf("FY=ALLOWFLIPY/S       ... allow vertical mirroring\n");
			
			result = RETURN_ERROR;
		}

		CloseGlobal();
	}
	else
	{
		printf("*** global initialization failed.\n");
		result = RETURN_FAIL;
	}

	return result;
}



/*--------------------------------------------------------------------

	success = mosaic(	sourcefiles, destfile, xseg, yseg,
						outfile, updatekeys?, usekeys?, partwidth, partheight, allowx, allowy)

--------------------------------------------------------------------*/

BOOL mosaic(char **sourcepatterns, char *destfile, int xseg, int yseg, char *outfile, BOOL update, int partwidth, int partheight, BOOL usekeys, BOOL allowx, BOOL allowy)
{
	BOOL success = FALSE;

	if (sourcepatterns && destfile && outfile)
	{
		struct FileListNode **matrix;
		int *orientationmatrix;

		orientationmatrix = Malloc(xseg * yseg * sizeof(int));
		matrix = Malloclear(xseg * yseg * sizeof(struct FileListNode *));
		
		if (matrix && orientationmatrix)
		{
			struct FileList *flist;
			PICTURE *outpic = NULL; 
			struct List *filenamelist = NULL;
		
			if (flist = FileList_Create())
			{
				BOOL abort = FALSE;
				char **scanpattern = sourcepatterns;
	
	
				/*
				**	info...
				*/
	
				printf("*** creating %ldx%ld mosaic %s\n    from reference picture %s\n",
					xseg, yseg, outfile, destfile);
					
				printf("    with %ldx%ld pixel segments each\n", partwidth, partheight); 
	
	
				/*
				**	scan filepatterns
				*/
	
	
				printf("*** scanning\n");
				
				while (*scanpattern)
				{
					int entries_so_far = flist->numentries;
				
					if (Scan(flist, *scanpattern, "#?.info", TRUE, SIGBREAKF_CTRL_C, usekeys) == FALSE)
					{
						abort = TRUE;
						break;
					}
	
					printf("    - %ld pictures in %s\n", flist->numentries - entries_so_far, *scanpattern);
	
					scanpattern++;
				}
	
	
	
				/*
				**	generate keys
				*/
	
				if (!abort)
				{
					
					if (flist->numentries > 0)
					{
						printf("    found %ld pictures and %ld valid keys.\n", flist->numentries, flist->numkeys);

						if (usekeys)
						{
							if (flist->numkeys == 0)
							{
								printf("*** no valid keys found.\n");
								abort = TRUE;
							}
						}
						else
						{
							
							if (update)
							{
								printf("*** updating all keys.\n");
								abort = !FileList_CreateKeys(flist, TRUE, SIGBREAKF_CTRL_C);
							}
							else
							{
								if (flist->numentries == flist->numkeys)
								{
									printf("*** no keys to calculate.\n");
								}
								else
								{
									printf("*** calculating %ld keys.\n", flist->numentries - flist->numkeys);
									abort = !FileList_CreateKeys(flist, FALSE, SIGBREAKF_CTRL_C);
								}
							}
						}
					}
					else
					{
						printf("    no pictures found.\n");
						abort = TRUE;
					}
				}


	
				/*
				**	decompose reference picture
				*/
				
				if (!abort)
				{					
					BOOL success = FALSE;

					if (filenamelist = CreateList())
					{
						PICTURE *refpic;
						printf("*** decomposing reference picture %s.\n", destfile);
		
						if (refpic = LoadPicture(destfile, NULL))
						{
							ULONG width, height, *rawdata;
									
							if (DoPictureMethod(refpic, PICMTHD_RENDER, PIXFMT_0RGB_32, NULL))
							{
								if (GetPictureAttrs(refpic, 
									PICATTR_Width, &width,
									PICATTR_Height, &height,
									PICATTR_RawData, &rawdata) == 3)
								{				
									success = TRUE;
									

									/*
									**	if a segment of the reference picture is too small,
									** we must scale it up
									*/

									if (width < KEYWIDTH * xseg || height < KEYHEIGHT * yseg)
									{
										width = MAX(width, KEYWIDTH * xseg);
										height = MAX(height, KEYHEIGHT * yseg);

										success = DoPictureMethod(refpic, PICMTHD_SCALE,
											width, height, TAG_DONE);
									}
									
									if (success)
									{
										int x, y;
										double cx, cy, dx, dy;
				
										dx = (double) width / (double) xseg;
										dy = (double) height / (double) yseg;
				
										cy = 0.0;							
										for (y = 0; y < yseg && success; ++y)
										{
											cx = 0.0;
											for (x = 0; x < xseg && success; ++x)
											{											
												success = FALSE;
												if (!(SetSignal(0,0) & SIGBREAKF_CTRL_C))
												{
													PICTUREKEY *key;
													double error;
													int orientation = OR_NORMAL;
													
													if (key = CalcPictureKeySegment(refpic, (int) cx, (int) cy, (int) dx, (int) dy))
													{
														struct FileListNode *flnode;
														
														flnode = FileList_FindBestKey(flist, key, &error, &orientation, allowx, allowy);

														matrix[y * xseg + x] = flnode;
														orientationmatrix[y * xseg + x] = orientation;

														printf("    segment(%ld,%ld - error: %.3Lf): %s\n",
															x,y, error, flnode->fullname);
														
														if (!FindName(filenamelist, flnode->fullname))
														{
															struct Node *newnode;
															if (newnode = CreateNode(flnode->fullname))
															{
																AddTail(filenamelist, newnode);
																success = TRUE;
															}
														}
														else
														{
															success = TRUE;
														}
														
														Free(key);	
													}
												}
												
												cx += dx;
											}
											cy += dy;
										}
									}
								}
							}					
							
							DeletePicture(refpic);
						}
					}

					if (!success)
					{
						printf("    error: picture could not be loaded or out of memory\n");
						abort = TRUE;
					}
				}
	
	
	
	
				/*
				**	assembe final picture
				*/
	
	
				if (!abort)
				{
					printf("*** assembling final picture\n");

					success = FALSE;
					
					if (outpic = MakePicture(NULL, partwidth * xseg, partheight * yseg,
							GGFX_PixelFormat, PIXFMT_0RGB_32, GGFX_Independent, TRUE, TAG_DONE))
					{
						struct Node *node = filenamelist->lh_Head, *nextnode;
						int x, y;
						
						success = TRUE;
						
						while ((nextnode = node->ln_Succ) && success)
						{
							struct FileListNode *flnode = (struct FileListNode *) node;
							PICTURE *segpic;

							success = FALSE;

							printf("    inserting %s\n", node->ln_Name);

							if (segpic = LoadPicture(node->ln_Name, NULL))
							{
						
								if (DoPictureMethod(segpic, PICMTHD_AUTOCROP, TAG_DONE))
								{
									if (DoPictureMethod(segpic, PICMTHD_SCALE, partwidth, partheight, TAG_DONE))
									{
										int orientation = OR_NORMAL;

										for (y = 0; y < yseg; ++y)
										{
											for (x = 0; x < xseg; ++x)
											{
												if (StriCmp(node->ln_Name, matrix[y*xseg+x]->fullname) == 0)
												{

													printf("              in segment (%ld,%ld) ", x,y);
													if (orientationmatrix[y*xseg+x] & OR_FLIPX)
													{
														printf("<-X-> ");
													}
													if (orientationmatrix[y*xseg+x] & OR_FLIPY)
													{
														printf("<-Y-> ");
													}
													printf("\n");

													if ((orientation & OR_FLIPX) != (orientationmatrix[y*xseg+x] & OR_FLIPX))
													{
														DoPictureMethod(segpic, PICMTHD_FLIPX, NULL);
														orientation ^= OR_FLIPX;
													}

													if ((orientation & OR_FLIPY) != (orientationmatrix[y*xseg+x] & OR_FLIPY))
													{
														DoPictureMethod(segpic, PICMTHD_FLIPY, NULL);
														orientation ^= OR_FLIPY;
													}

												
													if (DoPictureMethod(outpic, PICMTHD_INSERT, segpic,
														GGFX_DestX, x * partwidth,
														GGFX_DestY, y * partheight,
														GGFX_DestWidth, partwidth,
														GGFX_DestHeight, partheight, TAG_DONE))
													{
														success = TRUE;
													}
												}
											}
										}
									}
								}

								DeletePicture(segpic);
							}

							if (SetSignal(0,0) & SIGBREAKF_CTRL_C)
							{
								success = FALSE;
								break;
							}

							node = nextnode;
						}
					}

					if (!success)
					{
						printf("    error: not enough memory\n");
						abort = TRUE;
					}
				}


				
	

				/*
				**	save final picture
				*/
	
				if (!abort)
				{
					assert(outpic);

					printf("*** saving final picture\n");
					
					SavePPM(outfile, outpic);
				}


				FileList_Delete(flist);
			}

			DeleteList(filenamelist);
			DeletePicture(outpic);
		}

		Free(matrix);				
		Free(orientationmatrix);				

	}
	
	return success;
}



/*********************************************************************

	success = SavePPM(filename, picture)

*********************************************************************/

BOOL SavePPM(char *filename, PICTURE *pic)
{
	BOOL success = FALSE;

	if (filename && pic)
	{
		if (DoPictureMethod(pic, PICMTHD_RENDER, PIXFMT_0RGB_32, TAG_DONE))
		{		
			ULONG width, height, *data;

			if (GetPictureAttrs(pic, PICATTR_Width, &width,
				PICATTR_Height, &height,
				PICATTR_RawData, &data, TAG_DONE) == 3)
			{
				char *linebuffer;
				
				if (linebuffer = Malloc(width * 3))
				{
					BPTR file;

					if (file = Open(filename, MODE_NEWFILE))
					{				
						char textbuffer[100];
	
						sprintf(textbuffer, "P6\n%ld\n%ld\n%ld\n",
							width, height, 255); 
	
						if (success = (Write(file, textbuffer, strlen(textbuffer)) == strlen(textbuffer)))
						{
							int y, x;
							char *s = (char *) data;
							
							for (y = 0; y < height && success; ++y)
							{
								char *p = linebuffer;
								for (x = 0; x < width; ++x)
								{
									s++;
									*p++ = *s++;
									*p++ = *s++;
									*p++ = *s++;
								}
								success = (Write(file, linebuffer, width*3) == width*3);
							}
						}
	
						Close(file);
					}
					
					Free(linebuffer);
				}
			}
		}
	}	

	return success;
}



