#include "../nlohmann/json.hpp"
#include"../common/define.h"
#include"Solver_Base.h"
#include"eigen_solver.h"
#include"interface.h"

#include <complex>
#include <fstream>
#include <iostream>
#include <iomanip>

using json = nlohmann::json;
using namespace std;

void Solver_EigenMode::GetSolver(std::string input)
{
	json temp = nlohmann::json::parse(input);
	_mNbrMode = temp.at("NbrMode");//ģʽ��Ŀ
	_mSearchValue = temp.at("searchValue");//����ֵ-��Ч������
	_mLda0 = temp.at("lambda0");//��ղ���
	_mPreproType = 0;//����������
}

void Solver_EigenMode::GetRealFlag(bool flag)
{
	_mIsReal = flag;
}

void Solver_EigenMode::SetParam(Eigen::SparseMatrix<double, Eigen::RowMajor>* A_real,
	Eigen::SparseMatrix<double, Eigen::RowMajor>* B_real,
	Eigen::SparseMatrix<double, Eigen::RowMajor>* P_real, double target)
{
	if (_mIsReal == true)
	{
		_mA_real = A_real;
		_mB_real = B_real;
		_mP_real = P_real;
		_mParam.A_n = _mA_real->rows();
		_mParam.B_n = _mB_real->rows();
		_mParam.A_nzero = _mA_real->nonZeros();
		_mParam.B_nzero = _mB_real->nonZeros();
		_mParam.target = target;
	}
}

void Solver_EigenMode::SetParam(Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor>* A_complex,
	Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor>* B_complex,
	Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor>* P_complex, double target)
{
	if (_mIsReal == false)
	{
		_mA_complex = A_complex;
		_mB_complex = B_complex;
		_mP_complex = P_complex;
		_mParam.A_n = A_complex->rows();
		_mParam.B_n = B_complex->rows();
		_mParam.A_nzero = A_complex->nonZeros();
		_mParam.B_nzero = B_complex->nonZeros();
		_mParam.target = target;
	}
}


void Solver_EigenMode::Run(Eigen::MatrixXcd* x, Eigen::VectorXcd* lambda)
{
	double k0 = 2. * Pi / _mLda0;
	if (_mIsReal == true)
	{
		x[0] = Eigen::MatrixXcd::Zero(_mP_real->rows(), _mNbrMode);
		lambda[0] = Eigen::VectorXcd::Zero(_mNbrMode);

		//init
		double* A_real, * B_real;
		int* A_Roffsets, * A_Colindices, * B_Roffsets, * B_Colindices;
		double** x_real, * eigenValues;
		x_real = new double* [_mNbrMode];
		for (int i = 0; i < _mNbrMode; i++)
		{
			x_real[i] = new double[_mParam.A_n];
		}
		eigenValues = new double[_mNbrMode];
		//getdata
		A_real = _mA_real->valuePtr();
		B_real = _mB_real->valuePtr();
		A_Colindices = _mA_real->innerIndexPtr();
		A_Roffsets = _mA_real->outerIndexPtr();
		B_Colindices = _mB_real->innerIndexPtr();
		B_Roffsets = _mB_real->outerIndexPtr();

		realEigenSolver(A_Roffsets, A_Colindices, A_real,B_Roffsets, B_Colindices, B_real,
			_mParam.A_n, _mParam.B_n, _mParam.A_nzero, _mParam.B_nzero, _mParam.target, 0, _mNbrMode,
			eigenValues, x_real);

		Eigen::MatrixXd tempX(_mParam.A_n, _mNbrMode);
		for (int i = 0; i < _mNbrMode; i++)
		{
			lambda[0](i) = sqrt(-eigenValues[i]) / k0;
			for (int j = 0; j < _mParam.A_n; j++)
				tempX(j, i) = x_real[i][j];
		}
		tempX = _mP_real[0] * tempX;

		//output
		for (int i = 0; i < _mNbrMode; i++)
		{
			for (int j = 0; j < _mP_real->rows(); j++)
			{
				x[0](j, i) = tempX(j, i);
			}
		}

		for(int i=0;i<_mNbrMode;i++)
			delete[] x_real[i];
		delete[] x_real,eigenValues;
	}
	else
	{
		x[0] = Eigen::MatrixXcd::Zero(_mP_complex->rows(), _mNbrMode);
		lambda[0] = Eigen::VectorXcd::Zero(_mNbrMode);

		//init
		complex<double>* A_complex, * B_complex;
		int* A_Roffsets, * A_Colindices, * B_Roffsets, * B_Colindices;
		complex<double>** x_complex, * eigenValues;
		x_complex = new complex<double>* [_mNbrMode];
		for (int i = 0; i < _mNbrMode; i++)
		{
			x_complex[i] = new complex<double>[_mParam.A_n];
		}
		eigenValues = new complex<double>[_mNbrMode];
		//getdata
		A_complex = _mA_complex->valuePtr();
		B_complex = _mB_complex->valuePtr();
		A_Colindices = _mA_complex->innerIndexPtr();
		A_Roffsets = _mA_complex->outerIndexPtr();
		B_Colindices = _mB_complex->innerIndexPtr();
		B_Roffsets = _mB_complex->outerIndexPtr();

		complexEigenSolver(A_Roffsets, A_Colindices, A_complex, B_Roffsets, B_Colindices, B_complex,
			_mParam.A_n, _mParam.B_n, _mParam.A_nzero, _mParam.B_nzero, _mParam.target,0., 0, _mNbrMode,
			eigenValues, x_complex);

		Eigen::MatrixXcd tempX(_mParam.A_n, _mNbrMode);
		for (int i = 0; i < _mNbrMode; i++)
		{
			lambda[0](i) = sqrt(-eigenValues[i]) / k0;
			cout << lambda[0](i) << endl;//������
			for (int j = 0; j < _mParam.A_n; j++)
				tempX(j, i) = x_complex[i][j];
		}
		tempX = _mP_complex[0] * tempX;

		//output
		for (int i = 0; i < _mNbrMode; i++)
		{
			for (int j = 0; j < _mP_complex->rows(); j++)
			{
				x[0](j, i) = tempX(j, i);
			}
		}
		
		for(int i=0;i<_mNbrMode;i++)
			delete[] x_complex[i];
		delete[] x_complex,eigenValues;
	}
}

void Solver_EigenFreq::GetSolver(std::string input)
{
	json temp = nlohmann::json::parse(input);
	_mNbrMode = temp.at("NbrMode");//ģʽ��Ŀ
	_mSearchValue = temp.at("searchValue");//����ֵ-����Ƶ��
	_mPreproType = 0; //����������
	_mElectricType = temp.at("EletricType"); //���糡���� 0-Et 1-Ez 2-E 
}

void Solver_EigenFreq::GetRealFlag(bool flag)
{
	_mIsReal = flag;
}

void Solver_EigenFreq::SetParam(Eigen::SparseMatrix<double, Eigen::RowMajor>* A_real,
	Eigen::SparseMatrix<double, Eigen::RowMajor>* B_real,
	Eigen::SparseMatrix<double, Eigen::RowMajor>* P_real, double target)
{
	if (_mIsReal == true)
	{
		_mA_real = A_real;
		_mB_real = B_real;
		_mP_real = P_real;
		_mParam.A_n = _mA_real->rows();
		_mParam.B_n = _mB_real->rows();
		_mParam.A_nzero = _mA_real->nonZeros();
		_mParam.B_nzero = _mB_real->nonZeros();
		_mParam.target = target;
	}
}

void Solver_EigenFreq::SetParam(Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor>* A_complex,
	Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor>* B_complex,
	Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor>* P_complex, double target)
{
	if (_mIsReal == false)
	{
		_mA_complex = A_complex;
		_mB_complex = B_complex;
		_mP_complex = P_complex;
		_mParam.A_n = A_complex->rows();
		_mParam.B_n = B_complex->rows();
		_mParam.A_nzero = A_complex->nonZeros();
		_mParam.B_nzero = B_complex->nonZeros();
		_mParam.target = target;
	}
}


void Solver_EigenFreq::Run(Eigen::MatrixXcd* x, Eigen::VectorXcd* lambda)
{
	if (_mIsReal == true)
	{
		x[0] = Eigen::MatrixXcd::Zero(_mP_real->rows(), _mNbrMode);
		lambda[0] = Eigen::VectorXcd::Zero(_mNbrMode);

		//init
		double* A_real, * B_real;
		int* A_Roffsets, * A_Colindices, * B_Roffsets, * B_Colindices;
		double** x_real, * eigenValues;
		x_real = new double* [_mNbrMode];
		for (int i = 0; i < _mNbrMode; i++)
		{
			x_real[i] = new double[_mParam.A_n];
		}
		eigenValues = new double[_mNbrMode];
		//getdata
		A_real = _mA_real->valuePtr();
		B_real = _mB_real->valuePtr();
		A_Colindices = _mA_real->innerIndexPtr();
		A_Roffsets = _mA_real->outerIndexPtr();
		B_Colindices = _mB_real->innerIndexPtr();
		B_Roffsets = _mB_real->outerIndexPtr();

		realEigenSolver(A_Roffsets, A_Colindices, A_real, B_Roffsets, B_Colindices, B_real,
			_mParam.A_n, _mParam.B_n, _mParam.A_nzero, _mParam.B_nzero, _mParam.target, 0, _mNbrMode,
			eigenValues, x_real);

		Eigen::MatrixXd tempX(_mParam.A_n, _mNbrMode);
		for (int i = 0; i < _mNbrMode; i++)
		{
			lambda[0](i) = c_const / (2. * Pi / sqrt(eigenValues[i]));
			for (int j = 0; j < _mParam.A_n; j++)
				tempX(j, i) = x_real[i][j];
		}
		tempX = _mP_real[0] * tempX;

		//output
		for (int i = 0; i < _mNbrMode; i++)
		{
			for (int j = 0; j < _mP_real->rows(); j++)
			{
				x[0](j, i) = tempX(j, i);
			}
		}

		for(int i=0;i<_mNbrMode;i++)
			delete[] x_real[i];
		delete[] x_real,eigenValues;
	}
	else
	{
		x[0] = Eigen::MatrixXcd::Zero(_mP_complex->rows(), _mNbrMode);
		lambda[0] = Eigen::VectorXcd::Zero(_mNbrMode);

		//init
		complex<double>* A_complex, * B_complex;
		int* A_Roffsets, * A_Colindices, * B_Roffsets, * B_Colindices;
		complex<double>** x_complex, * eigenValues;
		x_complex = new complex<double>*[_mNbrMode];
		for (int i = 0; i < _mNbrMode; i++)
		{
			x_complex[i] = new complex<double>[_mParam.A_n];
		}
		eigenValues = new complex<double>[_mNbrMode];
		//getdata
		A_complex = _mA_complex->valuePtr();
		B_complex = _mB_complex->valuePtr();
		A_Colindices = _mA_complex->innerIndexPtr();
		A_Roffsets = _mA_complex->outerIndexPtr();
		B_Colindices = _mB_complex->innerIndexPtr();
		B_Roffsets = _mB_complex->outerIndexPtr();

		complexEigenSolver(A_Roffsets, A_Colindices, A_complex, B_Roffsets, B_Colindices, B_complex,
			_mParam.A_n, _mParam.B_n, _mParam.A_nzero, _mParam.B_nzero, _mParam.target, 0., 0, _mNbrMode,
			eigenValues, x_complex);

		Eigen::MatrixXcd tempX(_mParam.A_n, _mNbrMode);
		for (int i = 0; i < _mNbrMode; i++)
		{
			lambda[0](i) = c_const / (2. * Pi / sqrt(eigenValues[i]));
			for (int j = 0; j < _mParam.A_n; j++)
				tempX(j, i) = x_complex[i][j];
		}
		tempX = _mP_complex[0] * tempX;

		//output
		for (int i = 0; i < _mNbrMode; i++)
		{
			for (int j = 0; j < _mP_complex->rows(); j++)
			{
				x[0](j, i) = tempX(j, i);
			}
		}

		for(int i=0;i<_mNbrMode;i++)
			delete[] x_complex[i];
		delete[] x_complex,eigenValues;
	}
}

void Solver_LdaDom::GetSolver(std::string input)
{
	json temp = nlohmann::json::parse(input);
	_mLda0 = temp.at("lambda");//��ղ���
	_mElectricType = temp.at("EletricType");//���糡���� 0-Et 1-Ez 2-E 
}

void Solver_LdaDom::GetSolver2(std::string input)
{
	json temp = nlohmann::json::parse(input);
	_mLda0 =  0.299792458/(double)temp.at("freq");//��ղ���
	_mElectricType = temp.at("EletricType");//���糡���� 0-Et 1-Ez 2-E 
}


void Solver_LdaDom::GetRealFlag(bool flag)
{
	_mIsReal = flag;
}

void Solver_LdaDom::SetParam(Eigen::SparseMatrix<double, Eigen::RowMajor>* A_real,
	Eigen::VectorXd* B_real, Eigen::SparseMatrix<double, Eigen::RowMajor>* P_real)
{
	if (_mIsReal == true)
	{
		_mA_real = A_real;
		_mB_real = B_real;
		_mP_real = P_real;
		_mParam.A_n = _mA_real->rows();
		_mParam.A_nzero = _mA_real->nonZeros();
	}
}

void Solver_LdaDom::SetParam(Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor>* A_complex,
	Eigen::VectorXcd* B_complex, Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor>* P_complex)
{
	if (_mIsReal == false)
	{
		_mA_complex = A_complex;
		_mB_complex = B_complex;
		_mP_complex = P_complex;
		_mParam.A_n = A_complex->rows();
		_mParam.A_nzero = A_complex->nonZeros();
	}
}

void Solver_LdaDom::Run(Eigen::VectorXcd* x)
{
	if (_mIsReal == true)
	{
		x[0] = Eigen::VectorXcd::Zero(_mP_real->rows());
		//init
		double* A_real, * b_real, * x_real;
		int* A_Roffsets, * A_Colindices;
		x_real = new double[_mParam.A_n];
		//getdata
		A_real = _mA_real->valuePtr();
		A_Roffsets = _mA_real->outerIndexPtr();
		A_Colindices = _mA_real->innerIndexPtr();
		b_real = new double[_mParam.A_n];
		for (int i = 0; i < _mParam.A_n; i++)
			b_real[i] = _mB_real[0](i);

		double para[6] = { 0.0, 0, 1e-3, 2.0456e+08 * 1e-3, 0, -1 };
		
		solveRealLinearEqu(A_Roffsets, A_Colindices, A_real, b_real, x_real, _mParam.A_n, _mParam.A_nzero, para);

		Eigen::VectorXd temp(_mParam.A_n);
		for(int i=0;i<_mParam.A_n;i++)
		{
			temp(i)=x_real[i];
		}
		temp=_mP_real[0]*temp;
		for(int i=0;i<_mP_real->rows();i++)
		{
			x[0](i)=temp(i);
		}
		this->Test_OutputX(x);
		delete[] x_real;	
	}
	else
	{
		x[0] = Eigen::VectorXcd::Zero(_mP_complex->rows());
		//init
		complex<double>* A_compl, * b_compl, * x_compl;
		int* A_Roffsets, * A_Colindices;
		x_compl = new complex<double>[_mParam.A_n];
		//getdata
		A_compl = _mA_complex->valuePtr();
		A_Roffsets = _mA_complex->outerIndexPtr();
		A_Colindices = _mA_complex->innerIndexPtr();
		b_compl = new complex<double>[_mParam.A_n];
		for (int i = 0; i < _mParam.A_n; i++)
			b_compl[i] = _mB_complex[0](i);

		
		double para[6] = { 0.0, 0, 1e-3, 2.0456e+08 * 1e-3, 0, -1 };
		solveComplexLinearEqu(A_Roffsets, A_Colindices, A_compl, b_compl, x_compl, _mParam.A_n, _mParam.A_nzero, para);

		Eigen::VectorXcd temp(_mParam.A_n);
		for(int i=0;i<_mParam.A_n;i++)
		{
			temp(i)=x_compl[i];
		}
		temp=_mP_complex[0]*temp;
		for(int i=0;i<_mP_complex->rows();i++)
		{
			x[0](i)=temp(i);
		}
		
		delete[] x_compl;
	}
}
