// This file is part of necsim project which is released under MIT license.
// See file **LICENSE.txt** or visit https://opensource.org/licenses/MIT) for full license details.


/**
 * @author Samuel Thompson
 * @file DataMask.h
 * @brief  Contains DataMask for describing the spatial sampling pattern on a landscape.
 *
 * @copyright <a href="https://opensource.org/licenses/MIT"> MIT Licence.</a>
 */
#ifndef SPECIATIONCOUNTER_DataMask_H
#define SPECIATIONCOUNTER_DataMask_H

// Forward declaration of Landscape class
class Landscape;

#include <string>
#include <memory>

#include "SimParameters.h"
#include "Map.h"


// Class which contains the DataMask object, telling us where to sample from within the habitat map.
/**
 * @brief Contains the DataMask object, a Matrix of booleans describing the spatial sampling pattern.
 */
class DataMask
{
protected:
	// the file to read in from
	string inputfile;
	// True if the sample mask is true everywhere
	bool isNullSample;
	// True if the grid is smaller than the sample mask
	bool isGridOffset;
	unsigned long x_offset, y_offset;
	// Stores the size of the grid which is stored as a full species species_id_list
	unsigned long x_dim, y_dim;
	// Stores the size of the samplemask from which spatially sampling is read
	unsigned long mask_x_dim, mask_y_dim;
	// Function pointer for obtaining the proportional sampling from the sample mask.
	typedef double (DataMask::*fptr)(const long &x, const long &y, const long &xwrap, const long &ywrap);
	fptr getProportionfptr;
public:
	/** A binary grid telling whether or not the cell should be sampled.*/
	Map<bool> sample_mask;
	/** Exact grid for determining sampling proportion.*/
	Map<double> sample_mask_exact;

	/**
	 * @brief The DataMask constructor.
	 */
	DataMask();

	~DataMask() = default;

	/**
	 * @brief Returns if the simulation is using the a null samplemask, and therefore does not need to store the full
	 * sample grid in memory.
	 * @return true if using a null samplemask
	 */
	bool isNull();

	/**
	 * @brief Sets the parameters for the datamask, including the dimensions of the map, the offsets from the grid and
	 * the dimensions of the grid itself for recalculating coordinates.
	 * @param sim_parameters Simulation parameter to set the data mask from
	 */
	void setup(shared_ptr<SimParameters> sim_parameters);

	bool checkCanUseDefault(shared_ptr<SimParameters> sim_parameters);
	/**
	 * @brief Imports the sample mask as a boolean mask and sets the relevant sample mask dimensions.
	 * Should only be called if the import is actually required (i.e. the map is not null or none).
	 * @param xdim the x dimension of the grid area
	 * @param ydim the y dimension of the grid area
	 * @param mask_xdim the x dimension of the sample map file
	 * @param mask_ydim the y dimension of the sample map file
	 * @param xoffset the x offset of the grid area from the sample map file
	 * @param yoffset the y offset of the grid area from the sample map file
	 * @param inputfile_in the path to the sample map file
	 */
	void importBooleanMask(unsigned long xdim, unsigned long ydim, unsigned long mask_xdim, unsigned long mask_ydim,
						   unsigned long xoffset, unsigned long yoffset, string inputfile_in);

	/**
	 * @brief Imports the boolean map object.
	 */
	void doImport();

	/**
	 * @brief Sets the map dimensions and the getVal function pointer.
	 */
	void completeBoolImport();

	/**
	 * @brief Sets up the null sampling map.
	 */
	void setupNull(shared_ptr<SimParameters> mapvarin);

	/**
	 * @brief Imports the specified file for the sampling percentage within each cell.
	 *
	 * The map should consist of floating points representing the relative sampling rates in each cell. Note that the
	 * actual sampling proportion is equal to the cell value multiplied by global deme sampling proportion.
	 * @param mapvarin the SimParameters object containing the samplemask file location and dimensions
	 */
	void importSampleMask(shared_ptr<SimParameters> mapvarin);


	/**
	 * @brief Calculates the matrix value at the provided x, y location.
	 * If everywhere is sampled, simply returns true, as no sample_mask will be stored in memory.
	 * This is to save RAM where possible.
	 *
	 * @param x the x position on the grid
	 * @param y the y position on the grid
	 * @param xwrap the number of x wraps
	 * @param ywrap the number of y wraps
	 * @return the sample_mask value at x,y (or true if the file was "null")
	 */
	bool getVal(const long &x, const long &y, const long &xwrap, const long &ywrap);

	/**
	 * @brief Separate return function for always returning 1.0 as density value.
	 * @param x the x position on the grid
	 * @param y the y position on the grid
	 * @param xwrap the number of x wraps around the map
	 * @param ywrap the number of y wraps around the map
	 * @return the sample_mask_exact value at (x, y)
	 */
	double getNullProportion(const long &x, const long &y, const long &xwrap, const long &ywrap);

	/**
	 * @brief Returns the exact value from the spatial sampling map, for calculating the proportion of individuals
	 * to be sampled in each cell.
	 * @note this function assumes that the file is not "null" and the exact sampling mask has been imported. No error
	 * checks on these conditions are performed except in debugging mode.
	 * @param x the x position on the grid
	 * @param y the y position on the grid
	 * @param xwrap the number of x wraps around the map
	 * @param ywrap the number of y wraps around the map
	 * @return the sample_mask_exact value at (x, y)
	 */
	double getBoolProportion(const long &x, const long &y, const long &xwrap, const long &ywrap);

	/**
	 * @brief Returns the exact value from the spatial sampling map, for calculating the proportion of individuals
	 * to be sampled in each cell.
	 * @note this function assumes that the file is not "null" and the exact sampling mask has been imported. No error
	 * checks on these conditions are performed except in debugging mode.
	 * @param x the x position on the grid
	 * @param y the y position on the grid
	 * @param xwrap the number of x wraps around the map
	 * @param ywrap the number of y wraps around the map
	 * @return the sample_mask_exact value at (x, y)
	 */
	double getSampleProportion(const long &x, const long &y, const long &xwrap, const long &ywrap);

	/**
	 * @brief Returns the exact value from the spatial sampling map, as returned by the pointer function.
	 * @param x the x position on the grid
	 * @param y the y position on the grid
	 * @param xwrap the number of x wraps around the map
	 * @param ywrap the number of y wraps around the map
	 * @return the sample_mask_exact value at (x, y)
	 */
	double getExactValue(const long &x, const long &y, const long &xwrap, const long &ywrap);

	/**
	 * @brief Converts the spatial map into the boolean grid required for continued simulation.
	 * This is done so that the faster boolean accesses are possible.
	 * @param map1 the map object to obtain density values from
	 * @param deme_sampling the proportion of individuals to sample
	 * @param generation the generation individuals are added at
	 */
	void convertBoolean(shared_ptr<Landscape> map1, const double &deme_sampling, const double &generation);

	/**
	 * @brief Removes the spatial mask from memory. This should be performed if no more map expansions are required.
	 */
	void clearSpatialMask();

	/**
	 * @brief Converts the coordinates back into the grid format. Changes the values in the provided variables to
	 * be correct.
	 *
	 * @param x the x value to convert
	 * @param y the y value to convert
	 * @param x_wrap the xwrap variable to place the value into
	 * @param y_wrap the ywrap variable to place the value into
	 */
	void recalculateCoordinates(long &x, long &y, long &x_wrap, long &y_wrap);
};


#endif //SPECIATIONCOUNTER_DataMask_H
