#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>

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 SceneMain
{
public:
	// TODO : 読み込み時に分離するのではなく, 分割情報を保持しておいたほうがよさそう.
	enum ObjSplitConfig
	{
		ObjSplit_None,
		ObjSplit_Object,
		ObjSplit_Group,
	};

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

	void ClearObjects(void);

	geom::GeomFileFormat GetGeomFileTypeFromPath(const QString& path) const;
	bool IsGeomFile(const QString& path) const;
	GeomFileFormat GetGeomFileTypeByFileExt(const QString& ext) const;

	void LoadFileAutoFmt(const QString& path);

	bool LoadFile(const std::string& filename, geom::GeomFileFormat fmt);
	bool LoadObj(const std::string& filename);
	bool LoadStl(const std::string& filename);
	bool LoadPly(const std::string& filename);
	bool LoadMqo(const std::string& filename);
	bool LoadCollada(const std::string& filename);
	bool LoadPmd(const std::string& filename);

	void AddSampleShape(void);
	void AddGroundPlane(void);

	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 GetSceneTransformedBBox(void) const;

	void UpdateTransform(void);

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

	void RemoveItem(int sel_idx);

	void CreateSampleTexture(SampleTextureBuilder::TextureType tex_type);

	void RefreshObjectIndex(void);

	void AddObject(GeomObject* obj);

	bool ReloadObject(GeomObject* obj);

	int FindObjectIdx(const GeomObject* obj) const;

	void ShowAllObjects(void);

	void ClearAllVertSelect(void);

	void SetFrame(int frame);
	int GetMaxFrame(void) const;

	std::vector<GeomObject*> GetVisibleObjects(void);

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

private:
	void AdditionalInitializeOfGeom(GeomObject* geom);

	void SetObjectNameByFilepath(GeomObject& geom, const std::string& filepath) const;
	std::string GetFileTitle(const std::string& filepath) const;

	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);

	GeomObject* CreateNewGeometry(void);


public:
	boost::ptr_vector<GeomObject> m_Objects;
	int                           m_SelectedObjectIdx;
	int                           m_SelectedMaterialIdx;

	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;

	ObjSplitConfig       m_ObjSplitMode;

	gl::EnvImage         m_EnvImg;
	gl::MatcapImage      m_MatcapImg;

	ShadowConfig         m_ShadowConfig;

	Cursor3D             m_Cursor3d;

	CrossSectionConfig   m_CrossSectionConfig;

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