#include"Post_Base.h"
#include"../function/BF.h"
#include"../kernel/Nedelec3D_Util.h"

#include <fstream>
#include <iostream>
#include <iomanip>
#include <string>

void Post_2D_EigenMode::GetElectric()
{
	int nbrVertex = _mMesh->GetNbrVertex();
	int nbrEdge = _mMesh->GetNbrEdge();
	int nbrMode = _mSolver->GetNbrMode();

	_mEx = Eigen::MatrixXcd::Zero(nbrVertex, nbrMode);
	_mEy = Eigen::MatrixXcd::Zero(nbrVertex, nbrMode);
	_mEz = Eigen::MatrixXcd::Zero(nbrVertex, nbrMode);
	_mNormE = Eigen::MatrixXd::Zero(nbrVertex, nbrMode);

	//calculate Ez
	_mEz = _mX->block(0, 0, nbrVertex, nbrMode);
	Eigen::MatrixXcd Xt= _mX->block(nbrVertex, 0, nbrEdge, nbrMode);

	//calculate Ex Ey
	Eigen::MatrixXd numEt=Eigen::MatrixXd::Zero(nbrVertex, nbrMode);
	//reference vertex
	int NbrGuassPoints = 3;
	double* u, * v, * w;
	u = new double[NbrGuassPoints];
	v = new double[NbrGuassPoints];
	w = new double[NbrGuassPoints];
	u[0] = 0.; u[1] = 1.; u[2] = 0.;
	v[0] = 0.; v[1] = 0.; v[2] = 1.;
	w[0] = 0.; w[1] = 0.; w[2] = 0.;
	//init of geo
	Eigen::Vector3d* vertex = new Eigen::Vector3d[3];
	Eigen::Matrix3d Jac, InvJac;
	//init of basis function
	BF BF_Nedelec;
	Eigen::Vector3d** Et;
	int vdof=BF_Nedelec.GetNbrBF(TWODIM, TRIANGLE, BF_NEDELEC, BF_LINEFUNC);
	Et = new Eigen::Vector3d * [NbrGuassPoints];
	for (int i = 0; i < NbrGuassPoints; i++)
		Et[i] = new Eigen::Vector3d[vdof];
	//loop over nbrMode
	for (int numMode = 0; numMode < nbrMode; numMode++)
	{
		//loop over tri
		int NbrTri = _mMesh->GetNbrTri();
		for (int n = 0; n < NbrTri; n++)
		{
			//vertex inde
			int Mapping[3];
			for (int i = 0; i < 3; i++)
				Mapping[i] = _mMesh->GetTri(n, i);

			//coordinate of vertex
			for (int i = 0; i < 3; i++)
				_mMesh->GetVertex(Mapping[i], vertex[i]);

			//Jac
			Jac(0, 0) = vertex[1](0) - vertex[0](0); Jac(0, 1) = vertex[1](1) - vertex[0](1); Jac(0, 2) = 0.;
			Jac(1, 0) = vertex[2](0) - vertex[0](0); Jac(1, 1) = vertex[2](1) - vertex[0](1); Jac(1, 2) = 0.;
			Jac(2, 0) = 0.;			Jac(2, 1) = 0.;			Jac(2, 2) = 1.;
			InvJac = Jac.inverse();

			//basis function
			for (int i = 0; i < NbrGuassPoints; i++)
			{
				for (int j = 0; j < vdof; j++)
				{
					BF_Nedelec.GetValueBF(j + 1, u[i], v[i], w[i], Et[i][j]);
					Et[i][j] = InvJac * Et[i][j];
				}
			}

			//mapping 
			Eigen::VectorXi MappingIndexV = Eigen::VectorXi::Zero(vdof);
			for (int i = 0; i < vdof; i++)
				MappingIndexV(i) = _mMesh->GetEdgeOfTri(n, i);

			//electric value
			for (int i = 0; i < 3; i++)//nbrVertex
			{
				numEt(Mapping[i], numMode) = numEt(Mapping[i], numMode) + 1.;
				for (int j = 0; j < vdof; j++)
				{
					_mEx(Mapping[i], numMode) = _mEx(Mapping[i], numMode) + Et[i][j][0] * Xt(MappingIndexV(j), numMode);
					_mEy(Mapping[i], numMode) = _mEy(Mapping[i], numMode) + Et[i][j][1] * Xt(MappingIndexV(j), numMode);
				}
			}

		}//end loop over tri

		for (int i = 0; i < nbrVertex; i++)
		{
			_mEx(i, numMode) = _mEx(i, numMode) / numEt(i, numMode);
			_mEy(i, numMode) = _mEy(i, numMode) / numEt(i, numMode);
		}
	}//end loop over nbrMode

	for(int i=0;i< nbrVertex;i++)
		for (int j = 0; j < nbrMode; j++)
		{
			_mNormE(i, j) = sqrt(abs(_mEx(i, j) * _mEx(i, j)) + abs(_mEy(i, j) * _mEy(i, j)) + abs(_mEz(i, j) * _mEz(i, j)));
		}
	
	delete[] u, v, w, vertex;
	for (int i = 0; i < NbrGuassPoints; i++)
	{
		delete[] Et[i];
	}
	delete[] Et;
}

void Post_2D_EigenFreq::GetElectric()
{
	int nbrVertex = _mMesh->GetNbrVertex();
	int nbrEdge = _mMesh->GetNbrEdge();
	int nbrMode = _mSolver->GetNbrMode();

	_mEx = Eigen::MatrixXcd::Zero(nbrVertex, nbrMode);
	_mEy = Eigen::MatrixXcd::Zero(nbrVertex, nbrMode);
	_mEz = Eigen::MatrixXcd::Zero(nbrVertex, nbrMode);
	_mNormE = Eigen::MatrixXd::Zero(nbrVertex, nbrMode);

	//calculate Ez
	_mEz = _mX->block(0, 0, nbrVertex, nbrMode);
	Eigen::MatrixXcd Xt = _mX->block(nbrVertex, 0, nbrEdge, nbrMode);

	//calculate Ex Ey
	Eigen::MatrixXd numEt=Eigen::MatrixXd::Zero(nbrVertex, nbrMode);
	//reference vertex
	int NbrGuassPoints = 3;
	double* u, * v, * w;
	u = new double[NbrGuassPoints];
	v = new double[NbrGuassPoints];
	w = new double[NbrGuassPoints];
	u[0] = 0.; u[1] = 1.; u[2] = 0.;
	v[0] = 0.; v[1] = 0.; v[2] = 1.;
	w[0] = 0.; w[1] = 0.; w[2] = 0.;
	//init of geo
	Eigen::Vector3d* vertex = new Eigen::Vector3d[3];
	Eigen::Matrix3d Jac, InvJac;
	//init of basis function
	BF BF_Nedelec;
	Eigen::Vector3d** Et;
	int vdof = BF_Nedelec.GetNbrBF(TWODIM, TRIANGLE, BF_NEDELEC, BF_LINEFUNC);
	Et = new Eigen::Vector3d * [NbrGuassPoints];
	for (int i = 0; i < NbrGuassPoints; i++)
		Et[i] = new Eigen::Vector3d[vdof];
	//loop over nbrMode
	for (int numMode = 0; numMode < nbrMode; numMode++)
	{
		//loop over tri
		int NbrTri = _mMesh->GetNbrTri();
		for (int n = 0; n < NbrTri; n++)
		{
			//vertex inde
			int Mapping[3];
			for (int i = 0; i < 3; i++)
				Mapping[i] = _mMesh->GetTri(n, i);

			//coordinate of vertex
			for (int i = 0; i < 3; i++)
				_mMesh->GetVertex(Mapping[i], vertex[i]);

			//Jac
			Jac(0, 0) = vertex[1](0) - vertex[0](0); Jac(0, 1) = vertex[1](1) - vertex[0](1); Jac(0, 2) = 0.;
			Jac(1, 0) = vertex[2](0) - vertex[0](0); Jac(1, 1) = vertex[2](1) - vertex[0](1); Jac(1, 2) = 0.;
			Jac(2, 0) = 0.;			Jac(2, 1) = 0.;			Jac(2, 2) = 1.;
			InvJac = Jac.inverse();

			//basis function
			for (int i = 0; i < NbrGuassPoints; i++)
			{
				for (int j = 0; j < vdof; j++)
				{
					BF_Nedelec.GetValueBF(j + 1, u[i], v[i], w[i], Et[i][j]);
					Et[i][j] = InvJac * Et[i][j];
				}
			}

			//mapping 
			Eigen::VectorXi MappingIndexV = Eigen::VectorXi::Zero(vdof);
			for (int i = 0; i < vdof; i++)
				MappingIndexV(i) = _mMesh->GetEdgeOfTri(n, i);

			//electric value
			for (int i = 0; i < 3; i++)//nbrVertex
			{
				numEt(Mapping[i], numMode) = numEt(Mapping[i], numMode) + 1.;
				for (int j = 0; j < vdof; j++)
				{
					_mEx(Mapping[i], numMode) = _mEx(Mapping[i], numMode) + Et[i][j][0] * Xt(MappingIndexV(j), numMode);
					_mEy(Mapping[i], numMode) = _mEy(Mapping[i], numMode) + Et[i][j][1] * Xt(MappingIndexV(j), numMode);
				}
			}
		}//end loop over tri

		for (int i = 0; i < nbrVertex; i++)
		{
			_mEx(i, numMode) = _mEx(i, numMode) / numEt(i, numMode);
			_mEy(i, numMode) = _mEy(i, numMode) / numEt(i, numMode);
		}
	}//end loop over nbrMode

	for (int i = 0; i < nbrVertex; i++)
		for (int j = 0; j < nbrMode; j++)
		{
			_mNormE(i, j) = sqrt(abs(_mEx(i, j) * _mEx(i, j)) + abs(_mEy(i, j) * _mEy(i, j)) + abs(_mEz(i, j) * _mEz(i, j)));
		}

	delete[] u, v, w, vertex;
	for (int i = 0; i < NbrGuassPoints; i++)
	{
		delete[] Et[i];
	}
	delete[] Et;
}

void Post_2D_Scatter::GetElectric()
{
	int nbrVertex = _mMesh->GetNbrVertex();
	int nbrEdge = _mMesh->GetNbrEdge();

	_mEx = Eigen::VectorXcd::Zero(nbrVertex);
	_mEy = Eigen::VectorXcd::Zero(nbrVertex);
	_mEz = Eigen::VectorXcd::Zero(nbrVertex);
	_mNormE = Eigen::VectorXd::Zero(nbrVertex);
	//calculate Ez
	_mEz = _mX->block(0, 0, nbrVertex, 1);
	Eigen::VectorXcd Xt = _mX->block(nbrVertex, 0, nbrEdge, 1);
	//calculate Ex Ey
	Eigen::VectorXd numEt=Eigen::VectorXd::Zero(nbrVertex);
	//reference vertex
	int NbrGuassPoints = 3;
	double* u, * v, * w;
	u = new double[NbrGuassPoints];
	v = new double[NbrGuassPoints];
	w = new double[NbrGuassPoints];
	u[0] = 0.; u[1] = 1.; u[2] = 0.;
	v[0] = 0.; v[1] = 0.; v[2] = 1.;
	w[0] = 0.; w[1] = 0.; w[2] = 0.;
	//init of geo
	Eigen::Vector3d* vertex = new Eigen::Vector3d[3];
	Eigen::Matrix3d Jac, InvJac;
	//init of basis function
	BF BF_Nedelec;
	Eigen::Vector3d** Et;
	int vdof = BF_Nedelec.GetNbrBF(TWODIM, TRIANGLE, BF_NEDELEC, BF_LINEFUNC);
	Et = new Eigen::Vector3d * [NbrGuassPoints];
	for (int i = 0; i < NbrGuassPoints; i++)
		Et[i] = new Eigen::Vector3d[vdof];
	//loop over tri
	int NbrTri = _mMesh->GetNbrTri();
	for (int n = 0; n < NbrTri; n++)
	{
		//vertex inde
		int Mapping[3];
		for (int i = 0; i < 3; i++)
			Mapping[i] = _mMesh->GetTri(n, i);

		//coordinate of vertex
		for (int i = 0; i < 3; i++)
			_mMesh->GetVertex(Mapping[i], vertex[i]);

		//Jac
		Jac(0, 0) = vertex[1](0) - vertex[0](0); Jac(0, 1) = vertex[1](1) - vertex[0](1); Jac(0, 2) = 0.;
		Jac(1, 0) = vertex[2](0) - vertex[0](0); Jac(1, 1) = vertex[2](1) - vertex[0](1); Jac(1, 2) = 0.;
		Jac(2, 0) = 0.;			Jac(2, 1) = 0.;			Jac(2, 2) = 1.;
		InvJac = Jac.inverse();

		//basis function
		for (int i = 0; i < NbrGuassPoints; i++)
		{
			for (int j = 0; j < vdof; j++)
			{
				BF_Nedelec.GetValueBF(j + 1, u[i], v[i], w[i], Et[i][j]);
				Et[i][j] = InvJac * Et[i][j];
			}
		}

		//mapping 
		Eigen::VectorXi MappingIndexV = Eigen::VectorXi::Zero(vdof);
		for (int i = 0; i < vdof; i++)
			MappingIndexV(i) = _mMesh->GetEdgeOfTri(n, i);

		//electric value
		for (int i = 0; i < 3; i++)//nbrVertex
		{
			numEt(Mapping[i]) = numEt(Mapping[i]) + 1.;
			for (int j = 0; j < vdof; j++)
			{
				_mEx(Mapping[i]) = _mEx(Mapping[i]) + Et[i][j][0] * Xt(MappingIndexV(j));
				_mEy(Mapping[i]) = _mEy(Mapping[i]) + Et[i][j][1] * Xt(MappingIndexV(j));
			}
		}

	}//end loop over tri

	for (int i = 0; i < nbrVertex; i++)
	{
		_mEx(i) = _mEx(i) / numEt(i);
		_mEy(i) = _mEy(i) / numEt(i);
	}

	if (_mPhy->GetNbrBELE() > 0)
	{
		GetEb();
		for (int i = 0; i < nbrVertex; i++)
		{
			_mEx(i) = _mEx(i) + _mEbx(i);
			_mEy(i) = _mEy(i) + _mEby(i);
			_mEz(i) = _mEz(i) + _mEbz(i);

		}
	}

	for (int i = 0; i < nbrVertex; i++)
		_mNormE(i) = sqrt(abs(_mEx(i) * _mEx(i)) + abs(_mEy(i) * _mEy(i)) + abs(_mEz(i) * _mEz(i)));

	delete[] u, v, w, vertex;
	for (int i = 0; i < NbrGuassPoints; i++)
	{
		delete[] Et[i];
	}
	delete[] Et;
}

void Post_3D_Scatter::GetElectric()
{
	const int nbrVertex = _mMesh->GetNbrVertex();
	_mEx = Eigen::VectorXcd::Zero(nbrVertex);
	_mEy = Eigen::VectorXcd::Zero(nbrVertex);
	_mEz = Eigen::VectorXcd::Zero(nbrVertex);
	_mNormE = Eigen::VectorXd::Zero(nbrVertex);
	Eigen::VectorXd nCnt = Eigen::VectorXd::Zero(nbrVertex);

	const int elementOrder = (_mPhy != nullptr) ? _mPhy->GetElementOrder() : 1;
	const int bfOrder = Nedelec3D::bfOrderParam(elementOrder);

	BF bfN;
	bfN.GetNbrBF(THREEDIM, TETRAHEDRON, BF_NEDELEC, bfOrder);
	const int localDof = Nedelec3D::localDofPerTet(elementOrder);
	Eigen::Vector3d vertex[4];
	Eigen::Matrix3d Jac, InvJac;
	int map[20];

	const double u0[4] = { 1.0, 0.0, 0.0, 0.0 };
	const double v0[4] = { 0.0, 1.0, 0.0, 0.0 };
	const double w0[4] = { 0.0, 0.0, 1.0, 0.0 };

	for (int n = 0; n < _mMesh->GetNbrTet(); n++)
	{
		for (int i = 0; i < 4; i++)
			_mMesh->GetVertex(_mMesh->GetTet(n, i), vertex[i]);

		Jac(0, 0) = vertex[0](0) - vertex[3](0); Jac(0, 1) = vertex[0](1) - vertex[3](1); Jac(0, 2) = vertex[0](2) - vertex[3](2);
		Jac(1, 0) = vertex[1](0) - vertex[3](0); Jac(1, 1) = vertex[1](1) - vertex[3](1); Jac(1, 2) = vertex[1](2) - vertex[3](2);
		Jac(2, 0) = vertex[2](0) - vertex[3](0); Jac(2, 1) = vertex[2](1) - vertex[3](1); Jac(2, 2) = vertex[2](2) - vertex[3](2);
		const double detJ = Jac.determinant();
		if (std::abs(detJ) < 1e-30)
			continue;
		InvJac = Jac.inverse();

		if (elementOrder == 2)
			Nedelec3D::buildTetDofMap(_mMesh, n, 2, map, 20);

		double edgeLen[6] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
		if (_mPhy != nullptr && _mPhy->HasNumericPort() && elementOrder == 1)
		{
			edgeLen[0] = (vertex[0] - vertex[1]).norm();
			edgeLen[1] = (vertex[0] - vertex[2]).norm();
			edgeLen[2] = (vertex[0] - vertex[3]).norm();
			edgeLen[3] = (vertex[1] - vertex[2]).norm();
			edgeLen[4] = (vertex[1] - vertex[3]).norm();
			edgeLen[5] = (vertex[2] - vertex[3]).norm();
		}

		for (int j = 0; j < 4; j++)
		{
			Eigen::Vector3cd Esum = Eigen::Vector3cd::Zero();
			for (int i = 0; i < localDof; i++)
			{
				Eigen::Vector3d bf;
				bfN.GetValueBF(i + 1, u0[j], v0[j], w0[j], bf);
				bf = InvJac * bf;
				if (_mPhy != nullptr && _mPhy->HasNumericPort() && elementOrder == 1)
					bf *= edgeLen[i];
				const int dofId = (elementOrder == 2) ? map[i] : _mMesh->GetEdgeOfTet(n, i);
				const std::complex<double> xe = (*_mX)(dofId);
				Esum(0) += bf(0) * xe;
				Esum(1) += bf(1) * xe;
				Esum(2) += bf(2) * xe;
			}
			const int vid = _mMesh->GetTet(n, j);
			_mEx(vid) += Esum(0);
			_mEy(vid) += Esum(1);
			_mEz(vid) += Esum(2);
			nCnt(vid) += 1.0;
		}
	}

	for (int i = 0; i < nbrVertex; i++)
	{
		if (nCnt(i) > 0.0)
		{
			_mEx(i) /= nCnt(i);
			_mEy(i) /= nCnt(i);
			_mEz(i) /= nCnt(i);
		}
	}

	for (int i = 0; i < nbrVertex; i++)
		_mNormE(i) = sqrt(abs(_mEx(i) * _mEx(i)) + abs(_mEy(i) * _mEy(i)) + abs(_mEz(i) * _mEz(i)));

	if (_mPhy != nullptr && _mPhy->GetNbrBELE() > 0)
	{
		GetEb();
		for (int i = 0; i < nbrVertex; i++)
		{
			_mEx(i) += _mEbx(i);
			_mEy(i) += _mEby(i);
			_mEz(i) += _mEbz(i);
			_mNormE(i) = sqrt(abs(_mEx(i) * _mEx(i)) + abs(_mEy(i) * _mEy(i)) + abs(_mEz(i) * _mEz(i)));
		}
	}
}

void Post_3D_EigenFreq::GetElectric()
{
	const int nbrVertex = _mMesh->GetNbrVertex();
	const int nbrMode = _mSolver->GetNbrMode();

	_mEx = Eigen::MatrixXcd::Zero(nbrVertex, nbrMode);
	_mEy = Eigen::MatrixXcd::Zero(nbrVertex, nbrMode);
	_mEz = Eigen::MatrixXcd::Zero(nbrVertex, nbrMode);
	_mNormE = Eigen::MatrixXd::Zero(nbrVertex, nbrMode);

	BF bfN;
	bfN.GetNbrBF(THREEDIM, TETRAHEDRON, BF_NEDELEC, BF_LINEFUNC);
	Eigen::Vector3d vertex[4];
	Eigen::Matrix3d Jac, InvJac;

	const double u0[4] = { 1.0, 0.0, 0.0, 0.0 };
	const double v0[4] = { 0.0, 1.0, 0.0, 0.0 };
	const double w0[4] = { 0.0, 0.0, 1.0, 0.0 };

	for (int numMode = 0; numMode < nbrMode; numMode++)
	{
		Eigen::VectorXd nCnt = Eigen::VectorXd::Zero(nbrVertex);

		for (int n = 0; n < _mMesh->GetNbrTet(); n++)
		{
			for (int i = 0; i < 4; i++)
				_mMesh->GetVertex(_mMesh->GetTet(n, i), vertex[i]);

			Jac(0, 0) = vertex[0](0) - vertex[3](0); Jac(0, 1) = vertex[0](1) - vertex[3](1); Jac(0, 2) = vertex[0](2) - vertex[3](2);
			Jac(1, 0) = vertex[1](0) - vertex[3](0); Jac(1, 1) = vertex[1](1) - vertex[3](1); Jac(1, 2) = vertex[1](2) - vertex[3](2);
			Jac(2, 0) = vertex[2](0) - vertex[3](0); Jac(2, 1) = vertex[2](1) - vertex[3](1); Jac(2, 2) = vertex[2](2) - vertex[3](2);
			const double detJ = Jac.determinant();
			if (std::abs(detJ) < 1e-30)
				continue;
			InvJac = Jac.inverse();

			for (int j = 0; j < 4; j++)
			{
				Eigen::Vector3cd Esum = Eigen::Vector3cd::Zero();
				for (int i = 0; i < 6; i++)
				{
					Eigen::Vector3d bf;
					bfN.GetValueBF(i + 1, u0[j], v0[j], w0[j], bf);
					bf = InvJac * bf;
					const int eid = _mMesh->GetEdgeOfTet(n, i);
					const std::complex<double> xe = (*_mX)(eid, numMode);
					Esum(0) += bf(0) * xe;
					Esum(1) += bf(1) * xe;
					Esum(2) += bf(2) * xe;
				}
				const int vid = _mMesh->GetTet(n, j);
				_mEx(vid, numMode) += Esum(0);
				_mEy(vid, numMode) += Esum(1);
				_mEz(vid, numMode) += Esum(2);
				nCnt(vid) += 1.0;
			}
		}

		for (int i = 0; i < nbrVertex; i++)
		{
			if (nCnt(i) > 0.0)
			{
				_mEx(i, numMode) /= nCnt(i);
				_mEy(i, numMode) /= nCnt(i);
				_mEz(i, numMode) /= nCnt(i);
			}
		}
	}

	for (int i = 0; i < nbrVertex; i++)
		for (int j = 0; j < nbrMode; j++)
			_mNormE(i, j) = sqrt(abs(_mEx(i, j) * _mEx(i, j)) + abs(_mEy(i, j) * _mEy(i, j)) + abs(_mEz(i, j) * _mEz(i, j)));
}
