#include "StdAfx.h"
#include "GuiConfig.h"

#include <QXmlStreamWriter>
#include <QDomDocument>

#include <Qt5Utility/QXmlExt.h>

#include "View3DConfig.h"

#include <QFile>

#include <C2/util/optional.h>



class NodeValueMap
{
public:
	void Create(QDomNode& node)
	{
		values.clear();

		QXML_ELEM_FOREACH(n, node)
		{
			values[n.nodeName()] = n.firstChild().nodeValue();
		}
	}

	util::Optional<QString> GetValue(const QString& name)
	{
		auto i = values.find(name);
		if (i == values.end())
			return util::Optional<QString>();

		return util::Optional<QString>(i->second);
	}

public:
	std::map<QString, QString> values;
};



template<typename T>
void WriteNamed(QXmlStreamWriter& x, const NamedValue<T>& v)
{
	qxe::WriteBool(x ,v.GetName(), v);
}

bool ReadNamed(NodeValueMap& nm, NamedValue<bool>& v)
{
	util::Optional<QString> s = nm.GetValue(v.GetName());
	if (!s.IsValid())
		return false;

	v = (s.Get() == "True");
	return true;
}


class SerializeBase
{
public:
	virtual void Serialize(NamedValue<bool>& v) = 0;
};


class WriteSerialize : public SerializeBase
{
public:
	WriteSerialize(QXmlStreamWriter* _x) : x(_x) {}

	virtual void Serialize(NamedValue<bool>& v)
	{
		qxe::WriteBool(*x ,v.GetName(), v);
	}

public:
	QXmlStreamWriter* x;
};

class ReadSerialize : public SerializeBase
{
public:
	ReadSerialize(NodeValueMap* _nm) : nm(_nm) {}

	virtual void Serialize(NamedValue<bool>& v)
	{
		util::Optional<QString> s = nm->GetValue(v.GetName());
		if (!s.IsValid())
			return;

		v = (s.Get() == "True");
	}

public:
	NodeValueMap* nm;
};


void Serialize_Config_ViewOption(SerializeBase& s, View3DConfig& config_3d)
{
	s.Serialize( config_3d.m_DrawFace            );
	s.Serialize( config_3d.m_DrawWire            );
	s.Serialize( config_3d.m_DrawVert            );
	s.Serialize( config_3d.m_EnableVertexColor   );
	s.Serialize( config_3d.m_VertColorTo01       );
	s.Serialize( config_3d.m_DrawPolyline        );
	s.Serialize( config_3d.m_DrawPolylineIdx     );
	s.Serialize( config_3d.m_DrawPolylineLen     );
	s.Serialize( config_3d.m_DrawPolylineLenWhl  );
	s.Serialize( config_3d.m_DrawVid             );
	s.Serialize( config_3d.m_ShowSelVertCoord    );
	s.Serialize( config_3d.m_ShowSelVertIdx      );
	s.Serialize( config_3d.m_ShowMeshBound       );
	s.Serialize( config_3d.m_DrawFid             );
	s.Serialize( config_3d.m_DrawBBox            );
	s.Serialize( config_3d.m_DrawBBoxRange       );
	s.Serialize( config_3d.m_DrawVertNormal      );
	s.Serialize( config_3d.m_ShowVidTopMost      );
	s.Serialize( config_3d.m_DrawBone            );

	s.Serialize( config_3d.m_EnableLighting      );
	s.Serialize( config_3d.m_LightIsDirectional  );
	s.Serialize( config_3d.m_EnableTexture       );
	s.Serialize( config_3d.m_EnableFlatShade     );
	s.Serialize( config_3d.m_EnableCullFace      );
	s.Serialize( config_3d.m_DoubleSideShading   );
	s.Serialize( config_3d.m_CullAngle_F         );
	s.Serialize( config_3d.m_EnableCoverageTrans );
	s.Serialize (config_3d.m_SeparateSpecular    );

	s.Serialize( config_3d.m_DrawAxis            );
	s.Serialize( config_3d.m_DrawMiniAxis        );
	s.Serialize( config_3d.m_DrawGround          );
	s.Serialize( config_3d.m_DrawLightPosition   );
	s.Serialize( config_3d.m_DrawLookPos         );
	s.Serialize( config_3d.m_DrawCameraMeasure   );

	s.Serialize( config_3d.m_HighlightSelected     );
	s.Serialize( config_3d.m_HighlightSelectedWire );
	s.Serialize( config_3d.m_HighlightMaterial     );
	s.Serialize( config_3d.m_ShowOnlySelect        );

	s.Serialize( config_3d.m_ShowCloseVertInfo   );
	s.Serialize( config_3d.m_PickTransparent     );

	s.Serialize( config_3d.m_ShowRenderTime      );
	s.Serialize( config_3d.m_DrawRenderRange     );
	s.Serialize( config_3d.m_DrawCameraRecord    );

	s.Serialize( config_3d.m_SyncLightTocamera   );

	s.Serialize( config_3d.m_FlipMouseMR         );
}

bool GuiConfig::SaveConfigT(const char* filename)
{
	QString fn = QString::fromLocal8Bit(filename);

	QFile f(fn);
	if(!f.open(QIODevice::WriteOnly))
		return false;

	QXmlStreamWriter x(&f);
	x.setAutoFormatting(true);

	View3DConfig& config_3d = *m_Config3D;

	SceneConfig& scene_config = m_Scene->m_Config;

	m_TextureFlipY = m_Scene->m_TextureTransform.m_FlipY;
	m_TexSampleMode = m_Scene->m_TexConfig.SampleModeToStr();

	x.writeStartDocument();
	{
		x.writeStartElement("Config");
		{
			x.writeStartElement("ViewOption");
			Serialize_Config_ViewOption(WriteSerialize(&x), config_3d);
			x.writeEndElement();

			x.writeStartElement("Acc");
			{
				qxe::WriteBool( x , config_3d.m_EnableWireDispList.GetName() , config_3d.m_EnableWireDispList );
				qxe::WriteBool( x , config_3d.m_EnableFaceVBO.GetName()      , config_3d.m_EnableFaceVBO      );
				qxe::WriteBool( x , config_3d.m_EnableWireVBO.GetName()      , config_3d.m_EnableWireVBO      );
			}
			x.writeEndElement();
			
			x.writeStartElement("Texture");
			{
				qxe::WriteBool( x , m_TextureFlipY.GetName() , m_TextureFlipY );
				qxe::WriteText( x , m_TexSampleMode.GetName() , m_TexSampleMode );
				qxe::WriteBool( x , m_AutFitUVView.GetName() , m_AutFitUVView );
			}
			x.writeEndElement();
			
			x.writeStartElement("Geometry");
			{
				qxe::WriteBool( x , scene_config.m_EnableAutoReisze.GetName()    , scene_config.m_EnableAutoReisze    );
				qxe::WriteBool( x , scene_config.m_EnableAutoCentering.GetName() , scene_config.m_EnableAutoCentering );
			}
			x.writeEndElement();

			x.writeStartElement("Coordinate");
			{
				qxe::WriteInt  ( x , m_CoordType.GetName() , m_CoordType );
				qxe::WriteBool ( x , m_RevZ.GetName()      , m_RevZ      );
			}
			x.writeEndElement();

			x.writeStartElement("Shader");
			{
				const char* current_name = ShaderLibrary::GetTypeToName(config_3d.m_ShaderMode);
				x.writeTextElement( "CurrentShader" , current_name );
			}
			x.writeEndElement();

			x.writeStartElement("EnvImg");
			{
				qxe::WriteBool ( x , "ShowEnvSphere"       , m_Scene->m_EnvImg.m_VisibleEnvSphere   );
				qxe::WriteBool ( x , "EnableEnvReflection" , m_Scene->m_EnvImg.m_IsEnableReflection );
			}
			x.writeEndElement();
			
			x.writeStartElement("Shadow");
			{
				qxe::WriteBool ( x , "EnableShadow"     , m_Scene->m_ShadowConfig.m_EnableShadow     );
				qxe::WriteBool ( x , "EnableSoftShadow" , m_Scene->m_ShadowConfig.m_EnableSoftShadow );
			}
			x.writeEndElement();

			x.writeStartElement("Cursor");
			{
				Cursor3D& cursor = m_Scene->m_Cursor3d;
				qxe::WriteBool ( x , cursor.ShowCursor     .GetName() , cursor.ShowCursor     );
				qxe::WriteBool ( x , cursor.CursorDepth    .GetName() , cursor.CursorDepth    );
				qxe::WriteBool ( x , cursor.ShowMeasure    .GetName() , cursor.ShowMeasure    );
				qxe::WriteBool ( x , cursor.ShowMeasureLen .GetName() , cursor.ShowMeasureLen );
				qxe::WriteBool ( x , cursor.ShowMeasureXYZ .GetName() , cursor.ShowMeasureXYZ );
				qxe::WriteBool ( x , cursor.ShowAxis       .GetName() , cursor.ShowAxis       );
				qxe::WriteBool ( x , cursor.ShowCoord      .GetName() , cursor.ShowCoord      );
				qxe::WriteBool ( x , cursor.CheckBaryCoord .GetName() , cursor.CheckBaryCoord );
				qxe::WriteBool ( x , cursor.SelCloseMat    .GetName() , cursor.SelCloseMat    );
				qxe::WriteBool ( x , cursor.RecordStroke   .GetName() , cursor.RecordStroke   );
				qxe::WriteBool ( x , cursor.ShowStrokeLen  .GetName() , cursor.ShowStrokeLen  );
				qxe::WriteBool ( x , cursor.TranspStorke   .GetName() , cursor.TranspStorke   );
			}
			x.writeEndElement();

			x.writeStartElement("GUIMain");
			{
				qxe::WriteBool ( x , m_EnableAllFeatures.GetName() , m_EnableAllFeatures );
			}
			x.writeEndElement();

			x.writeStartElement("IOConfig");
			{
				qxe::WriteBool ( x , m_Scene->m_IOConfig.ObjSplit.GetName() , m_Scene->m_IOConfig.ObjSplit );
			}
			x.writeEndElement();

			x.writeStartElement("RecentFiles");
			for (size_t i = 0; i < m_RecentFiles.size(); ++i)
			{
				x.writeTextElement("Path", m_RecentFiles[i]);
			}
			x.writeEndElement();
		}
		x.writeEndElement();
	}
	x.writeEndDocument();

	f.close();

	return true;
}

bool GuiConfig::LoadConfigT(const char* filename)
{
	QString fn = QString::fromLocal8Bit(filename);

	QFile* f = new QFile(fn);
	if(!f->open(QIODevice::ReadOnly | QIODevice::Text))
	{
		delete f;
		return false;
	}

	QDomDocument xml;
	xml.setContent(f);

	View3DConfig& config_3d = *m_Config3D;

	SceneConfig& scene_config = m_Scene->m_Config;

	QXML_ELEM_FOREACH(n_root, xml)
	{
		if (n_root.nodeName() == "Config")
		{
			QXML_ELEM_FOREACH(n_config, n_root)
			{
				QString n_config_name = n_config.nodeName();
				if(n_config_name == "ViewOption")
				{
					NodeValueMap nm;
					nm.Create(n_config);

					Serialize_Config_ViewOption(ReadSerialize(&nm), config_3d);
				}
				else if(n_config_name == "Acc")
				{
					QXML_ELEM_FOREACH(n_acc, n_config)
					{
						if(false){}
						else if( n_acc.nodeName() == config_3d.m_EnableWireDispList.GetName() ) config_3d.m_EnableWireDispList = qxe::ReadBool(n_acc);
						else if( n_acc.nodeName() == config_3d.m_EnableFaceVBO.GetName()      ) config_3d.m_EnableFaceVBO      = qxe::ReadBool(n_acc);
						else if( n_acc.nodeName() == config_3d.m_EnableWireVBO.GetName()      ) config_3d.m_EnableWireVBO      = qxe::ReadBool(n_acc);
					}
				}
				else if(n_config_name == "Texture")
				{
					QXML_ELEM_FOREACH(n_tex, n_config)
					{
						if(false){}
						else if( n_tex.nodeName() == m_TextureFlipY.GetName()  ) m_TextureFlipY  = qxe::ReadBool(n_tex);
						else if( n_tex.nodeName() == m_TexSampleMode.GetName() ) m_TexSampleMode = qxe::ReadText(n_tex);
						else if( n_tex.nodeName() == m_AutFitUVView.GetName()  ) m_AutFitUVView  = qxe::ReadBool(n_tex);
					}
				}
				else if(n_config_name == "Geometry")
				{
					QXML_ELEM_FOREACH(n_obj, n_config)
					{
						if(false){}
						else if( n_obj.nodeName() == scene_config.m_EnableAutoReisze.GetName()    ) scene_config.m_EnableAutoReisze    = qxe::ReadBool(n_obj);
						else if( n_obj.nodeName() == scene_config.m_EnableAutoCentering.GetName() ) scene_config.m_EnableAutoCentering = qxe::ReadBool(n_obj);
					}
				}
				else if(n_config_name == "Coordinate")
				{
					QXML_ELEM_FOREACH(n_obj, n_config)
					{
						if(false){}
						else if( n_obj.nodeName() == m_CoordType.GetName() ) m_CoordType = qxe::ReadInt(n_obj);
						else if( n_obj.nodeName() == m_RevZ.GetName()      ) m_RevZ      = qxe::ReadBool(n_obj);
					}
				}
				else if(n_config_name == "Shader")
				{
					QXML_ELEM_FOREACH(n_obj, n_config)
					{
						if( n_obj.nodeName() == "CurrentShader" )
						{
							QString s = n_obj.firstChild().nodeValue();
							ShaderType t = ShaderLibrary::GetNameToType(s.toLocal8Bit().data());
							if(t != ShaderType::None)
							{
								config_3d.m_ShaderMode = t;
							}
						}
					}
				}
				else if(n_config_name == "EnvImg")
				{
					QXML_ELEM_FOREACH(n_env, n_config)
					{
						if(false){}
						else if( n_env.nodeName() == "ShowEnvSphere"       ) m_Scene->m_EnvImg.m_VisibleEnvSphere   = qxe::ReadBool(n_env);
						else if( n_env.nodeName() == "EnableEnvReflection" ) m_Scene->m_EnvImg.m_IsEnableReflection = qxe::ReadBool(n_env);
					}
				}
				else if(n_config_name == "Shadow")
				{
					QXML_ELEM_FOREACH(n_shadow, n_config)
					{
						if(false){}
						else if( n_shadow.nodeName() == "EnableShadow"     ) m_Scene->m_ShadowConfig.m_EnableShadow     = qxe::ReadBool(n_shadow);
						else if( n_shadow.nodeName() == "EnableSoftShadow" ) m_Scene->m_ShadowConfig.m_EnableSoftShadow = qxe::ReadBool(n_shadow);
					}
				}
				else if(n_config_name == "Cursor")
				{
					QXML_ELEM_FOREACH(n_shadow, n_config)
					{
						if(false){}
						else if( n_shadow.nodeName() == m_Scene->m_Cursor3d.ShowCursor     .GetName() ) m_Scene->m_Cursor3d.ShowCursor     = qxe::ReadBool(n_shadow);
						else if( n_shadow.nodeName() == m_Scene->m_Cursor3d.CursorDepth    .GetName() ) m_Scene->m_Cursor3d.CursorDepth    = qxe::ReadBool(n_shadow);
						else if( n_shadow.nodeName() == m_Scene->m_Cursor3d.ShowMeasure    .GetName() ) m_Scene->m_Cursor3d.ShowMeasure    = qxe::ReadBool(n_shadow);
						else if( n_shadow.nodeName() == m_Scene->m_Cursor3d.ShowMeasureLen .GetName() ) m_Scene->m_Cursor3d.ShowMeasureLen = qxe::ReadBool(n_shadow);
						else if( n_shadow.nodeName() == m_Scene->m_Cursor3d.ShowMeasureXYZ .GetName() ) m_Scene->m_Cursor3d.ShowMeasureXYZ = qxe::ReadBool(n_shadow);
						else if( n_shadow.nodeName() == m_Scene->m_Cursor3d.ShowAxis       .GetName() ) m_Scene->m_Cursor3d.ShowAxis       = qxe::ReadBool(n_shadow);
						else if( n_shadow.nodeName() == m_Scene->m_Cursor3d.ShowCoord      .GetName() ) m_Scene->m_Cursor3d.ShowCoord      = qxe::ReadBool(n_shadow);
						else if( n_shadow.nodeName() == m_Scene->m_Cursor3d.CheckBaryCoord .GetName() ) m_Scene->m_Cursor3d.CheckBaryCoord = qxe::ReadBool(n_shadow);
						else if( n_shadow.nodeName() == m_Scene->m_Cursor3d.SelCloseMat    .GetName() ) m_Scene->m_Cursor3d.SelCloseMat    = qxe::ReadBool(n_shadow);
						else if( n_shadow.nodeName() == m_Scene->m_Cursor3d.RecordStroke   .GetName() ) m_Scene->m_Cursor3d.RecordStroke   = qxe::ReadBool(n_shadow);
						else if( n_shadow.nodeName() == m_Scene->m_Cursor3d.ShowStrokeLen  .GetName() ) m_Scene->m_Cursor3d.ShowStrokeLen  = qxe::ReadBool(n_shadow);
						else if( n_shadow.nodeName() == m_Scene->m_Cursor3d.TranspStorke   .GetName() ) m_Scene->m_Cursor3d.TranspStorke   = qxe::ReadBool(n_shadow);
					}
				}
				else if (n_config_name == "GUIMain")
				{
					QXML_ELEM_FOREACH(n_gui, n_config)
					{
						if (n_gui.nodeName() == m_EnableAllFeatures.GetName())
							m_EnableAllFeatures = qxe::ReadBool(n_gui);
					}
				}
				else if (n_config_name == "IOConfig")
				{
					QXML_ELEM_FOREACH(n_gui, n_config)
					{
						if (n_gui.nodeName() == m_Scene->m_IOConfig.ObjSplit.GetName())
							m_Scene->m_IOConfig.ObjSplit = qxe::ReadBool(n_gui);
					}
				}
				else if (n_config_name == "RecentFiles")
				{
					QXML_ELEM_FOREACH(n_path, n_config)
					{
						QString pn = n_path.nodeName();
						if (pn == "Path")
							m_RecentFiles.push_back(n_path.firstChild().nodeValue());
					}
				}
			}
		}
	}

	f->close();

	m_Scene->m_TextureTransform.m_FlipY = m_TextureFlipY;
	m_Scene->m_TexConfig.SetModeByStr(m_TexSampleMode);

	return true;
}



bool WindowConfig::SaveConfig(const char* filename)
{
	QString fn = QString::fromLocal8Bit(filename);

	QFile f(fn);
	if(!f.open(QIODevice::WriteOnly))
		return false;

	QXmlStreamWriter x(&f);
	x.setAutoFormatting(true);

	x.writeStartDocument();
	{
		x.writeStartElement("WindowLayout");
		{
			x.writeStartElement("Position");
			{
				qxe::WriteInt  ( x , "x"           , m_MainWinLeft   );
				qxe::WriteInt  ( x , "y"           , m_MainWinTop    );
				qxe::WriteInt  ( x , "width"       , m_MainWinWidth  );
				qxe::WriteInt  ( x , "height"      , m_MainWinHeight );
				qxe::WriteBool ( x , "is_maximize" , m_IsMaximized   );
			}
			x.writeEndElement();
		}
		x.writeEndElement();
	}
	x.writeEndDocument();

	f.close();

	return true;
}

bool WindowConfig::LoadConfig(const char* filename)
{
	QString fn = QString::fromLocal8Bit(filename);

	QFile* f = new QFile(fn);
	if(!f->open(QIODevice::ReadOnly | QIODevice::Text))
	{
		delete f;
		return false;
	}

	QDomDocument xml;
	xml.setContent(f);

	QXML_ELEM_FOREACH(n_root, xml)
	{
		if(n_root.nodeName() == "WindowLayout")
		{
			QXML_ELEM_FOREACH(n_config, n_root)
			{
				if(n_config.nodeName() == "Position")
				{
					QXML_ELEM_FOREACH(n_opt, n_config)
					{
						if(false){}
						else if( n_opt.nodeName() == "x"           ) m_MainWinLeft   = qxe::ReadInt(n_opt);
						else if( n_opt.nodeName() == "y"           ) m_MainWinTop    = qxe::ReadInt(n_opt);
						else if( n_opt.nodeName() == "width"       ) m_MainWinWidth  = qxe::ReadInt(n_opt);
						else if( n_opt.nodeName() == "height"      ) m_MainWinHeight = qxe::ReadInt(n_opt);
						else if( n_opt.nodeName() == "is_maximize" ) m_IsMaximized   = qxe::ReadBool(n_opt);
					}
				}
			}
		}
	}

	f->close();

	return true;
}
