#include"../include/outcar.h"
#include"../include/read_write.h"
using std::string;
using std::vector;
using namespace _outcar;
double get_energy()
{
	double energy = 0;
	FILE* fp_ = fopen("OUTCAR", "r");
	if (fp_ == NULL)
	{
		printf("OUTCAR IS NOT EXIST!\n");
		return energy;
	}
	char buf[1024];
	while (fgets(buf, 1024, fp_) != NULL)
	{
		if (strstr(buf, "energy(sigma->0)") != NULL)
		{
			sscanf(buf, "%*s%*s%*s%*s%*s%*s%*s%lf", &energy);
		}
	}
	fclose(fp_);
	return energy;
}

vector<double> get_stress()
{
	FILE* fp = fopen("OUTCAR", "r");
	if (fp == NULL)
	{
		printf("OUTCAR IS NOT EXIST!\n");
		return {};
	}
	vector<double> ans(6);//11          22          33          23          13          12
	char buf[1024];
	while (fgets(buf, 1024, fp) != NULL)
	{
		if (strstr(buf, "in kB") != NULL)
		{
			//XX          YY          ZZ          XY          YZ          ZX
			sscanf(buf, "%*s%*s%lf%lf%lf%lf%lf%lf", &ans[0], &ans[1], &ans[2], &ans[5], &ans[3], &ans[4]);
			break;
		}
	}
	fclose(fp);
	return ans;
}
vector<string> _outcar::str_split(string s, vector<char> sp)
{
	vector<string> ans;
	bool flag = 0;
	string str;
	for (int i = 0; i < s.size(); i++)
	{
		if (find(sp.begin(), sp.end(), s[i]) != sp.end())
		{
			flag = 1;
			continue;
		}
		else
		{
			if (flag == 1)
			{
				ans.push_back(str);
				str.clear();
				flag = 0;
			}
			str += s[i];
		}
	}
	if (!str.empty() && find(sp.begin(), sp.end(), str[0]) == sp.end())
		ans.push_back(str);
	return ans;
}
void _outcar::print_normal(FILE* fp, Para_Info* info) {
	{
		fprintf(fp, "#data	%s\n", info->name.c_str());
		fprintf(fp, "%s\n", m[info->key].c_str());
	}
};
void _outcar::print_null(FILE* fp, Para_Info* info) {};
void _outcar::print_GGA(FILE* fp, Para_Info* info) {
	{
		fprintf(fp, "#data	%s\n", info->name.c_str());
		if (m[info->key] == "--")
			m["GGA"] = m["LEXCH"];
		fprintf(fp, "%s\n", m["GGA"].c_str());
	}
}
void _outcar::print_ISPIN(FILE* fp, Para_Info* info) {
	{
		fprintf(fp, "#data	%s\n", info->name.c_str());
		if (m[info->key] == "1")
			fprintf(fp, "F\n");
		else if (m[info->key] == "2")
			fprintf(fp, "T\n");
	}
}
void _outcar::print_ISIF(FILE* fp, Para_Info* info)
{
	{
		fprintf(fp, "#data	%s\n", info->name.c_str());
		unordered_map<string, string > m_isif
		{
			{"0","atom position"},
			{"1","atom position"},
			{"2","atom position"},
			{"3","atom position&cell shape&cell volume"},
			{"4","atom position&cell shape"},
			{"5","cell shape"},
			{"6","cell shape&cell volume"},
			{"7","cell volume"},
		};
		fprintf(fp, "%s\n", m_isif[m[info->key]].c_str());
	}
}
void _outcar::print_else(FILE* fp)
{
	string val;
	if (m["ISTART"] == "1" && m["ICHARG"] == "11" && m["NSW"] == "0")
		val = "F";
	else
		val = "T";
	{
		fprintf(fp, "#data	Self_consistent\n");
		fprintf(fp, "%s\n", val.c_str());
	}
	val.clear();
	if (m["GGA"] == "--")
		m["GGA"] = m["LEXCH"];
	if (m["LHFCALC"] == "T" && m["GGA"] == "PE" && m["HFSCREEN"] == "0.2")
		val = "HSE06";
	else if (m["LHFCALC"] == "T" && m["GGA"] == "PE" && m["HFSCREEN"] == "0.3")
		val = "HSE03";
	else if (m["LHFCALC"] == "T" && m["GGA"] == "PS" && m["HFSCREEN"] == "0.2")
		val = "HSEsol";
	else if (m["LHFCALC"] == "T" && m["GGA"] == "PE")
		val = "PBE0";
	else if (m["LHFCALC"] == "T" && m["GGA"] == "B3")
		val = "B3LYP-VWN3";
	else if (m["LHFCALC"] == "T" && m["GGA"] == "B5")
		val = "B3LYP-VWN5";
	else if (m["LHFCALC"] == "T" && m["GGA"] == "LIBXC" && m["LIBXC1"] == "HYB_GGA_XC_B3PW91")
		val = "B3PW91";
	else if (m["LHFCALC"] == "T" && m["GGA"] == "LIBXC" && m["LIBXC1"] == "HYB_GGA_XC_B1WC")
		val = "B1-WC";
	else if (m["LHFCALC"] == "T" && m["METAGGA"] == "SCAN")
		val = "B3LYP-VWN5";
	else if (m["LHFCALC"] == "T" && m["AEXX"] == "1.0")
		val = "Hartree-Fock";
	{
		fprintf(fp, "#data	Hybrid_type\n");
		fprintf(fp, "%s\n", val.c_str());
	}
	val.clear();
	if (m["LDAUTYPE"] == "1")
		val = "Liechtenstein";
	else if (m["LDAUTYPE"] == "2")
		val = "Dudarev";
	else if (m["LDAUTYPE"] == "4")
		val = "Liechtenstein(no exchange splitting)";
	{
		fprintf(fp, "#data	DFT_U_type\n");
		fprintf(fp, "%s\n", val.c_str());
	}
	val.clear();
	if (m["IVDW"] == "1" || m["IVDW"] == "10")
		val = "DFT-D2";
	else if (m["IVDW"] == "11")
		val = "DFT-D3";
	else if (m["IVDW"] == "12")
		val = "DFT-D3";
	else if (m["IVDW"] == "13")
		val = "DFT-D4";
	else if (m["IVDW"] == "2" || m["IVDW"] == "20")
		val = "TS method";
	else if (m["IVDW"] == "21")
		val = "TS method(iterative Hirshfeld)";
	else if (m["IVDW"] == "202")
		val = "MBD-rSC";
	else if (m["IVDW"] == "263")
		val = "MBD-rSC/FI";
	else if (m["IVDW"] == "4")
		val = "dDsC";
	else if (m["IVDW"] == "3")
		val = "DFT-ulg";
	if (m["LUSE_VDW"] == "T" && m["GGA"] == "RE")
		val = "vdW-DF1";
	else if (m["LUSE_VDW"] == "T" && m["GGA"] == "ML")
		val = "vdW-DF2";
	else if (m["LUSE_VDW"] == "T" && m["GGA"] == "OR")
		val = "optPBE-vdW";
	else if (m["LUSE_VDW"] == "T" && m["GGA"] == "BO")
		val = "optB88-vdW";
	else if (m["LUSE_VDW"] == "T" && m["GGA"] == "MK")
		val = "optB86b-vdW";
	else if (m["LUSE_VDW"] == "T" && m["GGA"] == "MK" && m["ZAB_VDW"] == "-1.8867")
		val = "rev-vdW-DF2";
	else if (m["LUSE_VDW"] == "T" && m["GGA"] == "CX")
		val = "vdW-DF-cx";
	else if (m["LUSE_VDW"] == "T" && m["GGA"] == "ML")
		val = "rVV10";
	else if (m["LUSE_VDW"] == "T" && m["METAGGA"] == "SCAN")
		val = "SCAN+rVV10";
	else if (m["LUSE_VDW"] == "T" && m["METAGGA"] == "R2SCAN")
		val = "r2SCAN + rVV10";
	else if (m["LVDW"] == "T" && !m.count("IVDW"))
		val = "DFT-D2";
	{
		fprintf(fp, "#data	VDW_D_type\n");
		fprintf(fp, "%s\n", val.c_str());
	}
}
void _outcar::print_text(FILE* fp, const char name[], vector<string> v)
{
	if (v.empty())
		return;
	fprintf(fp, "#data	%s\n", name);
	for (int i = 0; i < v.size(); i++)
		fprintf(fp, "%s", v[i].c_str());
}
void _outcar::OUTCAR::DB_GetInfo(Para_Info para, const char input_file[])
{
	FILE* fp = fopen(input_file, "r");
	string value;
	if (fp == NULL)
	{
		printf("%s in not exist!\n", input_file);
		return;
	}
	char buf[1024];
	int iat = 0;
	int cnt = 0;
	while (fgets(buf, 1024, fp))
	{
		if (strstr(buf, "free  energy   TOTEN"))
		{
			this->energy.clear();
			cnt = 3;
			while (cnt--)
			{
				this->energy.push_back(buf);
				fgets(buf, 1024, fp);
			}
			continue;
		}
		if (strstr(buf, "in kB"))
		{
			this->stress.clear();
			cnt = 2;
			while (cnt--)
			{
				this->stress.push_back(buf);
				fgets(buf, 1024, fp);
			}
			continue;
		}
		if (strstr(buf, "TOTAL-FORCE"))
		{
			this->force.clear();
			while (1)
			{
				this->force.push_back(buf);
				if (strstr(buf, "total drift"))
					break;
				fgets(buf, 1024, fp);
			}
			continue;
		}
		if (strstr(buf, "total charge") && !strstr(buf, "total charge-"))
		{
			this->charge.clear();
			while (1)
			{
				this->charge.push_back(buf);
				if (strstr(buf, "tot") && !strstr(buf, "#"))
					break;
				fgets(buf, 1024, fp);
			}
			continue;
		}
		if (strstr(buf, "magnetization (x)"))
		{
			this->magnitization_x.clear();
			while (1)
			{
				this->magnitization_x.push_back(buf);
				if (strstr(buf, "tot") && !strstr(buf, "#"))
					break;
				fgets(buf, 1024, fp);
			}
			continue;
		}
		if (strstr(buf, "magnetization (y)"))
		{
			this->magnitization_y.clear();
			while (1)
			{
				this->magnitization_y.push_back(buf);
				if (strstr(buf, "tot") && !strstr(buf, "#"))
					break;
				fgets(buf, 1024, fp);
			}
			continue;
		}
		if (strstr(buf, "magnetization (z)"))
		{
			this->magnitization_z.clear();
			while (1)
			{
				this->magnitization_z.push_back(buf);
				if (strstr(buf, "tot") && !strstr(buf, "#"))
					break;
				fgets(buf, 1024, fp);
			}
			continue;
		}
		string tmp = buf;
		string s = tmp.substr(0, find(tmp.begin(), tmp.end(), para.split[0]) - tmp.begin());
		vector<string> t = str_split(s, { ' ','\t','\r','\n' });
		if (t.empty())
			continue;
		else if (t.back() == para.key)
		{
			int cnt = 0;
			char* p = strrchr(buf, para.split[0]) + 1;
			char* token = strtok(p, " ");
			while (token != NULL && cnt < para.num)
			{
				value += token;
				token = strtok(NULL, " ");
				cnt++;
			}
			if (value.back() == '\n')
				value.pop_back();
			m[t.back()] = value;
			break;
		}
	}
	fclose(fp);
}

void _outcar::OUTCAR::output(const char output[])
{
	FILE* fp = fopen(output, "at+");
	//basic parameter from input file
	auto cpfile = [fp](const char source[]) {
		FILE* fps = fopen(source, "r");
		if (fps == nullptr)
			return;
		char buf[1024];
		while (fgets(buf, 1024, fps))
			fputs(buf, fp);
		fclose(fps);
	};
	fprintf(fp, "#data	STRUCT\n");
	cpfile("CONTCAR");
	fprintf(fp, "#data	POSCAR\n");
	cpfile("POSCAR");
	fprintf(fp, "#data	INCAR\n");
	cpfile("INCAR");
	fprintf(fp, "#data	KPOINTS\n");
	cpfile("KPOINTS");
	for (int i = 0; i < parameter.size(); i++)
		parameter[i].print(fp, &parameter[i]);
	print_else(fp);
	print_text(fp, "Energy_in_OUTCAR", energy);
	print_text(fp, "Stress_in_OUTCAR", stress);
	print_text(fp, "Force_in_OUTCAR", force);
	print_text(fp, "Charge_in_OUTCAR", charge);
	vector<string> magnitization;
	for (int i = 0; i < magnitization_x.size(); i++)
		magnitization.push_back(magnitization_x[i]);
	for (int i = 0; i < magnitization_y.size(); i++)
		magnitization.push_back(magnitization_y[i]);
	for (int i = 0; i < magnitization_z.size(); i++)
		magnitization.push_back(magnitization_z[i]);
	print_text(fp, "Magnitization_in_OUTCAR", magnitization);
	fclose(fp);
}

