/***********************************************************************************/
/*  Copyright 2009-2015 WSL Institute for Snow and Avalanche Research    SLF-DAVOS      */
/***********************************************************************************/
/* This file is part of Alpine3D.
    Alpine3D is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Alpine3D 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 Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with Alpine3D.  If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef VIEWFACTORS_H
#define VIEWFACTORS_H

#include <meteoio/MeteoIO.h>
#include <alpine3d/ebalance/VFSymetricMatrix.h>
#include <alpine3d/ebalance/ViewFactorsAlgorithm.h>

class ViewFactors : public ViewFactorsAlgorithm {

	public:
		ViewFactors(const mio::Config& cfg, const mio::DEMObject &dem_in);
		double getSkyViewFactor(const int &i, const int &j);
		double GetViewfactor(const int i, const int j, const int a, const int b);
		double getSymetricTerrainViewFactor(const int &i, const int &j);
		void setBoundarys(const unsigned int in_start_x, const unsigned int in_end_x, const unsigned int in_start_y, const unsigned int in_end_y);
		void generate();
		void generate_visible_matrix();

		bool getVisibleMatrixEntry(const unsigned int i, const unsigned int j, const unsigned int m, const unsigned int t);

		double min_area;             // min (inclined) surface area
		double min_vterr;            // min terrain view factor
		bool vf_in_ram;

	private:
		VFSymetricMatrix<float, double> vf; // view factor matrix with dynamic dimension
		VFSymetricMatrix<float, bool> visible; // visible matrix with dynamic dimension
		mio::DEMObject dem;
		mio::Array2D<double> sky_vf, vf_t;
		double cellsize;
		int dimx, dimy;
		double sub_crit;
		int LW_distance_index, SW_distance_index;

		unsigned int start_x, end_x;
		unsigned int start_y, end_y;

		const static double to_rad;

		const static double vf_thresh; //below this threshold, view factor is forced to 0

		bool visible_matrix_filled;

		double EuclidianDistance(const double x, const double y, const double z);
		double DoubleRounding(const double aNumber, const double precision);
		int GetStep(const int a, const int b);
		double GetMax(const int step, const double azi, const bool x_Mode);
		double GetDelta(const int step, const double azi, const bool x_Mode);
		double GetPseudoAzimuth(const int i, const int j, const int m, const int t);
		void GetNextCell(int &x, int &y, double &tMaxX, double &tMaxY, const int stepsX, const int stepsY, const double tDeltaX, const double tDeltaY);
		bool CellOutsideGrid(const int a, const int b);
		double DistanceBetween2Cells(const int i, const int j, const int a, const int b, const double bz);
		double DistanceOnTheBeamMiddleCell(const int i, const int j, const int a, const int b, const int m, const int t, const double bz);
		double DistanceOnTheBeamBorderCell(const int i, const int j, const int a, const int b, const int m, const int t, const double bz);
		bool IsAngleHigher(const double view_angleP, const int i, const int j, int &m, int &t);
		bool Is2CellsVisible(int i, int j, int m, int t);
		void ApplySunBorderTreatment(const int i, const int j, int &m, int &t);
		int ComputeFirstObstacle(const double solar_elev, const int i, const int j, const int mt, const int tt, int& horizon_x, int& horizon_y);
		double GetZEdgeVector(const double z, const double nx, const double ny, const double deltaX, const double deltaY);
		int GetSplitFactor(const double sideLength, const double dist);
		double GetCoordinateForSmallCell(const double veca, const double vecb, const double vecc, const int i, const int j, const int splitFactor);
		double CosAngleBetween2Vectors(const double nx, const double ny, const double nz, const double bx, const double by, const double bz);
		double GetSymetricPartOfViewFactor(const int i, const int j, const int a, const int b);
		int InitGridViewFactors();
		int InitSkyViewFactors();
		bool InitializeViewFactor();

		void setVF_IN_RAM(bool);
};

#endif
