// Copyright 2003 David Hilvert <dhilvert@auricle.dyndns.org>,
//                              <dhilvert@ugcs.caltech.edu>

/*  This file is part of the Anti-Lamenessing Engine.

    The Anti-Lamenessing Engine is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    The Anti-Lamenessing Engine is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with the Anti-Lamenessing Engine; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#ifndef __psf_rasterize_h__
#define __psf_rasterize_h__

#include "../../point.h"
#include "raster.h"
#include "psf.h"

/*
 * Point-spread function rasterizer.
 *
 * These operations rasterize a PSF to a multiple of the resolution of the
 * rendering grid for a given frame.  
 */

class rasterizer : public raster {
	psf *input;

public:
	unsigned int varieties() const {
		return input->varieties();
	}

	unsigned int select(unsigned int i, unsigned int j) const {
		return input->select(i, j);
	}

	rasterizer (psf *input, transformation t) {
		this->input = input;

		_height = -input->min_i();

		if (input->max_i() > _height)
			_height = input->max_i();

		_width = -input->min_j();

		if (input->max_j() > _width)
			_width = input->max_j();

		/*
		 * Approximate the desired resolution.
		 *
		 * Assume that maximum resolution is reached at (at least) one
		 * of the corners of the image.  (This should be true for
		 * projective, Euclidean, and translational transformations,
		 * but it would be worthwhile to check/prove this for the
		 * projective case at some point, since it's a bit less
		 * obvious.)
		 */

		point min_diff;

		/*
		 * XXX: this loop breaks when height <= 1 or width <= 1.
		 */

		for (unsigned int i = 0; i < t.unscaled_height(); i += (t.unscaled_height() - 1))
		for (unsigned int j = 0; j < t.unscaled_width();  j += (t.unscaled_width()  - 1)) {
			point corner = point(i, j);
			point delta1 = corner - t.scaled_inverse_transform(t.transform_scaled(corner) + point(1, 0));
			point delta2 = corner - t.scaled_inverse_transform(t.transform_scaled(corner) + point(0, 1));

			for (int index = 0; index < 2; index++) {
				ale_pos d1 = fabs(delta1[index]);
				ale_pos d2 = fabs(delta2[index]);

				/*
				 * Take the largest change in each direction.
				 */

				ale_pos delta = (d1 > d2) ? d1 : d2;

				if ((i == 0 && j == 0) || delta < min_diff[index])
					min_diff[index] = delta;
			}
		}

		ale_real resolution_multiplier = 20;  /* Arbitrary */

		_filter_dim_i = (int) ceil((ale_real) 2 * _height * resolution_multiplier / (ale_real) min_diff[0]);
		_filter_dim_j = (int) ceil((ale_real) 2 * _width * resolution_multiplier / (ale_real) min_diff[1]);

		/*
		 * Ensure that the array has an odd number of elements in each
		 * direction.  This allows us to move the center to the right
		 * place when using FFTW.
		 */

		if (_filter_dim_i % 2 == 0)
			_filter_dim_i++;
		if (_filter_dim_j % 2 == 0)
			_filter_dim_j++;

		/*
		 * Determine the number of arrays to create.
		 */

		num_arrays = input->varieties();

		/*
		 * Create arrays
		 */

		response_arrays = (ale_real **)malloc(num_arrays * sizeof(ale_real *));

		if (!response_arrays) {
			fprintf(stderr, "Could not allocate in rasterizer.\n");
			exit(1);
		}

		ale_real stepsize_i = (2 * _height) / _filter_dim_i;
		ale_real stepsize_j = (2 * _width) / _filter_dim_j;
		ale_real divisor = stepsize_i * stepsize_j;

		for (unsigned int n = 0; n < num_arrays; n++) {
			response_arrays[n] = (ale_real *)malloc(_filter_dim_i * _filter_dim_j * 3
					                    * sizeof(ale_real));
					
			if (!response_arrays[n]) {
				fprintf(stderr, "Could not allocate in rasterizer.\n");
				exit(1);
			}

			for (unsigned int i = 0; i < _filter_dim_i; i++)
			for (unsigned int j = 0; j < _filter_dim_j; j++) {
				psf_result r 
					= (*input)(-_height + stepsize_i * (ale_real) i,
						   -_height + stepsize_i * (ale_real) (i + 1),
						   -_width  + stepsize_j * (ale_real) j,
						   -_width  + stepsize_j * (ale_real) (j + 1), n);

				for (unsigned int k = 0; k < 3; k++) {
					response_arrays[n][i * _filter_dim_j * 3 + j * 3 + k]
						= r.matrix(k, k) / divisor;
				}
			}
		}

#if 0
		avg_response = (ale_real *)malloc(_filter_dim_i * _filter_dim_j * 3
				              * sizeof(ale_real));
					      
		if (!avg_response) {
			fprintf(stderr, "Could not allocate in rasterizer.\n");
			exit(1);
		}

		for (unsigned int i = 0; i < _filter_dim_i; i++)
		for (unsigned int j = 0; j < _filter_dim_j; j++) {
			psf::psf_result r 
				= (*input)(-_height + stepsize_i * i,
					   -_height + stepsize_i * (i + 1),
					   -_width  + stepsize_j * j,
					   -_width  + stepsize_j * (j + 1));

			for (unsigned int k = 0; k < 3; k++)
				avg_response[i * _filter_dim_j * 3 + j * 3 + k]
					= r.matrix(k, k) / divisor;
		}
#endif

		compute_integrals();
		
//		fprintf(stderr, "(w=%f h=%f we=%d he=%d [", _width, _height, _filter_dim_j, _filter_dim_i);
//		for (unsigned int i = 0; i < _filter_dim_i; i++)
//		for (unsigned int j = 0; j < _filter_dim_j; j++)
//			fprintf(stderr, "%f ", response_arrays[0][i * _filter_dim_j * 3 + j * 3 + 0]);
//		fprintf(stderr, "])");
	}
};

#endif
