#pragma once

#include <LibQtGeoViewerCore/SceneMain.h>
#include <LibQtGeoViewerCore/Cursor3D.h>
#include <LibQtGeoViewerCore/Camera.h>

#include "QtViewBase.h"

#include "GeometryRender.h"
#include "View3DConfig.h"
#include "CloseVertInfo.h"
#include "Modifier.h"
#include "GLWidgetList.h"
#include "CameraRecords.h"
#include "SelRange.h"
#include "PostProcBuffer.h"

#include "ShaderLibrary.h"
#include "Shader/ShaderInterface.h"

#include "PostprocLibrary.h"
#include "Postproc/PostprocInterface.h"

#include "../GLUtility/FBOPicker.h"

#include <C2/gl/Projection.h>
#include <C2/gl/Manipulator.h>
#include <C2/gl/Light.h>

#include <C2/util/optional.h>

#include <QGestureEvent>

using namespace gl;


namespace lib_gl
{
class GlPicker;
}


class PickedMeshSubface
{
public:
	PickedMeshSubface(void) :
		mbuf(NULL)
	{
	}

public:
	MeshBuf* mbuf;
	lib_geo::SubfaceIdx subf;
	lm::vec3f closePos;
};


class View3D :
	public QtViewBase,
	public SceneObserver
{
	Q_OBJECT

public:
	View3D(void);

	virtual bool InitializeView(MyGLWidget* ParentWidget);
	void RegisterScene(SceneMain* scene);

	void SetDfSpLightPower(float rat);
	void SetAmbientLightPower(float rat);

	virtual void FinalizeView(void);

	void BeginRender(void);
	void EndRender(void);

	virtual void OnPaint(void);
	virtual void OnResize(int width, int height);

	virtual void OnMousePress(QMouseEvent *e);
	virtual void OnMouseRelease(QMouseEvent *e);
	virtual void OnMouseDoubleClick(QMouseEvent *e);
	virtual void OnMouseMove(QMouseEvent *e);

	virtual void OnWheel(QWheelEvent *e);

	virtual void OnGesturePan(QPanGesture *gesture);
	virtual void OnGesturePinch(QPinchGesture *gesture);

	virtual void OnKeyPress(QKeyEvent *e);
	virtual void OnKeyRelease(QKeyEvent *e);
	bool KeyPressOnRotCamera(QKeyEvent *e, const Modifier& m);

	// SceneObserver通知イベント
	virtual void OnGeometryBuild(const SceneMain& scene);

	void ClearRenderMap(void);

	void ReleaseRenderbuffer(MeshBuf* mbuf);

	void RecordCamera(void);
	void MoveToRecordedCamera(int camera_idx, bool animation);
	void RemoveRecordedCamera(int camera_idx);
	void ClearRecordedCamera(void);

	void ResetPrimaryObjectLists(void);
	void ReleaseAllRenderBuffer(void);

	void DrawCloseVertInfo(void);
	void DrawGeomMeshBound(void);

	void DrawCutPlane(void) const;
	void DrawObjectsBBox(void) const;

	void RepaintParent(void);

	std::vector<int> PickObjectAroundMouse(const QPoint& pos, int sel_range);
	bool IsInRange(int x, int y, const QImage& bmp) const;
	int QColorToIndex(const QColor& c) const;

	float GetGeomDepth(int x, int y);
	util::Optional<lm::vec3f> GetGeomDepht3DPos(int x, int y);

	void LookDepth(void);
	bool LookPixelDepth(int x, int y);
	void Look3DCursor(void);
	void ResetCursorMeasure(void);

	void MoveCaemraTo(ViewPoint vp);
	void MoveLookPosToCenter(void);
	void MoveLookPosToOrigin(void);
	void AdjustCameraDistAuto(void);

	void ReleaseAllIndexList(void);

	bool PickFace3D(PickedMeshSubface& picked, const lm::vec3f& pos);
	bool PickFace2D(PickedMeshSubface& picked, int px, int py);

protected:
	void PaintMain(bool showOnlyGeom);
	void Render3D(Camera& camera, bool showOnlyGeom);
	void Render3DScene(Camera& camera, bool showOnlyGeom);
	void Render3DWithPostproc(Camera& camera);
	void UpdatePostProcContext(Camera& camera);
	void RenderClipRange(void);

	void RenderBillboard(void);
	void RenderSelRange(void);
	void RenderShadowmapBuf(void);

	void ClearRenderBuffer(void);
	void SetDefalutLightState(const Camera& camera);
	void SetDefalutCullState(void);

	void DrawAllGeom(bool UseShader);
	void DrawAllGeomMeshStd(bool UseShader, std::vector<MeshBuf*>& meshes);
	void DrawAllGeomVLinks(std::vector<MeshBuf*>& meshes);
	void DrawAllGeomBone(void);
	void DrawBone(SceneNode* n, int frame, const lm::matrix4f& m);

	void DrawAllGeomPolyline(std::vector<MeshBuf*>& meshes);
	void DrawPolyline(MeshBuf& mbuf);
	void DrawPolylineHighlight(MeshBuf& mbuf);
	void DrawPolylineGeom(const MeshBuf& mbuf, const lib_geo::BasePolyline& pl);
	void DrawAllGeomMeshIndexColor(int index_offset);

	void DrawOptions_Before(void);
	void DrawOptions_After(void);

	void DrawGeomMeshHighlight(void);
	void DrawSelVert(MeshBuf& mbuf, int vert_idx);
	void PickMouseClosePoint(int x, int y);
	void DrawSelection_VertSel(void);
	void GetSelVertsFromPickBuf(lib_gl::GlPicker& picker, unsigned int depth);
	void DrawVertexLinker(MeshBuf& mbuf);
	void DrawLookPos(void);

	void DrawEnvImage(Camera& camera);
	void DrawGround(void);
	void DrawAxis(void);
	void DrawMiniAxis(void);
	void Draw3DCursor(void);
	void Draw3DCursorAxis(const lm::vec3f& p, const lm::vec3f& c);
	void Draw3DCursorCoord(const lm::vec3f& p);
	void Draw3DCursorMeasure(void);
	void Draw3DCursorStrokes(void);
	void Draw3DCursorFreecutGuide(void);
	void Draw3DCursorBary(void);

	void DrawAllGeomCrossSection(void);
	void DrawAllGeomCrossSectionLine(void);

	void BeginCutPlaneConfig(bool forCut);
	void EndCutPlaneConfig(void);

	void DrawClipRangeGraph(Camera& camera);

	void GetClipLimit(float& t, float& b) const;

	void DrawRenderTime(void);

	// mouse events
	bool MouseMove_OnSelRange        (QMouseEvent* e, QPointF& diff);
	bool MouseMove_OnZoomSelRange    (QMouseEvent* e, QPointF& diff);
	bool MouseMove_OnUpdateCloseVert (QMouseEvent* e, QPointF& diff);
	bool MouseMove_OnCameraControl   (QMouseEvent* e, QPointF& diff);
	bool MouseMove_OnLightControl    (QMouseEvent* e, QPointF& diff);
	bool MouseMove_OnCursorControl   (QMouseEvent* e, QPointF& diff);

	lm::vec3f Move3DCursor(lm::vec3f& cp, double px, double py);

	bool UpdateCloseVert(QMouseEvent* e);

	void DrawCenterCross(void);

	bool IsRequireUpdateShadowmap(void);

	ShaderInterface* GetPrimaryShader(void);
	void SetShaderResources(void);

	void UpdateShadowmap(void);
	void DrawAllGeomForShadowbuf(void);

	void SelectVertsSelRange(bool select_add);

	void DrawLightPos(const lib_gl::Light& light);

	void PushAndIdentityGLModelProjMatrix(void);
	void PushAndIdentityGLAllMatrix(void);
	void PopGLModelProjMatrix(void);
	void PopGLAllMatrix(void);

	void PushBeginGLMat(GLenum mode);
	void PopEndGLMat(GLenum mode);

	void SelectClosestVertOnMouseDown(QMouseEvent *e);
	void SelectObjectOnMouseDown(QMouseEvent *e);

	bool CheckCameraIsReverse(void) const;

private:
	GeometryRender& GetRender(MeshBuf* mbuf);

	void RenderCurrentTextureToBillboard(int left, int top, int width, int height);

	void ZoomSelRange(void);

	bool IsVisibleObject(const GeomObject& obj) const;
	bool IsVisibleMBuf(const MeshBuf& mbuf) const;

	void TestOnCursorColseElement(void);

	void UpdateModifierTip(QKeyEvent* e, const Modifier& m);
	bool IsModifierEvent(QKeyEvent* e);

	void GetAllVisibleMeshes(std::vector<MeshBuf*>& meshe);

	void LockMouseMoveDir(Modifier& m, float& mx, float& my);

signals:
	void SelectedObjectChanged(int sel_obj, int sel_mesh);
	void SelectedMatChanged(int sel_idx);

	void CameraMoved(void);
	void StatusTipChanged(QString msg);
	void SequenceStepped(int step);

	void CursorMoved(void);

public:
	GLWidgetList* m_Widgets;

	CameraRecords m_CameraRecord;

	lib_gl::Light m_HeadLight;    //!< カメラと同位置にある, 車のヘッドライト状のライト
	lib_gl::Light m_ControlLight; //!< マウスで操作するライト

	View3DConfig m_Config;

	SceneMain* m_Scene;

	ShaderLibrary m_ShaderLib;
	ShaderContext m_ShaderContext;

	PosprocLibrary m_PPLib;
	PostprocContext m_PPContext;
	MultPathRenderBuf m_PPBuf;

	lgr::color4f m_BGColor;

	float m_GridAxisScale;

	bool m_FpsMode;

private:
	boost::ptr_map<const MeshBuf*, GeometryRender> m_RenderMap;

	bool m_GlewInitialized;

	int m_LastRenderTime;

	SelRange m_SelRange;
	SelRange m_ZoomSelRange;

	CloseVertInfo m_CloseVertInfo;

	QPointF m_LastMousePos;

	FBOPicker m_FboPicker;
};
