// Compute local resolution estimates
class LocalResolutionEstimator {

public:

	LocalResolutionEstimator();
	~LocalResolutionEstimator();

	void SetAllUserParameters(	Image *wanted_input_volume_one,
								Image *wanted_input_volume_two,
								Image *wanted_mask_volume,
								int wanted_first_slice,
								int wanted_last_slice,
								int wanted_sampling_step,
								float input_pixel_size_in_Angstroms,
								int wanted_box_size,
								float wanted_threshold_snr, // snr treshold. 0.334 gives the 0.143 criterion
								float wanted_threshold_confidence_n_sigma, // confidence - number of sigmas above estimate we want to be
								bool wanted_use_fixed_fsc_threshold,
								float wanted_fixed_fsc_threshold,
								wxString wanted_symmetry_symbol,
								bool wanted_whiten_half_maps,
								int wanted_padding_factor
								);

	void EstimateLocalResolution(Image *local_resolution_volume);


private:
	// Parameters from the user
	int		box_size;
	int		number_of_fsc_shells;
	float	pixel_size_in_Angstroms;
	float 	highest_resolution_expected_in_Angstroms;
	float	maximum_radius_in_Angstroms;
	Image 	*input_volume_one;
	Image 	*input_volume_two;
	Image	*input_volume_mask; // don't bother calculating local resolution outside the mask
	int		first_slice; // don't calculate resolution before first slice (xy plane) of the volume
	int 	last_slice;

	bool 	use_fixed_fsc_threshold;
	float 	fixed_fsc_threshold;
	float 	threshold_confidence_n_sigma; // confidence - number of sigmas above estimate we want to be
	float 	threshold_snr; // snr treshold. 0.334 gives the 0.143 criterion
	float 	resolution_value_before_first_shell; //40.0;//
	float 	resolution_value_where_wont_estimate;

	bool	whiten_half_maps;
	int		padding_factor;

	// Internal
	Image	box_one_no_padding;
	Image	box_two_no_padding;
	Image	box_one;
	Image	box_two;
	int		*shell_number_lut; // a look-up table: for each voxel in the local_volumes, remember what fsc shell we're in
	bool	shell_number_lut_is_allocated;
	int		sampling_step; // we don't necessarily need to estimate the resolution at every voxel
	int 	symmetry_redundancy;


	// Private methods
	void	AllocateShellNumberLUT();
	void	DeallocateShellNumberLUT();
	void	PopulateShellNumberLUT();
	void    CountIndependentVoxelsPerShell(float number_of_independent_voxels[], float wanted_scaling_factor);
	void 	ComputeFSCThresholdBasedOnUnbiasedSNREstimator(float number_of_independent_voxels[], float fsc_threshold[] );
	void	ComputeLocalFSCAndCompareToThreshold(float fsc_threshold[], Image *local_resolution_volume, Image box_mask);
	void 	AllocateLocalVolumes();
	void 	DeallocateLocalVolumes();
	void 	SetInputVolumes(Image *wanted_input_volume_one, Image *wanted_input_volume_two, Image *wanted_mask_volume);
	inline void SetPixelSize(float wanted_pixel_size_in_Angstroms) {pixel_size_in_Angstroms = wanted_pixel_size_in_Angstroms;};
	inline void SetBoxSize(int wanted_box_size) {box_size = wanted_box_size;};
	float 	ReturnResolutionOfIntersectionBetweenFSCAndThreshold(float resolution_one, float resolution_two, float fsc_one, float fsc_two, float threshold_one, float threshold_two);

	// The key equations
	float 	SigmaZSquaredAuto(float number_of_independent_voxels);
	float 	SigmaZSquared(float rho, float number_of_independent_voxels);
	float	RhoThreshold(float snr_threshold_halfmaps, float number_of_sigmas, float number_of_independent_voxels, int number_of_iterations = 2);

};
