/*
 * aaview.c: Ascii image viewer
 *
 * Compile with:
 *    gcc aaview.c -o aaview -laa -lMagick
 *    You need aalib and ImageMagick library
 *
 * Usage:
 *
 * Zoom in and out with '+' and '-' keys
 * Move the image with the arrow keys
 * Quit with 'q'
 *
 * Copyright (c) 2003, eazy <eazy@ondaquadra.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * *  Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
 * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <aalib.h>
#include <string.h>
#include <magick/api.h>
#include <unistd.h>

int             image_height;	/* Numero di righe che conpongono l'immagine */
int             image_width;	/* Numero di colonne che compongono
				 * l'immagine */
Image          *image, *temp;
int             x_cord = 0, y_cord = 0, depth;
aa_context     *context;
char           *framebuffer;	/* Puntatore alla memoria video gestita dalle
				 * aalib, consente di accedere in raw alla
				 * memoria */
float           dx = 1, dy = 1;

struct aa_renderparams *render_param;

#define aa_getpalette(r,g,b) (((r)*30+(g)*59+(b)*11)>>8)

void
display(void)
{
	int             x, y, width, height;
	PixelPacket    *pixel;

	/*
	  Controlla se in larghezza l'immagine rimane dentro ai confini
	  del terminale
	 */
	if (image_width * dx < aa_imgwidth(context)) {

		/*
		  La larghezza è quella dell'immagine e la coordinata di
		  visualizzazione sull'asse delle x viene posta all'inizio
		  dell'immagine
		 */
		width = image_width * dx;
		x_cord = 0;
	} else
		/*
		  Altrimenti la larghezza è quella dettata dalle dimensioni massime del
		  terminale
		 */
		width = aa_imgwidth(context);

	/*
	  Controlla se il lunghezza l'immagine rimane dentro ai confini
	  del terminale
	 */
	if (image_height * dy < aa_imgheight(context)) {

		/*
		  La lunghezza è quella dell'immagine e la coordinata di
		  visualizzazione sull'asse delle y viene posta all'inizio
		  dell'immagine
		 */
		height = image_height * dy;
		y_cord = 0;
	} else
		/*
		  Altrimenti la lunghezza è quella dettata dalle dimensioni massime del
		  terminale
		 */
		height = aa_imgheight(context);

	/*
	  Se la coordinata di visualizzazione è negativa viene posta all'inizio
	  dell'immagine
	 */
	if (x_cord < 0)
		x_cord = 0;

	/*
	  La coordinata di visualizzazione viene decrementata nel caso vada oltre
	  i confini dell'immagine
	 */
	while (x_cord + width > image_width * dx)
		x_cord--;

	/*
	  Se la coordinata di visualizzazione è negativa viene posta all'inizio
	  dell'immagine
	 */
	if (y_cord < 0)
		y_cord = 0;

	/*
	  La coordinata di visualizzazione viene decrementata nel caso vada oltre
	  i confini dell'immagine
	 */
	while (y_cord + height > image_height * dy)
		y_cord--;

	/*
	  Preleva la porzione dell'immagine indicata dalle coordinate di visualizzazione
	  e dalle dimensioni
	 */
	if ((pixel = GetImagePixels(temp, x_cord, y_cord, width, height)) == NULL) {
		fprintf(stderr, "GetImagePixels() error\n");
		exit(-1);
	}
	if (sizeof(pixel->red) == 1)
		depth = 255;
	else if (sizeof(pixel->red) == 2)
		depth = 65535;
	else {
		fprintf(stderr, "Color depth not supported\n");
		exit(-1);
	}

	/*
	  Pulisce la memoria video
	 */
	memset(framebuffer, 0, aa_imgwidth(context) * aa_imgheight(context));

	/*
	  Scrive l'immagine in memoria un pixel alla volta
	 */
	for (y = 0; y < height; y++)
		for (x = 0; x < width; x++) {
			aa_putpixel(context, x, y, aa_getpalette(pixel->red * 255 / depth,
								 pixel->green * 255 / depth, pixel->blue * 255 / depth));
			pixel++;
		}

	/*
	  Esegue il rendering dell'immagine
	 */
	aa_render(context, render_param,
		  0, 0, aa_scrwidth(context), aa_scrheight(context));

	/*
	  Visualizza l'immagine sul terminale
	 */
	aa_flush(context);
}

int
main(int argc, char **argv)
{
	int             key;
	float           rapp_x, rapp_y;

	ImageInfo      *image_info;
	ExceptionInfo   exception;

	if (argc != 2) {
		fprintf(stderr, "Usage: %s filename\n", argv[0]);
		exit(-1);
	}
	/*
	if (!aa_parseoptions(NULL, NULL, &argc, argv) || argc != 1) {
		printf("%s", aa_help);
		exit(1);
	}
	*/

	context = aa_autoinit(&aa_defparams);
	if (context == NULL) {
		printf("Failed to initialize aalib\n");
		exit(1);
	}
	
	render_param = aa_getrenderparams();

	render_param->bright = 50;
	render_param->contrast = 80;
	//render_param->gamma = 10;

	aa_autoinitkbd(context, 0);

	framebuffer = aa_image(context);

	GetExceptionInfo(&exception);
	image_info = CloneImageInfo((ImageInfo *) NULL);
	strcpy(image_info->filename, argv[1]);
	if ((image = ReadImage(image_info, &exception)) == NULL) {
		CatchException(&exception);
		exit(-1);
	}
	image_width = image->magick_columns;
	image_height = image->magick_rows;

	/*
	  Raddoppio la larghezza dell'immagine in modo che mantenga le giuste
	  proporzioni una volta renderizzata in testo. Un carattere con il
	  font di default ha un'altezza pari al doppio della larghezza
	 */
	dx *= 2;

	/*
	  Calcola il rapporto tra la dimensione del terminale e la dimensione
	  dell'immagine rispettivamente per l'asse delle x e delle y.
	  Tanto più piccolo è il valore del rapporto calcolato, tanto più
	  l'immagine sfora dai confini del terminale
	 */
	rapp_x = (float) aa_imgwidth(context) / (float) image_width * dx;
	rapp_y = (float) aa_imgheight(context) / (float) image_height * dy;

	/*
	  Viene preso il lato dell'immagine che sfora maggiormente dai confini
	  del terminale e viene calcolato il resize dell'immagine sulla base di
	  quest'ultimo
	 */
	if (rapp_x < rapp_y) {
		dx *= rapp_x;
		dy *= rapp_x;
	} else {
		dx *= rapp_y;
		dy *= rapp_y;
	}

	/*
	  Viene eseguito il resize dell'immagine in modo tale che non sfori
	  dai confini del terminale
	 */
	temp = ScaleImage(image, image_width * dx, image_height * dy, &exception);

	for (;;) {

		/*
		  Visualizza l'immagine sul terminale
		 */
		display();
		key = aa_getevent(context, 1);
		switch (key) {
		case '+':
			/*
			  Ingrandisce l'immagine raddoppiandone le dimensioni ed eseguendo
			  il resize
			 */
			dx *= 2;
			dy *= 2;
			temp = ScaleImage(image, image_width * dx, image_height * dy, &exception);
			break;
		case '-':
			/*
			  Controlla che le dimensioni dell'immagine non raggiungano un valore nullo
			 */
			if (image_width * dx * 0.5 < 1 || image_height * dy * 0.5 < 1)
				break;

			/*
			  Rimpicciolisce l'immagine dimezzandone le dimensioni ed eseguendo
			  il resize
			 */
			dx *= 0.5;
			dy *= 0.5;
			temp = ScaleImage(image, image_width * dx, image_height * dy, &exception);
			break;
		case 'q':
			goto end;

			/*
			  Modifica le coordinate di visualizzazione dell'immagine
			 */
		case AA_UP:
			y_cord -= 10;
			break;
		case AA_DOWN:
			y_cord += 10;
			break;
		case AA_LEFT:
			x_cord -= 10;
			break;
		case AA_RIGHT:
			x_cord += 10;
			break;
		}
	}

	/*
	  Libera le risorse allocate dalla libreria aalib
	 */
end:	aa_close(context);

}

