/***************************************************************************
 *	Reveal.hpp
 *	Part of Reveal
 *	2015-  Florent Berthaut
 *	hitmuri.net
 ****************************************************************************/
/*
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation; either version 2 of the License, or
 *	(at your option) any later version.
 *
 *	This program 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 General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with this program; if not, write to the Free Software
 *	Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */


#ifndef Reveal_h
#define Reveal_h

#include "modules/ContextHandler.hpp"
#include "modules/GroupModule.hpp"

class DepthCamModule;
class ProjectorsModule;
class SceneModule;
class RootSceneGroupModule;
class RevealedModule;
class SpacesModule;
class OutputManagerModule;
class PreviewModule;

class Geometry;
class ModuleListObserver;

class Reveal : public GroupModule {

	public:
		enum REVIL_PROGRAM {
			PREVIEWPROG, SLICEPROG, SELECTPROG, RENDERPROG, CALIBPROG, POSTPROG
		};
		enum REVIL_UNIFORM {
			VIEWMAT, VIEWPROJMAT, MODELMAT, INVMODELMAT, MIRRORED, MODELSCALE,
			SUBSHAPEINVMAT,
			VIEWPORTWIDTH, VIEWPORTHEIGHT, 
			SHAPECOLOR, SHAPEID, SHAPEIDBIT, SUBSHAPEID, SUBSHAPESNB, 
			SHAPEGEOM,

			SURFACE, SURFACECOLOR, SURFACETHICKNESS,

            AUDIOOUTPUT,

			INSIDEVISIBLE, INSIDESTRUCT, STRUCTRATIO,
			GRADIENTALPHA, GRADIENTTYPE, GRADIENTSTEPS, GRADIENTCURVERATIO, 
			GRADIENTTEXTURE,
			DENSITYALPHA, DENSITYTYPE, DENSITYRATIO, DENSITYSIZE, 
			DENSITYCURVERATIO,
			TEXALPHA, TEXBLEND, TEXOFFSET, TEXSCALE, TEXGRAY,
			REACTIVITY,

			BBOXMIN, BBOXSIZE, BBOXROT, BBOXLOCALSIZE, 

			REVSIZE, REVSURFACE, REVINSIDE, REVCENTER,	
			REVCURSOR, REVCOLOR, REVHISTO, REVVOXELS, REVEALEDBY, 
			OUTSHAPESIZE, OUTHISTOSIZE, OUTVOXELSIZE, OUTDEPTHSIZE, OUTNBDEPTH,
			BRECT,
			OUTPUTTEX, SLICETEX, SELECTTEX, INSIDETEX, SURFACETEX, COORDSTEX,
			REACTTEX, SURFDISTEX,
			CAMTEX, CAMTEXFIL, CAMXRESO, CAMYRESO, CAMXZFACTOR, CAMYZFACTOR, 
			CAMFILTER, CAMFLIP, CAMCONTOURTHRESH, 
			CAMMARKERS, MARKTEX, BACKGROUND,
			DEPTHTYPE, DEPTHID, OUTPUTCURS, 
			PATW, PATPOSX, PATPOSY, PATNBX, PATNBY, PATBRIGHT, PATTERNTEX,
			PATRANGE, PATOFFSET,
			POSTFILTER, RENDERTEX,

			AUDIOTEX, AUDIORATE, MAXAUDIOVALUE, AUDIOBUFSIZE, AUDIOMIXSIZE,
			PROCESSAUDIO, AUDIONEXTBUF, AUDIONBTRACKS
		};

		enum GEOM_ID { 
			GEOM_BOX, GEOM_SPHERE, GEOM_TUBE, GEOM_CONE, GEOM_FRAME,
			GEOM_PATH, GEOM_QUAD,
			GEOM_CAM1280, GEOM_CAM640, GEOM_CAM320,
			GEOM_MODEL
		};

		enum VISIBLE_FROM {
			VISIBLE_FROM_NONE, VISIBLE_FROM_ALL
		};

	public:
		static Reveal* getInstance();
		~Reveal();

		void init();
		int run();
		void update();
		void planOpen(std::string);    

		void initPrograms();

		static void openCallback(Module* mod, const std::string& f) {
			dynamic_cast<Reveal*>(mod)->open(f);
		}
		void open(const std::string& f);
		static void saveCallback(Module* mod, const std::string& f) {
			dynamic_cast<Reveal*>(mod)->save(f);
		}
		void save(const std::string& f);
		static void quitCallback(Module* mod) {
			dynamic_cast<Reveal*>(mod)->askQuit();
		}
		void askQuit();
		void quit();

		static void ressourcesCallback(Module* mod, const std::string& f) {
			dynamic_cast<Reveal*>(mod)->setRessourcesFolder(f);
		}
		void setRessourcesFolder(const std::string& f);
		static void uploadCallback(Module* mod, 
								   const std::vector<std::string>& vf
								   ) {
			dynamic_cast<Reveal*>(mod)->uploadRessource(vf);
		}
		void uploadRessource(const std::vector<std::string>& vf);
		static void debugOscCallback(Module* mod, const std::vector<bool>& vf) {
			dynamic_cast<Reveal*>(mod)->debugOSC(vf[0]);
		}
		void debugOSC(const bool& deb){m_debugOSC=deb;}
		const bool& getDebugOSC() {return m_debugOSC;}

		static void audioOutputCallback(Module* mod, 
				const std::vector<bool>& vf) {
			dynamic_cast<Reveal*>(mod)->setAudioOutput(vf[0]);
		}
		void setAudioOutput(const bool& aud);

		virtual void load(xmlNodePtr);

		void addUpdateObserver(Module* upObs);
		void removeUpdateObserver(Module* upObs);

		void refreshModules();
		Module* createModule(const std::string& typeStr);
		Module* findModule(const std::string& fullName);
		Attribute* findAttribute(const std::string& fullName);

		int getVisibilityMask(const std::string& visibleFrom);

		Listener* createListener(const std::string& typeStr);

		inline SpacesModule* getSpaces(){return m_spaces;}
	
		//Revealed modules
		void registerRevealed(RevealedModule*);
		void unregisterRevealed(RevealedModule*);
		RevealedModule* getRevealedModule(const unsigned int& s) { 
			if(m_revealedMap.find(s)!=m_revealedMap.end()) {
				return m_revealedMap[s];
			}
			return NULL;
		}
		unsigned int getNbRevealed(){return m_revealedVec.size();}
		inline const int& getMaxNbRevealed(){return m_maxNbRevealed;}
		void updateRevealed();

		void registerOutputRevealed(RevealedModule* rev);
		void unregisterOutputRevealed(RevealedModule* rev);
		inline std::vector<RevealedModule*>& editRegisteredOutputRevealed() {
			return m_registeredOutputRevealed;
		}
		void updateOutputRevealed();

		inline const int& getMaxNbDepthCams(){return m_maxNbDepthCams;}
		void registerOutputDepthCam(DepthCamModule* cam);
		void unregisterOutputDepthCam(DepthCamModule* cam);
		inline std::vector<DepthCamModule*>& editRegisteredOutputDepthCams() {
			return m_registeredOutputDepthCams;
		}

		//removing things
		void deleteListener(Listener*);
		void deleteModule(Module*);

		//Geometries
		const std::vector<Geometry*>& getGeoms(){return m_geoms;}
		Geometry* getGeom(const GEOM_ID& geomStr) { 
			return m_geomsMap[geomStr];
		}
		void registerGeom(Geometry* geom);
		void unregisterGeom(Geometry* geom);

		void addModuleListObserver(ModuleListObserver* obs);
		void removeModuleListObserver(ModuleListObserver* obs);

		void addContextHandler(ContextHandler* cont);
		void removeContextHandler(ContextHandler* cont);
		inline ContextHandler* getContextHandler(const int& id) {
			return m_contextHandlers[id];
		}

		GLuint createProgram(const char* vertSource, const char* fragSource);

		GLFWwindow* getPreviewWindow();

		void measureStart();
		void measureStore(const int& nbPixels);
		const std::string& getCurrentFile(){return m_currentFile;}

	private:
		Reveal();
		void testDelete();

	private:
		SpacesModule* m_spaces;
		RootSceneGroupModule* m_scene;
		PreviewModule* m_preview;
		OutputManagerModule* m_output;
		bool m_debugOSC;
		bool m_audioOut;

		int m_openPlanned;
		std::string m_openPlannedName;
		std::string m_currentFile;

		std::map<std::string, Module*> m_typesMap;

		std::vector<Module*> m_updateObservers;
		unsigned int m_upObsIdCounter;
		timeval m_prevTime;

		std::map<std::string, Module*> m_modulesMap;
		std::map<std::string, Attribute*> m_fullAttributesMap;
		std::map<unsigned int, ModuleListObserver*> m_moduleListObservers;

		std::map<GEOM_ID, Geometry*> m_geomsMap;
		std::vector<Geometry*> m_geoms;

		std::map<std::string, int> m_visibleFromMap;
		std::vector<std::string> m_visibleFromVec;

		int m_contextHandlersCounter;
		std::map<unsigned int, ContextHandler*> m_contextHandlers;

		std::vector<RevealedModule*> m_revealedVec;
		std::map<unsigned int, RevealedModule*> m_revealedMap;
		int m_maxNbRevealed;
		std::vector<RevealedModule*> m_registeredOutputRevealed;
		int m_maxNbDepthCams;
		std::vector<DepthCamModule*> m_registeredOutputDepthCams;
		std::vector<Listener*> m_listenersDeleteVec;
		std::vector<Attribute*> m_attributesDeleteVec;
		std::vector<Module*> m_modulesDeleteVec;


		timeval m_measureTime;
		bool m_measuring;
		std::map<int, int> m_measurePixelTimes;
		std::map<int, int> m_measurePixelNb;
};

class ModuleListObserver {
	public:
		virtual ~ModuleListObserver(){}
		virtual void updateModulesList(const std::map<std::string, Module*>&)=0;
		void setModuleListObsID(const unsigned int& id){m_moduleListObsID=id;}
		const unsigned int& getModuleListObsID(){return m_moduleListObsID;}

	protected:
		unsigned int m_moduleListObsID;

};

#endif