#include "meshKernal.h"
#include <vtkDataSet.h>
#include <vtkPoints.h>
#include <vtkCell.h>
#include <QDataStream>
#include <QString>
#include <QDomDocument>
#include <QDomElement>
#include <QDomAttr>
#include <QDomText>
#include "ElementType.h"
#include <vtkIdList.h>
#include <vtkUnstructuredGrid.h>
#include <QDebug>

namespace MeshData
{
	int MeshKernal::idOffset = 0;
	int MeshKernal::pointIDOffset = 1;
	int MeshKernal::cellIDOffset = 1;

	MeshKernal::MeshKernal()
	{
		idOffset++;
		setID(idOffset);
		_pointIDOffset = pointIDOffset;
		_cellIDOffset = cellIDOffset;
//		_mesh = vtkSmartPointer<vtkUnstructuredGrid>::New();
		_specificColor.first = false;
		appendProperty(QObject::tr("Visible"), _visible);
	}

	void MeshKernal::setMeshData(vtkDataSet* dataset)
	{
		_mesh = dataset;
		_pointIDOffset = pointIDOffset;
		_cellIDOffset = cellIDOffset;
		pointIDOffset += dataset->GetNumberOfPoints();
		cellIDOffset += dataset->GetNumberOfCells();
		appendProperty(QObject::tr("Points"), (int)dataset->GetNumberOfPoints());
		appendProperty(QObject::tr("Cells"), (int)dataset->GetNumberOfCells());
	}

	vtkDataSet* MeshKernal::getMeshData()
	{
		return _mesh;
	}

	double* MeshKernal::getPointAt(const int index)
	{
		return _mesh->GetPoint(index);
	}

	vtkCell* MeshKernal::getCellAt(const int index)
	{
		return _mesh->GetCell(index);
	}

	bool MeshKernal::isVisible()
	{
		return _visible;
	}

	void MeshKernal::setVisible(bool v)
	{
		_visible = v;
		appendProperty(QObject::tr("Visible"), _visible);
	}

	void MeshKernal::dataToStream(QDataStream* s)
	{
		*s << _id << _name << _mesh->GetNumberOfPoints() << _mesh->GetNumberOfCells();
	}

	void MeshKernal::setID(int id)
	{
		DataBase::setID(id);
		if (id > idOffset)
			idOffset = id;
	}

	void MeshKernal::setPath(const QString& path)
	{
		_path = path;
	}

	QString MeshKernal::getPath()
	{
		return _path;
	}

	void MeshKernal::setPointIDOFfset(int offset)
	{
		_pointIDOffset = offset;
		if (pointIDOffset < offset)
			pointIDOffset = offset;
	}

	void MeshKernal::setCellIDOFfset(int offset)
	{
		_cellIDOffset = offset;
		if (_cellIDOffset < offset)
			cellIDOffset = offset;
	}

	QDomElement& MeshKernal::writeToProjectFile(QDomDocument* doc, QDomElement* parent)
	{
        QDomElement kernelele = doc->createElement("MeshKernel");
		QDomAttr idAttr = doc->createAttribute("ID");
		idAttr.setValue(QString::number(_id));
		kernelele.setAttributeNode(idAttr);
		QDomAttr visible = doc->createAttribute("Visible");
		visible.setValue("True");
		if (!_visible) visible.setValue("False");
		kernelele.setAttributeNode(visible);
		QDomElement nameele = doc->createElement("Name");
		QDomText nameText = doc->createTextNode(_name);
		nameele.appendChild(nameText);
		kernelele.appendChild(nameele);

		if (!_path.isEmpty())
		{
			QDomElement pathele = doc->createElement("Path");
			QDomText pathtext = doc->createTextNode(_path);
			pathele.appendChild(pathtext);
			kernelele.appendChild(pathele);
		}
 
		QDomElement idoffset = doc->createElement("IDOffset");
		QDomAttr nodeid = doc->createAttribute("Node");
		nodeid.setValue(QString::number(_pointIDOffset));
		QDomAttr cellid = doc->createAttribute("Cell");
		cellid.setValue(QString::number(_cellIDOffset));
		idoffset.setAttributeNode(nodeid);
		idoffset.setAttributeNode(cellid);
		kernelele.appendChild(idoffset);
		//节点
		QDomElement nodeListEle = doc->createElement("NodeList");
		const int nNode = _mesh->GetNumberOfPoints();
		for (int i = 0; i < nNode; ++i)
		{
			double* coor = _mesh->GetPoint(i);
			QDomElement node = doc->createElement("Node");
			QString s = QString("%1,%2,%3,%4").arg(i).arg(coor[0]).arg(coor[1]).arg(coor[2]);
			QDomText nodetext = doc->createTextNode(s);
			node.appendChild(nodetext);
			nodeListEle.appendChild(node);
		}
		kernelele.appendChild(nodeListEle);
		//单元
		QDomElement elementListEle = doc->createElement("ElementList");
		const int nele = _mesh->GetNumberOfCells();
		for (int i = 0; i < nele; ++i)
		{
			vtkCell* cell = _mesh->GetCell(i);
			QString stype = vtkCellTYpeToString((VTKCellType)cell->GetCellType());
			QString text = QString("%1,%2").arg(i).arg(stype);
			vtkIdList* ids = cell->GetPointIds();
			const int n = ids->GetNumberOfIds();
			QDomElement element = doc->createElement("Element");
			for (int i = 0; i < n; ++i)
			{
				int id = ids->GetId(i);
				text.append(",");
				text.append(QString("%1").arg(id));
			}
			QDomText eletext = doc->createTextNode(text);
			element.appendChild(eletext);
			elementListEle.appendChild(element);
		}

		kernelele.appendChild(elementListEle);

		parent->appendChild(kernelele);
		return kernelele;
	}

	void MeshKernal::readDataFromProjectFile(QDomElement* kernelele)
	{
		QString sid = kernelele->attribute("ID");
		this->setID(sid.toInt());
		QString svis = kernelele->attribute("Visible");
		bool visible = true;
		if (svis.toLower() == "false") visible = false;
		this->setVisible(visible);

		QDomNodeList nameNodeList = kernelele->elementsByTagName("Name");
		if (nameNodeList.size() != 1) return;
		QString name = nameNodeList.at(0).toElement().text();
		this->setName(name);
		QDomNodeList pathList = kernelele->elementsByTagName("Path");
		if (pathList.size() > 0)
		{
			QString path = pathList.at(0).toElement().text();
			this->setPath(path);
		}
		QDomNodeList idofflist = kernelele->elementsByTagName("IDOffset");
		if (idofflist.size() != 1) return;
		QDomElement idele = idofflist.at(0).toElement();
		QString spointoff = idele.attribute("Node");
		QString scelloff = idele.attribute("Cell");
		const int po = spointoff.toInt();
		const int co = scelloff.toInt();
		this->setPointIDOFfset(po);
		this->setCellIDOFfset(co);
		
		//读取网格
		vtkSmartPointer<vtkUnstructuredGrid> ung = vtkSmartPointer<vtkUnstructuredGrid>::New();
		//节点
		QDomNodeList nodelist = kernelele->elementsByTagName("Node");
		vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
		const int nNode = nodelist.size();
		for (int i = 0; i < nNode; ++i)
		{
			QDomElement nodeele = nodelist.at(i).toElement();
			QString text = nodeele.text();
			QStringList scoor = text.split(",");
			points->InsertNextPoint(scoor[1].toDouble(), scoor[2].toDouble(), scoor[3].toDouble());
		}
		ung->SetPoints(points);
		QDomNodeList elementList = kernelele->elementsByTagName("Element");
		const int nele = elementList.size();
		for (int i = 0; i < nele; ++i)
		{
			QDomElement eleele = elementList.at(i).toElement();
			QString text = eleele.text();
			QStringList info = text.split(",");
			VTKCellType celltype = TypeStringToVTKCellType(info.at(1));
			vtkSmartPointer<vtkIdList> idlist = vtkSmartPointer<vtkIdList>::New();
			for (int i = 2; i < info.size(); ++i)
			{
				int id = info.at(i).toInt();
				idlist->InsertNextId(id);
			}
			ung->InsertNextCell(celltype, idlist);
		}
	
		this->setMeshData(ung);
	}

	int MeshKernal::getPointCount()
	{
		if (_mesh != nullptr)

			return _mesh->GetNumberOfPoints();
		return -1;
	}

	int MeshKernal::getCellCount()
	{
		if (_mesh != nullptr)
			return _mesh->GetNumberOfCells();
		return -1;
	}

	void MeshKernal::resetOffset()
	{
		idOffset = 0;
		pointIDOffset = 1;
		cellIDOffset = 1;
	}

	void MeshKernal::setDimension(int d)
	{
		_dimension = d;
	}

	int MeshKernal::getDimension()
	{
		return _dimension;
	}

	void MeshKernal::writeBinaryFile(QDataStream* dataStream)
	{
		*dataStream << _id << _visible << _name << _path;
		*dataStream << _pointIDOffset << _cellIDOffset;
		
		const int nNode = _mesh->GetNumberOfPoints();
		*dataStream << nNode;;
		for (int i = 0; i < nNode; ++i)
		{
			double* coor = _mesh->GetPoint(i);
			*dataStream << i << coor[0] << coor[1] << coor[2];
		}

		const int nele = _mesh->GetNumberOfCells();
		*dataStream << nele;
		for (int i = 0; i < nele; ++i)
		{
			vtkCell* cell = _mesh->GetCell(i);
			int cellType = cell->GetCellType();
			vtkIdList* ids = cell->GetPointIds();
			const int n = ids->GetNumberOfIds();
			*dataStream << i << cellType << n;

			for (int i = 0; i < n; ++i)
			{
				int id = ids->GetId(i);
				*dataStream << id;
			}
		}
	}

	void MeshKernal::readBinaryFile(QDataStream* dataStream)
	{
		int KernalID = 0;                        //KernalID
		bool visible = false;                    //KernalVisible
		QString MeshName, MeshPath;              //MeshName,MeshPath
		*dataStream >> KernalID >> visible >> MeshName >> MeshPath;

		this->setID(KernalID);
		this->setVisible(visible);
		this->setName(MeshName);
		this->setPath(MeshPath);

		int nodeOffset = 0, cellOffset = 0;
		*dataStream >> nodeOffset >> cellOffset;
		this->setCellIDOFfset(cellOffset);
		this->setPointIDOFfset(nodeOffset);

		int nNode = 0;
		*dataStream >> nNode;
		vtkSmartPointer<vtkUnstructuredGrid> ung = vtkSmartPointer<vtkUnstructuredGrid>::New();
		vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
		int nodeid = 0;                                    //NodeID
		double node_x, node_y, node_z;                     //Nodecoordinate(x,y,z)
		for (int i = 0; i < nNode; ++i)
		{
			*dataStream >> nodeid >> node_x >> node_y >> node_z;
			points->InsertNextPoint(node_x, node_y, node_z);
		}
		ung->SetPoints(points);
		
		int nElement{ 0 }, eletype{ 0 }, ElementID{ 0 }, eleNodeCount{0};	//ElementNumber,ElementType,ElementID
		*dataStream >> nElement;
		for (int i = 0; i < nElement; ++i)
		{
			*dataStream >> ElementID >> eletype >>eleNodeCount;
			VTKCellType celltype = VTKCellType(eletype);
//			int nVertex = GetNodeCountByElementType(celltype);	//不同类型的element的节点个数
			vtkSmartPointer<vtkIdList> idlist = vtkSmartPointer<vtkIdList>::New();
		
			int id = 0;
			for (int i = 0; i < eleNodeCount; ++i)
			{
				*dataStream >> id;
				idlist->InsertNextId(id);
			}
			ung->InsertNextCell(celltype, idlist);
		}

		this->setMeshData(ung);
	}

	void MeshKernal::setGmshSetting(DataProperty::DataBase* data)
	{
		_gmshSetting = data;
	}

	DataProperty::DataBase* MeshKernal::getGmshSetting()
	{
		return _gmshSetting;
	}

	void MeshKernal::setSpecificColor(bool enable, QColor c)
	{
		_specificColor.first = enable;
		_specificColor.second = c;
	}

	void MeshKernal::setSpecificColor(bool enable, double r, double g, double b, double alpha)
	{
		_specificColor.first = enable;
		_specificColor.second.setRedF(r);
		_specificColor.second.setGreenF(g);
		_specificColor.second.setBlueF(b);
		_specificColor.second.setAlpha(alpha);
	}

	QColor MeshKernal::getSpecificColor(bool &isEnable)
	{
		isEnable = _specificColor.first;
		return _specificColor.second;
	}

}

