//--------------------------------------------------------------------------------------------------
// Implementation of the papers "Exact Acceleration of Linear Object Detectors", 12th European
// Conference on Computer Vision, 2012 and "Deformable Part Models with Individual Part Scaling",
// 24th British Machine Vision Conference, 2013.
//
// Copyright (c) 2013 Idiap Research Institute, <http://www.idiap.ch/>
// Written by Charles Dubout <charles.dubout@idiap.ch>
//
// This file is part of FFLDv2 (the Fast Fourier Linear Detector version 2)
//
// FFLDv2 is free software: you can redistribute it and/or modify it under the terms of the GNU
// Affero General Public License version 3 as published by the Free Software Foundation.
//
// FFLDv2 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 Affero
// General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License along with FFLDv2. If
// not, see <http://www.gnu.org/licenses/>.
//--------------------------------------------------------------------------------------------------

#ifndef FFLD_PATCHWORK_H
#define FFLD_PATCHWORK_H

#include "HOGPyramid.h"
#include "Rectangle.h"

#include <utility>

#include <fftw3.h>

#if defined(_WIN32) && defined(_MSC_VER)
// This is required due to the writing of the wisdom file and the fact that
// multiple C runtime libraries may be being used on Windows. This comes from
// the fact we use the pre-compiled mingw FFTW DLLs, but compile this project
// with MSVC. See http://www.fftw.org/install/windows.html for more
// information.
static void my_fftw_write_char(char c, void *f) { fputc(c, (FILE *) f); }
#define fftw_export_wisdom_to_file(f) fftw_export_wisdom(my_fftw_write_char, (void*) (f))
#define fftwf_export_wisdom_to_file(f) fftwf_export_wisdom(my_fftw_write_char, (void*) (f))
#define fftwl_export_wisdom_to_file(f) fftwl_export_wisdom(my_fftw_write_char, (void*) (f))

static int my_fftw_read_char(void *f) { return fgetc((FILE *) f); }
#define fftw_import_wisdom_from_file(f) fftw_import_wisdom(my_fftw_read_char, (void*) (f))
#define fftwf_import_wisdom_from_file(f) fftwf_import_wisdom(my_fftw_read_char, (void*) (f))
#define fftwl_import_wisdom_from_file(f) fftwl_import_wisdom(my_fftw_read_char, (void*) (f))
#endif

namespace FFLD
{
/// The Patchwork class computes convolutions much faster than the HOGPyramid class.
class Patchwork
{
public:
	/// Type of a scalar value.
	typedef std::complex<HOGPyramid::Scalar> Scalar;
	
	/// Type of a matrix.
	typedef Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> Matrix;
	
	/// Type of a patchwork plane cell (fixed-size complex array of length NbFeatures).
	typedef Eigen::Array<Scalar, HOGPyramid::NbFeatures, 1> Cell;
	
	/// Type of a patchwork plane (matrix of cells).
	typedef Eigen::Matrix<Cell, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> Plane;
	
	/// Type of a patchwork filter (plane + original filter size).
	typedef std::pair<Plane, std::pair<int, int> > Filter;
	
	/// Constructs an empty patchwork. An empty patchwork has no plane.
	Patchwork();
	
	/// Constructs a patchwork from a pyramid.
	/// @param[in] pyramid Pyramid.
	/// @note If the pyramid is larger than the last maxRows and maxCols passed to the Init method
	/// the Patchwork will be empty.
	/// @note Assumes that the features of the pyramid levels are zero in the padded regions but for
	/// the last feature, which is assumed to be one.
	explicit Patchwork(const HOGPyramid & pyramid);
	
	/// Returns whether the patchwork is empty. An empty patchwork has no plane.
	bool empty() const;
	
	/// Returns the amount of horizontal zero padding (in cells).
	int padx() const;
	
	/// Returns the amount of vertical zero padding (in cells).
	int pady() const;
	
	/// Returns the number of levels per octave in the pyramid.
	int interval() const;
	
	/// Returns the convolutions of the patchwork with filters (useful to compute the SVM margins).
	/// @param[in] filters Filters.
	/// @param[out] convolutions Convolution of each filter and each level.
	void convolve(const std::vector<Filter> & filters,
				  std::vector<std::vector<HOGPyramid::Matrix> > & convolutions) const;
	
	/// Initializes the FFTW library.
	/// @param[in] maxRows Maximum number of rows of a pyramid level (including padding).
	/// @param[in] maxCols Maximum number of columns of a pyramid level (including padding).
	/// @returns Whether the initialization was successful.
	/// @note Must be called before any other method (including constructors).
	static bool InitFFTW(int maxRows, int maxCols, bool cacheWisdom);
	
	/// Returns the current maximum number of rows of a pyramid level (including padding).
	static int MaxRows();
	
	/// Returns the current maximum number of columns of a pyramid level (including padding).
	static int MaxCols();
	
	/// Returns a transformed version of a filter to be used by the @c convolve method.
	/// @param[in] filter Filter to transform.
	/// @param[out] result Transformed filter.
	/// @note If Init was not already called or if the filter is larger than the last maxRows and
	/// maxCols passed to the Init method the result will be empty.
	static void TransformFilter(const HOGPyramid::Level & filter, Filter & result);
	
private:
	int padx_;
	int pady_;
	int interval_;
	std::vector<std::pair<Rectangle, int> > rectangles_;
	std::vector<Plane> planes_;
	
	static int MaxRows_;
	static int MaxCols_;
	static int HalfCols_;
	
#ifndef FFLD_HOGPYRAMID_DOUBLE
	static fftwf_plan Forwards_;
	static fftwf_plan Inverse_;
#else
	static fftw_plan Forwards_;
	static fftw_plan Inverse_;
#endif
};
}

#endif
