/*
Copyright (c) 2015 to 2016 by Cornell University and The Regents Of
The University Of California. All Rights Reserved.

Permission to use this Procedural Yarn Fitting and Generation Tool (the "Work")
and its associated copyrights solely for educational, research and non-profit
purposes, without fee is hereby granted, provided that the user agrees as
follows:

Those desiring to incorporate the Work into commercial products or use Work and
its associated copyrights for commercial purposes should contact the Center for
Technology Licensing at Cornell University at

395 Pine Tree Road, Suite 310, Ithaca, NY 14850;
email: ctl-connect@cornell.edu;
Tel: 607-254-4698;
FAX: 607-254-5454

for a commercial license.

IN NO EVENT SHALL CORNELL UNIVERSITY ("CORNELL") OR THE UNIVERSITY OF
CALIFORNIA ("UC") BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL,
INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF
THE USE OF THE WORK AND ITS ASSOCIATED COPYRIGHTS, EVEN IF CORNELL OR UC MAY
HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

THE WORK PROVIDED HEREIN IS ON AN "AS IS" BASIS, AND NEITHER CORNELL NOR UC HAS
ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
MODIFICATIONS. CORNELL AND UC MAKE NO REPRESENTATIONS AND EXTEND NO WARRANTIES
OF ANY KIND, EITHER IMPLIED OR EXPRESS, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, OR
THAT THE USE OF WORK AND ITS ASSOCIATED COPYRIGHTS WILL NOT INFRINGE ANY PATENT,
TRADEMARK OR OTHER RIGHTS.
*/

#pragma once
#include "Util.h"
#include "opencv/include/opencv/cv.h"
#include "opencv/include/opencv/highgui.h"
#include "opencv/include/opencv2/opencv.hpp"

#define RAKE_PATH "D:\\Cloth\\mitsuba-fiber\\work\\"
#define IMPROVED_FLYAWAYS
#define USE_SIGMA_THRESHOLDS

namespace CT {
	typedef std::vector<vec3f> Polyline;
	typedef std::vector<Polyline> Polylines;

	class MitsubaVol {
		std::vector<float> m_data;
		int m_dim[3];
		vec3f m_aabb[2];
		int channel;
		friend class CTAnalyzer;
	public:
		bool load(const std::string &filename, bool verbose = true);
		bool save(const std::string &filename, bool verbose = true);
		int index(int x, int y, int z);
		float lookup(int x, int y, int z) { return this->m_data[index(x, y, z)]; }
		float lookup(int index) { return this->m_data[index]; }
		void destroy();
	};

	class PlyCurves {
		Polylines curves;
		int m_curve_num;	  // total curve number
		float m_curve_length; // total curve length
		friend class CTAnalyzer;
	public:
		bool load(const std::string &filename, bool verbose = true);
		bool save(const std::string &filename, bool verbose = true);
	};

	class Fibers {
		Polylines fibers;
		friend class CTAnalyzer;
	public:
		int m_fiber_num;     // total fiber number
		float m_fiber_length;// total fiber length
		bool load(const std::string &filename, bool verbose = true);
		bool save(const std::string &filename, bool verbose = true);
	};

	struct Config {
		/* Number */
        int ply_num, fiber_num;
#ifndef IMPROVED_FLYAWAYS
        int flyaway_num;
#endif

		/* AABB */
		vec3f aabb_min, aabb_max;
		
		/* Stepsize */
		float z_step_size, fly_step_size; 
		int z_step_num;
		
		/* Twisting */
		int yarn_clock_wise, fiber_clock_wise;
		float yarn_alpha, alpha;
		
		/* Ellipse fitting */
		float yarn_radius;
		float ellipse_long, ellipse_short;
		
		/* Cross-section fiber density distribution */
		float epsilon, beta, R_max;

		/* Fiber migration */
		int use_migration;
		float s_i, rho_min, rho_max;

#ifdef IMPROVED_FLYAWAYS
        int use_flyaways;
        float flyaway_hair_density;
        float flyaway_hair_ze_mu, flyaway_hair_ze_sigma;
        float flyaway_hair_r0_mu, flyaway_hair_r0_sigma;
        float flyaway_hair_re_mu, flyaway_hair_re_sigma;
        float flyaway_hair_pe_mu, flyaway_hair_pe_sigma;
        float flyaway_loop_density;
        float flyaway_loop_r1_mu, flyaway_loop_r1_sigma;
#else
		/* Flyaway fibers */
		float mu, sigma;
#endif

		Config():ply_num(0), fiber_num(0),
			aabb_min(vec3f()), aabb_max(vec3f()),
			z_step_size(0.f), fly_step_size(0.f), z_step_num(0), 
			yarn_clock_wise(0), fiber_clock_wise(0), yarn_alpha(0.f), alpha(0.f),
			yarn_radius(0.f), ellipse_short(0.f), ellipse_long(0.f),
			epsilon(0.f), beta(0.f), R_max(1.f), 
			use_migration(0), s_i(0.f), rho_min(0.f), rho_max(0.f),
#ifdef IMPROVED_FLYAWAYS
            use_flyaways(0),
            flyaway_hair_density(0.0f), flyaway_hair_ze_mu(0.0f), flyaway_hair_ze_sigma(0.0f),
            flyaway_hair_r0_mu(0.0f), flyaway_hair_r0_sigma(0.0f),
            flyaway_hair_re_mu(0.0f), flyaway_hair_re_sigma(0.0f),
            flyaway_hair_pe_mu(0.0f), flyaway_hair_pe_sigma(0.0f),
            flyaway_loop_density(0.0f),
            flyaway_loop_r1_mu(0.0f), flyaway_loop_r1_sigma(0.0f)
#else
            flyaway_num(0),	mu(0.f), sigma(0.f) 
#endif
		{

		}

		void load(const std::string &filename);
		void save(const std::string &filename);
	};

	class CTAnalyzer {

		MitsubaVol vol;

		PlyCurves curve, smooth_curve;
		
		Fibers fibers;
		std::vector<Fibers> untied_fibers;			  // Raw input
		std::vector<Fibers> untied_cleaned_fibers;    // No garbage + No flyaway
		std::vector<Fibers> untied_fibers_no_garbage; // No garbage
		std::vector<Fibers> unrolled_ply_fibers;	  // Loaded input for step2

        std::vector<Fibers> untied_flyaway_fibers;    // Flyaway only
        std::vector<float>  untied_flyaway_thresholds;

		IplImage *ctImg, *displayImg;

		omp_lock_t omp_lock;
		int num_of_omp_cores;

		float scalingFactor; // pixel to world scaling
		float pixelsPerFiber;  // pixel area per fiber

		std::string unrolled_ply_file;

	public:
		CTAnalyzer() { }
		~CTAnalyzer() { }
		 
		void fillin_ct_slice(int sliceId);
		void fillin_ct_slice(int sliceId, IplImage *img);
		float density_ct_slice(IplImage *img);
		float density_ct_slice(IplImage *img, std::vector<vec2f> &points);
		void set_pixels_per_fiber(float ppf) { this->pixelsPerFiber = ppf; }
		void set_unrolled_ply_file(const std::string &fn) { this->unrolled_ply_file = fn; }
		void load_unrolled_ply(const int K, const std::string &fn) {
			std::vector<std::string> fns;
			for (int i = 0; i < K; i++) {
				std::string fi_n = fn.substr(0, fn.find('.')) + std::to_string((long long)i) + ".txt";
				fns.push_back(fi_n);
			}

			this->unrolled_ply_fibers.resize(fns.size());
			for (int i = 0; i < fns.size(); i++)
				this->unrolled_ply_fibers[i].load(fns[i]);
		}

		void RobustFit(Config &config, int step = -1);
		void RobustFitPlyNum(Config &config);
		void RobustFitFiberNum(Config &config);
		void RobustFitAABB(Config &config);
		void RobustFitZStep(Config &config);
		void RobustFitCrossSectionShape(Config &config);
		void RobustFitYarnTwist(Config &config);
		void RobustUntieYarnToPlys(Config &config);
		void RobustRemovePlyFlyAways(Config &config);
		void RobustFitFiberMigration(Config &config);
		void RobustFitFiberTwist(Config &config);
		void RobustFitFiberDistribution(Config &config);
		void RobustFitFlyAway(Config &config);
		void load_ct_fibers(const std::string &fiberFile);
		void smooth_ct_fibers(Fibers &fibers, int filter_size); 
		void smooth_ct_fiber(Polyline &fiber, int filter_size);

		void BlobDetection(IplImage *img, std::vector<vec2f> &keypoints);


		void view_slice(int id);
		void view_all_slices(
			float time_pause = 100, 
			int stride = 5,
			bool show_curve = false, 
			bool blob_detect = true,
			bool fit_ellipse = true,
			bool verbose = false
		); 

		void load_ct_volume(const std::string &filename);
		void save_ct_volume(const std::string &filename);
		void load_ply_curves(const std::string &filename);
		void smooth_ply_curves();

    protected:
        static float percentile(const std::vector<float> &data, float p);
        static void meanStd(const std::vector<float> &data, float &mean_, float &std_);
	};

}