#pragma once

#include "SceneConfig.h"
#include "ShadowConfig.h"
#include "Cursor3D.h"
#include "CrossSectionConfig.h"

#include <LibQtGeoViewerCore/GeomObject.h>

#include <LibQtGeoViewerCore/Image/SampleTextureBuilder.h>
#include <LibQtGeoViewerCore/Image/EnvImage.h>
#include <LibQtGeoViewerCore/Image/MatcapImage.h>

#include <LibQtGeoViewerCore/GeomFileFormat.h>

#include <boost/ptr_container/ptr_vector.hpp>

#include <QString>

#include "ObjLoaderConfig.h"
#include "Selection3D.h"

using namespace geom;


class SceneObserver;
class SceneMain;



namespace lib_geo
{
	class ObjMesh;
}



//! シーンオブジェクトの状態変化確認用オブサーバ
class SceneObserver
{
public:
	//! ジオメトリ生成完了通知
	virtual void OnGeometryBuild(const SceneMain& scene) {}
};


class FileLoadErrorException : public std::exception
{
public:
	FileLoadErrorException()
	{
	}

	FileLoadErrorException(const char* what):
		std::exception(what)
	{
	}
};


class IOConfig
{
public:
	IOConfig(void) :
		ObjSplit(false),
		InterpolateAnimation(false)
	{
	}

public:
	bool ObjSplit;
	bool InterpolateAnimation;
};


class IndexRangeConfig
{
public:
	IndexRangeConfig(void) :
		beginRatio(0.0),
		endRatio(1.0)
	{
	}

public:
	double beginRatio;
	double endRatio;
};

class SceneMain
{
public:
	SceneMain(void);
	void Initialize(void);
	void FinalizeScene(void);

	void ClearObjects(void);

	void ImportFileAutoFmt(const QString& path);
	bool ImportFile(const std::string& filename, geom::GeomFileFormat fmt);

	void LoadDefaultMatTexture(const std::string& filename);

	// オブサーバ制御
	void AddObserver(SceneObserver* observer);
	bool RemoveObserver(size_t idx);
	bool RemoveObserver(SceneObserver* registered_observer);

	void ReportDoneEditGeometry(void);

	lm::range3f GetSceneBBox(void) const;
	lm::range3f GetSceneBBoxIni(void) const;
	lm::range3f GetSceneTransformedBBox(void) const;

	void UpdateTransform(void);

	GeomObject* GetPrimaryObject(void);
	const GeomObject* GetPrimaryObject(void) const;
	int GetPrimaryObjectIdx(void) const;

	void GetSelectedMeshes(std::vector<MeshBuf*>& meshes);
	MeshBuf* GetPrimaryMeshbuf(void);
	const MeshBuf* GetPrimaryMeshbuf(void) const;
	int GetPrimaryMeshbufIdx(void) const;
	MeshBuf* GetSelOrFirstMeshbuf(void);
	const MeshBuf* GetSelOrFirstMeshbuf(void) const;

	GeomTextureSet* GetSelectedTexture(void);
	lib_geo::BaseMaterial* GetSelectedMaterial(void);

	void RemoveItem(int sel_idx);

	void CreateSampleTexture(SampleTextureBuilder::TextureType tex_type);

	void RefreshObjectIndex(void);

	bool ReloadObject(GeomObject* obj);

	int FindObjectIdx(const GeomObject* obj) const;

	void ShowAllObject(void);
	void HideAllObject(void);

	void ClearAllVertSelect(void);

	void SetFrame(int frame);

	void AddCrossSectionRecord();
	void ClearCrossSectionRecord();
	void GetFreeCutParam(lm::vec3f& p, lm::vec3f& n);
	void UpdateCrossSectionIfRequire(bool force_update);

	GeomObject* CreateNewGeometry(void);

	std::vector<MeshBuf*> GetCurSelMeshes(void);

	void FlipCurselFace(bool normal_only);
	void RebuildCurselNormal(void);

	int GetKeyframeMax(void) const;

	float GetCursorSphereClipLen(void) const;
	float GetCursorSphereClipLen(const lm::range3f& scene_bb) const;
	bool IsOutOfSphereClip(const lm::vec3f& v) const;
	bool IsOutOfSphereClip(const lm::range3f& scene_bb, const lm::vec3f& v) const;

private:
	void AddObjMeshsToSceneEachObject(const std::string& filename, lib_geo::ObjMesh& obj_mesh, std::vector<lib_geo::BaseMesh>& mesh_ary);
	void AddObjMeshsToSceneEachGroup(const std::string& filename, lib_geo::ObjMesh& obj_mesh, std::vector<lib_geo::BaseMesh>& mesh_ary);
	void OnPostLoadObj(GeomObject* geom, const lib_geo::ObjMesh& obj_mesh);


public:
	boost::ptr_vector<GeomObject> m_Objects;

	Selection3D          m_Sels;

	lib_graph::Material  m_DefaultMaterial;
	lib_graph::Material  m_DefaultAlphaMaterial;
	gl::GlTexture        m_DefaultTexture;

	SceneConfig          m_Config;

	SceneTransform       m_WorldTransform;
	TextureTransform     m_TextureTransform;

	gl::TextureConfig    m_TexConfig;

	IOConfig m_IOConfig;

	gl::EnvImage         m_EnvImg;
	gl::MatcapImage      m_MatcapImg;
	float                m_ConstantColorStrength;

	ShadowConfig         m_ShadowConfig;

	Cursor3D             m_Cursor3d;

	CrossSectionConfig   m_CrossSectionConfig;

	int m_CurFrame;

	IndexRangeConfig m_IndexVisrange;

private:
	std::vector<SceneObserver*> m_Observers;
};
