#include"../phy/Phy_Base.h"
#include"../material/Material_Base.h"
#include"../mesh/Mesh_Base.h"
#include"../solver/Solver_Base.h"
#include"../test/interfaceClass.h"
#include"../common/define.h"

#include "../nlohmann/json.hpp"
using json = nlohmann::json;

namespace {

void parsePbcBlock(const json& pbc, Phy_WaveOpticsModel::PbcGroupConfig& group)
{
	std::vector<int> srcIndex = pbc.at("srcIndex");
	std::vector<int> dstIndex = pbc.at("dstIndex");
	std::vector<double> phiR = pbc.at("phiR");
	std::vector<double> phiI = pbc.at("phiI");
	const int nbrPbcPairs = static_cast<int>(phiR.size());
	if (nbrPbcPairs <= 0)
		return;

	group.pbc = Eigen::MatrixXi(nbrPbcPairs, 2);
	group.data = Eigen::VectorXcd(nbrPbcPairs);
	group.angle = Eigen::VectorXd(nbrPbcPairs);
	group.waveVec = Eigen::MatrixXd::Zero(3, nbrPbcPairs);
	group.srcDomains.clear();
	group.dstDomains.clear();
	group.srcDomains.resize(nbrPbcPairs);
	group.dstDomains.resize(nbrPbcPairs);

	const bool multiDomainSinglePair =
		(nbrPbcPairs == 1 && srcIndex.size() > 1 && srcIndex.size() == dstIndex.size());

	for (int i = 0; i < nbrPbcPairs; i++)
	{
		group.data(i) = std::complex<double>(phiR[i], phiI[i]);
		group.angle(i) = 0.0;
		if (pbc.contains("pbcAngle"))
		{
			if (pbc.at("pbcAngle").is_array())
			{
				std::vector<double> angles = pbc.at("pbcAngle");
				if (i < static_cast<int>(angles.size()))
					group.angle(i) = angles[i];
			}
			else
			{
				group.angle(i) = pbc.at("pbcAngle");
			}
		}

		if (pbc.contains("pbcWaveVec"))
		{
			const auto& wv = pbc.at("pbcWaveVec");
			if (wv.is_array() && !wv.empty())
			{
				if (wv[0].is_array())
				{
					for (int k = 0; k < 3 && k < static_cast<int>(wv[i].size()); k++)
						group.waveVec(k, i) = wv[i][k];
				}
				else if (wv.size() >= 3)
				{
					for (int k = 0; k < 3; k++)
						group.waveVec(k, i) = wv[k];
				}
			}
		}

		if (multiDomainSinglePair)
		{
			group.srcDomains[i] = Eigen::VectorXi(static_cast<int>(srcIndex.size()));
			group.dstDomains[i] = Eigen::VectorXi(static_cast<int>(dstIndex.size()));
			for (int k = 0; k < static_cast<int>(srcIndex.size()); k++)
			{
				group.srcDomains[i](k) = srcIndex[k];
				group.dstDomains[i](k) = dstIndex[k];
			}
			group.pbc(i, 0) = srcIndex[0];
			group.pbc(i, 1) = dstIndex[0];
		}
		else
		{
			group.pbc(i, 0) = srcIndex[i];
			group.pbc(i, 1) = dstIndex[i];
			group.srcDomains[i] = Eigen::VectorXi(1);
			group.dstDomains[i] = Eigen::VectorXi(1);
			group.srcDomains[i](0) = srcIndex[i];
			group.dstDomains[i](0) = dstIndex[i];
		}
	}
}

} // namespace

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

void Phy_WaveOpticsModel::Test_ReadData(string str)
{
	json js = nlohmann::json::parse(str);

	_mElementOrder = js.value("ElementOrder", 1);
	if (_mElementOrder != 1 && _mElementOrder != 2)
		_mElementOrder = 1;

	std::vector<int> BoundaryFlag = js.at("BoundaryFlag");
 	int nbrBoundary = js.at("NbrBoundary");
	int nbrPEC, nbrPMC, nbrELE, nbrSBC, nbrPBC, nbrMAG, nbrSCD;
	nbrPEC = 0;
	nbrPMC = 0;
	nbrELE = 0;
	nbrSBC = 0;
	nbrPBC = 0;
	nbrMAG = 0;
	nbrSCD = 0;	
	for (int i = 0; i < nbrBoundary; i++)
	{
		if (BoundaryFlag[i] == 0)
			nbrPMC++;
		if (BoundaryFlag[i] == 1)
			nbrPEC++;
		if (BoundaryFlag[i] == 2)
			nbrSBC++;
		if (BoundaryFlag[i] == 3)
			nbrELE++;
		if (BoundaryFlag[i] == 4)
			nbrPBC++;
		if (BoundaryFlag[i] == 8)
			nbrMAG++;
		if (BoundaryFlag[i] == 9)
			nbrSCD++;
	}
	nbrPBC = nbrPBC / 2;

	_mPMC = Eigen::VectorXi(nbrPMC);
	_mPEC = Eigen::VectorXi(nbrPEC);
	_mELE = Eigen::VectorXi(nbrELE);
	_mSBC = Eigen::MatrixXi(nbrSBC, 2);
	_mEincx = new std::string[nbrSBC];
	_mEincy = new std::string[nbrSBC];
	_mEincz = new std::string[nbrSBC];
	_mSBCDir = Eigen::MatrixXd(nbrSBC, 3);
	_mPBCGroups.clear();
	_mMAG = Eigen::VectorXi(nbrMAG);
	_mSCD = Eigen::VectorXi(nbrSCD);
	_mPortEinc = new std::string[3];
	_mPortEout = new std::string[3];
	_mPortEincCurl = new std::string[3];
	_mPortEoutCurl = new std::string[3];

	nbrPMC = 0;
	nbrPEC = 0;
	nbrPBC = 0;


	for (int i = 0; i < BoundaryFlag.size(); i++)
	{
		if (BoundaryFlag[i] == 0)
		{
			_mPMC(nbrPMC) = i;
			nbrPMC++;
		}
		if (BoundaryFlag[i] == 1)
		{
			_mPEC(nbrPEC) = i;
			nbrPEC++;
		}
		if (BoundaryFlag[i] == 4)
		{
			nbrPBC++;
		}
	}
	//��ȡ�߽�����������
	if (js.contains("sbc"))
	{
		json sbc = js.at("sbc");
		std::vector<double> SBCType = sbc.at("SBCType");
		std::vector<double> Index = sbc.at("Index");
		std::vector<std::string> E0x = sbc.at("E0x");
		std::vector<std::string> E0y = sbc.at("E0y");
		std::vector<std::string> E0z = sbc.at("E0z");
		std::vector<double> kx = sbc.at("kx");
		std::vector<double> ky = sbc.at("ky");
		std::vector<double> kz = sbc.at("kz");
		const int nSbcExplicit = static_cast<int>(Index.size());
		if (nSbcExplicit != nbrSBC)
		{
			nbrSBC = nSbcExplicit;
			_mSBC = Eigen::MatrixXi(nbrSBC, 2);
			delete[] _mEincx;
			delete[] _mEincy;
			delete[] _mEincz;
			_mEincx = new std::string[nbrSBC];
			_mEincy = new std::string[nbrSBC];
			_mEincz = new std::string[nbrSBC];
			_mSBCDir = Eigen::MatrixXd(nbrSBC, 3);
		}
		for (int i = 0; i < nbrSBC; i++)
		{
			_mSBC(i, 0) = static_cast<int>(Index[i]);
			_mSBC(i, 1) = static_cast<int>(SBCType[i]);
			_mEincx[i] = E0x[i];
			_mEincy[i] = E0y[i];
			_mEincz[i] = E0z[i];
			_mSBCDir(i, 0) = kx[i];
			_mSBCDir(i, 1) = ky[i];
			_mSBCDir(i, 2) = kz[i];
		}
	}

	if (js.contains("ef"))
	{
		json ef = js.at("ef");
		std::vector<int> EF = ef.at("index");
		std::vector<string> E0x = ef.at("E0x");
		std::vector<string> E0y = ef.at("E0y");
		std::vector<string> E0z = ef.at("E0z");
		_mE0x = new std::string[nbrELE];
		_mE0y = new std::string[nbrELE];
		_mE0z = new std::string[nbrELE];
		for (int i = 0; i < EF.size(); i++)
		{
			_mELE(i) = EF[i];
			_mE0x[i] = E0x[i];
			_mE0y[i] = E0y[i];
			_mE0z[i] = E0z[i];
		}
	}

	if (js.contains("mag"))
	{
		json mag = js.at("mag");
		std::vector<int> MAG = mag.at("index");
		std::vector<string> H0x = mag.at("H0x");
		std::vector<string> H0y = mag.at("H0y");
		std::vector<string> H0z = mag.at("H0z");
		_mH0x = new std::string[nbrMAG];
		_mH0y = new std::string[nbrMAG];
		_mH0z = new std::string[nbrMAG];
		for (int i = 0; i < MAG.size(); i++)
		{
			_mMAG(i) = MAG[i];
			_mH0x[i] = H0x[i];
			_mH0y[i] = H0y[i];
			_mH0z[i] = H0z[i];
		}
	}

	if (js.contains("scd"))
	{
		json scd = js.at("scd");
		std::vector<int> SCD = scd.at("index");
		std::vector<string> J0x = scd.at("J0x");
		std::vector<string> J0y = scd.at("J0y");
		std::vector<string> J0z = scd.at("J0z");
		_mJ0x = new std::string[nbrSCD];
		_mJ0y = new std::string[nbrSCD];
		_mJ0z = new std::string[nbrSCD];
		for (int i = 0; i < SCD.size(); i++)
		{
			_mSCD(i) = SCD[i];
			_mJ0x[i] = J0x[i];
			_mJ0y[i] = J0y[i];
			_mJ0z[i] = J0z[i];
		}
		
	}

	nbrPBC = nbrPBC / 2;
	if (js.contains("pbc"))
	{
		PbcGroupConfig group;
		parsePbcBlock(js.at("pbc"), group);
		if (group.pbc.rows() > 0)
			_mPBCGroups.push_back(group);
	}
	if (js.contains("pbc2"))
	{
		PbcGroupConfig group2;
		parsePbcBlock(js.at("pbc2"), group2);
		if (group2.pbc.rows() > 0)
			_mPBCGroups.push_back(group2);
	}
	else if (nbrPBC != 0 && _mPBCGroups.empty())
	{
		// legacy: BoundaryFlag==4 pairs without explicit pbc block
	}
	
	if (js.contains("mpd"))
	{
		json mpd = js.at("mpd");
		std::vector<int> MPD = mpd.at("index");
		std::vector<std::vector<double>> MPDData;
		std::vector<double> mx = mpd.at("mx");
		std::vector<double> my = mpd.at("my");
		std::vector<double> mz = mpd.at("mz");
		int nbrMPD = MPD.size();
		_mMPD = Eigen::VectorXi(nbrMPD);
		_mMPDData = Eigen::MatrixXd(nbrMPD, 3);
		for (int i = 0; i < nbrMPD; i++)
		{
			_mMPD[i] = MPD[i];
			_mMPDData(i, 0) = mx[i];
			_mMPDData(i, 1) = my[i];
			_mMPDData(i, 2) = mz[i];
		}
	}
	if (js.contains("epd"))
	{
		json epd = js.at("epd");
		std::vector<int> EPD = epd.at("index");
		std::vector<std::vector<double>> EPDData;
		std::vector<double> px = epd.at("px");
		std::vector<double> py = epd.at("py");
		std::vector<double> pz = epd.at("pz");
		int nbrEPD = EPD.size();
		_mEPD = Eigen::VectorXi(nbrEPD);
		_mEPDData = Eigen::MatrixXd(nbrEPD, 3);
		for (int i = 0; i < nbrEPD; i++)
		{
			_mEPD[i] = EPD[i];
			_mEPDData(i, 0) = px[i];
			_mEPDData(i, 1) = py[i];
			_mEPDData(i, 2) = pz[i];
		}
	}
	if (js.contains("bele"))
	{
		json bele = js.at("bele");
		std::vector<int> BELE = bele.at("index");
		int nbrBELE = BELE.size();
		_mBELE = Eigen::VectorXi(nbrBELE);
		for (int i = 0; i < nbrBELE; i++)
		{
			_mBELE[i] = BELE[i];
		}
		_mEbx = bele.at("Ebx");
		_mEby = bele.at("Eby");
		_mEbz = bele.at("Ebz");
		_mcurlEbx = bele.at("curlEbx");
		_mcurlEby = bele.at("curlEby");
		_mcurlEbz = bele.at("curlEbz");
		if (bele.contains("curlcurlEbx"))
			_mcurlcurlEbx = bele.at("curlcurlEbx");
		if (bele.contains("curlcurlEby"))
			_mcurlcurlEby = bele.at("curlcurlEby");
		if (bele.contains("curlcurlEbz"))
			_mcurlcurlEbz = bele.at("curlcurlEbz");
	}
	if (js.contains("pml"))
	{
		json pml = js.at("pml");
		std::vector<int>PML = pml.at("index");
		vector<int> PMLType = pml.at("PMLType");
		std::vector<std::vector<double>> PMLData = pml.at("PMLData");
		int nbrPML = PML.size();
		_mPMLLda = pml.at("lambda");
		_mPML = Eigen::VectorXi(nbrPML);
		_mPMLData = Eigen::MatrixXd(nbrPML,6);
		_mPMLType = Eigen::VectorXi(nbrPML);
		_mPMLEdge = Eigen::VectorXi(nbrPML);
		
		for (int i = 0; i < nbrPML; i++)
		{
			_mPML[i] = PML[i];
			_mPMLType[i] = PMLType[i];
			for (int j = 0; j < 6; j++)
			{
				_mPMLData(i,j) = PMLData[i][j];
			}
		}
		if (pml.contains("PMLBoundaryIndex"))
		{
			vector<int> PMLEdge = pml.at("PMLBoundaryIndex");
			for (int i = 0; i < nbrPML; i++)
			{
				_mPMLEdge(i) = PMLEdge[i];
			}
		}
	}

	if (js.contains("port"))
	{
		json port = js.at("port");
		std::vector<int>PORTinc = port.at("PORTinc");
		std::vector<int>PORTout = port.at("PORTout");
		std::vector<std::string> Einc = port.at("Einc");
		std::vector<std::string> Eout = port.at("Eout");
		std::vector<std::string> EincCurl = port.at("EincCurl");
		std::vector<std::string> EoutCurl = port.at("EoutCurl");
		_mPortinc = Eigen::VectorXi(PORTinc.size());
		_mPortout = Eigen::VectorXi(PORTout.size());
		for (int i = 0; i < PORTinc.size(); i++)
		{
			_mPortinc(i) = PORTinc[i];
		}
		for (int i = 0; i < PORTout.size(); i++)
		{
			_mPortout(i) = PORTout[i];
		}
		for (int i = 0; i < 3; i++)
		{
			_mPortEinc[i] = Einc[i];
		}
		for (int i = 0; i < 3; i++)
		{
			_mPortEout[i] = Eout[i];
		}
		for (int i = 0; i < 3; i++)
		{
			_mPortEincCurl[i] = EincCurl[i];
		}
		for (int i = 0; i < 3; i++)
		{
			_mPortEoutCurl[i] = EoutCurl[i];
		}
		if (port.contains("modeFile"))
		{
			const std::string modeFile = port.at("modeFile").get<std::string>();
			if (!_mPortModes.LoadFromFile(modeFile))
				std::cerr << "[Phy] failed to load port mode file: " << modeFile << std::endl;
		}
	}
	if (js.contains("beam"))
	{
		json beam = js.at("beam");
		_mBeam = beam.at("type");
		_mkx = beam.at("kx");
		_mky = beam.at("ky");
	}
}

void MaterialLib::Test_ReadData(string str)
{
	json js = nlohmann::json::parse(str);

	std::vector<int> matType = js.at("matType");
	std::vector<double> epsilonrR = js.at("epsilonrR");
	std::vector<double> epsilonrI = js.at("epsilonrI");
	std::vector<double> murR = js.at("murR");
	std::vector<double> murI = js.at("murI");
	std::vector<double> sigmaX = js.at("sigma");
	std::vector<double> chiheR = js.at("chiheR");
	std::vector<double> chiheI = js.at("chiheI");
	std::vector<double> chiehR = js.at("chiehR");
	std::vector<double> chiehI = js.at("chiehI");
	std::vector<double> nn = js.at("n");
	std::vector<double> kk = js.at("k");
	int nbrDomain = js.at("NbrDomain");

	SetNumberMat(nbrDomain);
	int j = 0;
	for (int i = 0; i < nbrDomain; i++)
	{
		int* domain;
		std::complex<double>* epsr, * mur, * chihe, * chieh;
		double* sigma;
		epsr = new std::complex<double>[1];
		mur = new std::complex<double>[1];
		sigma = new double[1];
		chihe = new std::complex<double>[1];
		chieh = new std::complex<double>[1];
		domain = new int[1];
		domain[0] = i + 1;
		if (matType[i] == 0)
		{
			epsr[0] = std::complex<double>(epsilonrR[j], epsilonrI[j]);
			mur[0] = std::complex<double>(murR[j], murI[j]);
			sigma[0] = sigmaX[j];
			chihe[0] = std::complex<double>(chiheR[j], chiheI[j]);
			chieh[0] = std::complex<double>(chiehR[j], chiehI[j]);
			j = j + 1;
		}
		if (matType[i] == 1)
		{
			epsr[0] = std::complex<double>(nn[i - j], kk[i - j]);
			epsr[0] = epsr[0] * epsr[0];
			mur[0] = 1.0;
			sigma[0] = 0.0;
			chihe[0] = 0.0;
			chieh[0] = 0.0;
		}
		std::string name = "mat";
		SetMat(i, name, 1, domain, 0, epsr, mur, sigma, chihe, chieh);
		delete[] domain, epsr, mur, sigma, chihe, chieh;
	}
}
