#pragma once
#include"../material/Material_Base.h"
#include"../mesh/Mesh_Base.h"
#include"../phy/Phy_Base.h"
#include"../solver/Solver_Base.h"
#include"../post/Post_Base.h"
#include"../Eigen/Sparse"
#include"../Eigen/Dense"

#include<vector>
#include<complex>
#include<string>

class OpticsFEM_2D_EigenMode
{
public:
	void GetMaterial(MaterialLib* matLib);
	void GetMesh(Mesh_2D* mesh);
	void GetPhy(Phy_WaveOpticsModel* phy);
	void GetSolver(Solver_EigenMode* solver);
	void GetPost(Post_2D_EigenMode* post);

	void Assemble();
	void Run();
	void Post(std::string str);

	void Test_OutputMatrix();
	void Test_OutputLambda();

private:
	void Assemble_WaveEquation();
	void Assemble_PEC_PBC();

	MaterialLib* _mMatLib;
	Mesh_2D* _mMesh;
	Phy_WaveOpticsModel* _mPhy;
	Solver_EigenMode* _mSolver;
	Post_2D_EigenMode* _mPost;

	bool _mIsReal;
	int _mDof;

	Eigen::MatrixXcd _mX;
	Eigen::VectorXcd _mLambda;//neff

	std::vector<Eigen::Triplet<double>> _mTripleA_real, _mTripleB_real;
	Eigen::SparseMatrix<double, Eigen::RowMajor> _mA_real;
	Eigen::SparseMatrix<double, Eigen::RowMajor> _mB_real;
	Eigen::SparseMatrix<double, Eigen::RowMajor> _mP_real;
	std::vector<Eigen::Triplet<std::complex<double>>> _mTripleA_complex, _mTripleB_complex;
	Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor> _mA_complex;
	Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor> _mB_complex;
	Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor> _mP_complex;

};


class OpticsFEM_2D_EigenFreq
{
public:
	void GetMaterial(MaterialLib* matLib);
	void GetMesh(Mesh_2D* mesh);
	void GetPhy(Phy_WaveOpticsModel* phy);
	void GetSolver(Solver_EigenFreq* solver);
	void GetPost(Post_2D_EigenFreq* post);

	void Assemble();
	void Run();
	void Post(std::string str);

	void Test_OutputMatrix();
private:
	void Assemble_WaveEquation();
	void Assemble_PEC_PBC();

	MaterialLib* _mMatLib;
	Mesh_2D* _mMesh;
	Phy_WaveOpticsModel* _mPhy;
	Solver_EigenFreq* _mSolver;
	Post_2D_EigenFreq* _mPost;

	bool _mIsReal;
	int _mDof;
	int _mElectricType;

	Eigen::MatrixXcd _mX;
	Eigen::VectorXcd _mLambda;//eigenfreq

	std::vector<Eigen::Triplet<double>> _mTripleA_real, _mTripleB_real;
	Eigen::SparseMatrix<double, Eigen::RowMajor> _mA_real;
	Eigen::SparseMatrix<double, Eigen::RowMajor> _mB_real;
	Eigen::SparseMatrix<double, Eigen::RowMajor> _mP_real;
	std::vector<Eigen::Triplet<std::complex<double>>> _mTripleA_complex, _mTripleB_complex;
	Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor> _mA_complex;
	Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor> _mB_complex;
	Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor> _mP_complex;
};

class OpticsFEM_2D_Scatter
{
public:
	void GetMaterial(MaterialLib* matLib);
	void GetMesh(Mesh_2D* mesh);
	void GetPhy(Phy_WaveOpticsModel* phy);
	void GetSolver(Solver_LdaDom* solver);
	void GetPost(Post_2D_Scatter* post);

	void Assemble();
	void Run();
	void Post(std::string str);

	void Test_OutputMatrix();
private:
	void Assemble_WaveEquation();
	void Assemble_SBC();
	void Assemble_BELE();
	void Assemble_MAG();
	void Assemble_SCD();
	void Assemble_MPD();
	void Assemble_EPD();
	void Assemble_PEC_ELE();
	void Assemble_PBC();
	void Assemble_Port();

	MaterialLib* _mMatLib;
	Mesh_2D* _mMesh;
	Phy_WaveOpticsModel* _mPhy;
	Solver_LdaDom* _mSolver;
	Post_2D_Scatter* _mPost;

	bool _mIsReal;
	int _mDof;

	Eigen::VectorXcd _mX;

	std::vector<Eigen::Triplet<double>> _mTripleA_real;
	Eigen::SparseMatrix<double, Eigen::RowMajor> _mA_real;
	Eigen::SparseMatrix<double, Eigen::RowMajor> _mP_real;
	Eigen::VectorXd _mB_real;
	std::vector<Eigen::Triplet<std::complex<double>>> _mTripleA_complex;
	Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor> _mA_complex;
	Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor> _mP_complex;
	Eigen::VectorXcd _mB_complex;

	std::vector<Eigen::Triplet<std::complex<double>>> _mTriplePort_A_complex;
	Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor> _mPort_A_complex;
	Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor> _mPort_P_complex;
	Eigen::VectorXcd _mPort_B_complex;
	Eigen::VectorXcd _mS_complex;
	Eigen::VectorXcd _mG1_complex;
	Eigen::VectorXcd _mG2_complex;
	Eigen::VectorXcd _mT_complex;
	complex<double>P1, P2, b1;
};


class OpticsFEM_3D_EigenFreq
{
public:
	void GetMaterial(MaterialLib* matLib);
	void GetMesh(Mesh_3D* mesh);
	void GetPhy(Phy_WaveOpticsModel* phy);
	void GetSolver(Solver_EigenFreq* solver);
	void GetPost(Post_3D_EigenFreq* post);

	void Assemble();
	void Run();
	void Post(std::string str);

	void Test_OutputMatrix(const std::string& outDir = "./OutFile");

private:
	void Assemble_WaveEquation();
	void Assemble_PEC_PBC();

	MaterialLib* _mMatLib;
	Mesh_3D* _mMesh;
	Phy_WaveOpticsModel* _mPhy;
	Solver_EigenFreq* _mSolver;
	Post_3D_EigenFreq* _mPost;

	bool _mIsReal;
	int _mDof;

	Eigen::MatrixXcd _mX;
	Eigen::VectorXcd _mLambda;

	std::vector<Eigen::Triplet<double>> _mTripleA_real, _mTripleB_real;
	Eigen::SparseMatrix<double, Eigen::RowMajor> _mA_real;
	Eigen::SparseMatrix<double, Eigen::RowMajor> _mB_real;
	Eigen::SparseMatrix<double, Eigen::RowMajor> _mP_real;
	std::vector<Eigen::Triplet<std::complex<double>>> _mTripleA_complex, _mTripleB_complex;
	Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor> _mA_complex;
	Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor> _mB_complex;
	Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor> _mP_complex;
};

class OpticsFEM_3D_Scatter
{
public:
	void GetMaterial(MaterialLib* matLib);
	void GetMesh(Mesh_3D* mesh);
	void GetPhy(Phy_WaveOpticsModel* phy);
	void GetSolver(Solver_LdaDom* solver);
	void GetPost(Post_3D_Scatter* post);

	void Assemble();
	void Run();
	void Post(std::string str);
	void Test_OutputMatrix(const std::string& outDir = ".");
	void SetExportAsmDirectory(const std::string& dir);

private:
	void Assemble_WaveEquation();
	void Assemble_WaveEquation_Order2();
	void Assemble_SBC();
	void Assemble_BELE();
	void Assemble_MAG();
	void Assemble_SCD();
	void Assemble_MPD();
	void Assemble_EPD();
	void Assemble_PEC_ELE();
	void Assemble_PBC();
	void Assemble_Port();

	MaterialLib* _mMatLib;
	Mesh_3D* _mMesh;
	Phy_WaveOpticsModel* _mPhy;
	Solver_LdaDom* _mSolver;
	Post_3D_Scatter* _mPost;

	bool _mIsReal;
	int _mDof;
	int _mPortExtraDof = 0;
	std::vector<int> _mFreeDofIndices; // full-system DOF indices kept after PEC elimination (port case)

	Eigen::VectorXcd _mX;

	std::vector<Eigen::Triplet<double>> _mTripleA_real;
	Eigen::SparseMatrix<double, Eigen::RowMajor> _mA_real;
	Eigen::SparseMatrix<double, Eigen::RowMajor> _mP_real;
	Eigen::VectorXd _mB_real;
	std::vector<Eigen::Triplet<std::complex<double>>> _mTripleA_complex;
	Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor> _mA_complex;
	Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor> _mP_complex;
	Eigen::VectorXcd _mB_complex;
	std::string _mExportAsmDir;
};

class OpticsFEM_3D_Scatter2
{
public:
	void GetMaterial(MaterialLib* matLib);
	void GetMesh(Mesh_3D* mesh);
	void GetPhy(Phy_WaveOpticsModel* phy);
	void GetSolver(Solver_LdaDom* solver);
	void GetPost(Post_3D_Scatter* post);

	void Assemble();

private:
	void Assemble_WaveEquation();
	void Assemble_SBC();
	void Assemble_PEC_ELE();
	

	MaterialLib* _mMatLib;
	Mesh_3D* _mMesh;
	Phy_WaveOpticsModel* _mPhy;
	Solver_LdaDom* _mSolver;
	Post_3D_Scatter* _mPost;
	OpticsFEM_2D_EigenMode* _mPortFEM;

	bool _mIsReal;
	int _mDof;

	std::vector<Eigen::Triplet<double>> _mTripleA_real;
	Eigen::SparseMatrix<double, Eigen::RowMajor> _mA_real;
	Eigen::SparseMatrix<double, Eigen::RowMajor> _mP_real;
	Eigen::VectorXd _mB_real;
	std::vector<Eigen::Triplet<std::complex<double>>> _mTripleA_complex;
	Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor> _mA_complex;
	Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor> _mP_complex;
	Eigen::VectorXcd _mB_complex;
};
