#include "eigen_solver.h"

#include <algorithm>
#include <cstdio>
#include <fstream>

#ifdef _WIN32
#include <direct.h>
static void EnsureEigenSolverDir(const char* dir)
{
	_mkdir(dir);
}
static int RunRealEigenExe()
{
	return system("cd real_solver && real.exe");
}
static int RunComplexEigenExe()
{
	return system("cd complex_solver && complex.exe");
}
#else
#include <sys/stat.h>
static void EnsureEigenSolverDir(const char* dir)
{
	mkdir(dir, 0755);
}
static int RunRealEigenExe()
{
	return system("cd ./real_solver && ./real");
}
static int RunComplexEigenExe()
{
	return system("cd ./complex_solver && ./complex");
}
#endif

static void rfilewrite(const char *name, int *data, int Norf);
static void cfilewrite(const char *name, int *data, int nzero);
static void datafilewrite(const char *name, double *data, int nzero);


EIGENSOLVER_API void complexEigenSolver(int *A_Roffsets, int *A_Cindices, std::complex<double> *A_complex, int *B_Roffsets, int *B_Cindices, std::complex<double> *B_complex, int A_n, int B_n, int A_nzero, int B_nzero, double target_real, double target_imag, int option, int nums, std::complex<double> *eigenValues, std::complex<double> **eigenVectors) {
    EnsureEigenSolverDir("complex_solver");
    // 将参数写入文件 canshu.txt
    std::ofstream ofs;
    ofs.open("./complex_solver/canshu.txt", std::ios::out);
    ofs << A_n << std::endl
        << B_n << std::endl
        << target_real << std::endl
        << target_imag << std::endl
        << option << std::endl
        << nums << std::endl;
    ofs.close();

    // 将complex数据写入double中
    double *A_real = new double[A_nzero];
    double *A_imag = new double[A_nzero];

    for (size_t i = 0; i < A_nzero; ++i) {
        A_real[i] = A_complex[i].real();
        A_imag[i] = A_complex[i].imag();
    }

    double *B_real = new double[B_nzero];
    double *B_imag = new double[B_nzero];

    for (size_t i = 0; i < B_nzero; ++i) {
        B_real[i] = B_complex[i].real();
        B_imag[i] = B_complex[i].imag();
    }

    // A
    rfilewrite("./complex_solver/A_Roffsets.dat", A_Roffsets, A_n);
    cfilewrite("./complex_solver/A_Cindices.dat", A_Cindices, A_nzero);
    datafilewrite("./complex_solver/A_real.dat", A_real, A_nzero);
    datafilewrite("./complex_solver/A_imag.dat", A_imag, A_nzero);
    delete[] A_real;
    delete[] A_imag;

    // B
    rfilewrite("./complex_solver/B_Roffsets.dat", B_Roffsets, B_n);
    cfilewrite("./complex_solver/B_Cindices.dat", B_Cindices, B_nzero);
    datafilewrite("./complex_solver/B_real.dat", B_real, B_nzero);
    datafilewrite("./complex_solver/B_imag.dat", B_imag, B_nzero);
    delete[] B_real;
    delete[] B_imag;

    RunComplexEigenExe();

    // 读取特征值文件
    double *eigenValues_real = new double[nums];
    double *eigenValues_imag = new double[nums];

    std::ifstream ifs_EigenValues_real, ifs_EigenValues_imag;
    ifs_EigenValues_real.open("./complex_solver/eigenValues_real.txt", std::ios::in);
    ifs_EigenValues_imag.open("./complex_solver/eigenValues_imag.txt", std::ios::in);
    for (int i = 0; i < nums; i++) {
        ifs_EigenValues_real >> eigenValues_real[i];
        ifs_EigenValues_imag >> eigenValues_imag[i];
        eigenValues[i].real(eigenValues_real[i]);
        eigenValues[i].imag(eigenValues_imag[i]);
    }
    ifs_EigenValues_real.close();
    ifs_EigenValues_imag.close();
    delete[] eigenValues_real;
    delete[] eigenValues_imag;

    // 读取特征向量文件
    double **eigenVectors_real = new double *[nums];
    double **eigenVectors_imag = new double *[nums];
    for (size_t i = 0; i < nums; ++i) {
        eigenVectors_real[i] = new double[B_n];
        eigenVectors_imag[i] = new double[B_n];
    }

    std::ifstream ifs_EigenVectors_real, ifs_EigenVectors_imag;
    ifs_EigenVectors_real.open("./complex_solver/eigenVectors_real.txt", std::ios::in);
    ifs_EigenVectors_imag.open("./complex_solver/eigenVectors_imag.txt", std::ios::in);
    for (int i = 0; i < nums; i++) {
        for (int j = 0; j < B_n; j++) {
            ifs_EigenVectors_real >> eigenVectors_real[i][j];
            ifs_EigenVectors_imag >> eigenVectors_imag[i][j];
            eigenVectors[i][j].real(eigenVectors_real[i][j]);
            eigenVectors[i][j].imag(eigenVectors_imag[i][j]);
        }
    }
    ifs_EigenVectors_real.close();
    ifs_EigenVectors_imag.close();
    for (size_t i = 0; i < nums; ++i) {
        delete[] eigenVectors_real[i];
        delete[] eigenVectors_imag[i];
    }
    delete[] eigenVectors_real;
    delete[] eigenVectors_imag;

    // 清理文件
    remove("./complex_solver/A_Roffsets.dat");
    remove("./complex_solver/A_Cindices.dat");
    remove("./complex_solver/A_real.dat");
    remove("./complex_solver/A_imag.dat");
    remove("./complex_solver/B_Roffsets.dat");
    remove("./complex_solver/B_Cindices.dat");
    remove("./complex_solver/B_real.dat");
    remove("./complex_solver/B_imag.dat");
    remove("./complex_solver/canshu.txt");
    remove("./complex_solver/eigenValues_real.txt");
    remove("./complex_solver/eigenValues_imag.txt");
    remove("./complex_solver/eigenVectors_real.txt");
    remove("./complex_solver/eigenVectors_imag.txt");
}

EIGENSOLVER_API void realEigenSolver(int *A_Roffsets, int *A_Cindices, double *A_real, int *B_Roffsets, int *B_Cindices, double *B_real, int A_n, int B_n, int A_nzero, int B_nzero, double target, int option, int nums, double *eigenValues, double **eigenVectors) {
    EnsureEigenSolverDir("real_solver");
    //将参数写入文件 canshu.txt
    std::ofstream ofs;
    ofs.open("./real_solver/canshu.txt", std::ios::out);
    ofs << A_n << std::endl
        << B_n << std::endl
        << target << std::endl
        << option << std::endl
        << nums << std::endl;
    ofs.close();

    double *d_A_real = new double[A_nzero];
    double *d_B_real = new double[B_nzero];
    std::transform(A_real, A_real + A_nzero, d_A_real, [](double f) { return static_cast<double>(f); });
    std::transform(B_real, B_real + B_nzero, d_B_real, [](double f) { return static_cast<double>(f); });

    // A
    rfilewrite("./real_solver/A_Roffsets.dat", A_Roffsets, A_n);
    cfilewrite("./real_solver/A_Cindices.dat", A_Cindices, A_nzero);
    datafilewrite("./real_solver/A_real.dat", d_A_real, A_nzero);
    // B
    rfilewrite("./real_solver/B_Roffsets.dat", B_Roffsets, B_n);
    cfilewrite("./real_solver/B_Cindices.dat", B_Cindices, B_nzero);
    datafilewrite("./real_solver/B_real.dat", d_B_real, B_nzero);

    RunRealEigenExe();

    //读取特征值文件
    std::ifstream ifs_EigenValues;
    ifs_EigenValues.open("./real_solver/eigenValues.txt", std::ios::in);
    for (int i = 0; i < nums; i++) {
        ifs_EigenValues >> eigenValues[i];
    }
    ifs_EigenValues.close();

    //读取特征向量文件
    std::ifstream ifs_EigenVectors;
    ifs_EigenVectors.open("./real_solver/eigenVectors.txt", std::ios::in);
    for (int i = 0; i < nums; i++) {
        for (int j = 0; j < B_n; j++) {
            ifs_EigenVectors >> eigenVectors[i][j];
        }
    }
    ifs_EigenVectors.close();

    //清理文件
    remove("./real_solver/eigenValues.txt");
    remove("./real_solver/eigenVectors.txt");
    remove("./real_solver/A_Roffsets.dat");
    remove("./real_solver/A_Cindices.dat");
    remove("./real_solver/A_real.dat");
    remove("./real_solver/B_Roffsets.dat");
    remove("./real_solver/B_Cindices.dat");
    remove("./real_solver/B_real.dat");
    remove("./real_solver/canshu.txt");
}

static void rfilewrite(const char *name, int *data, int Norf) {
    FILE *fid1;
    fid1 = fopen(name, "wb");

    fwrite(data, sizeof(int), Norf + 1, fid1);
    fclose(fid1);
}

static void cfilewrite(const char *name, int *data, int nzero) {
    FILE *fid1;
    fid1 = fopen(name, "wb");

    fwrite(data, sizeof(int), nzero, fid1);
    fclose(fid1);
}

static void datafilewrite(const char *name, double *data, int nzero) {
    FILE *fid1;
    fid1 = fopen(name, "wb");

    fwrite(data, sizeof(double), nzero, fid1);
    fclose(fid1);
}
