/********************************************************
 *  ██████╗  ██████╗████████╗██╗
 * ██╔════╝ ██╔════╝╚══██╔══╝██║
 * ██║  ███╗██║        ██║   ██║
 * ██║   ██║██║        ██║   ██║
 * ╚██████╔╝╚██████╗   ██║   ███████╗
 *  ╚═════╝  ╚═════╝   ╚═╝   ╚══════╝
 * Geophysical Computational Tools & Library (GCTL)
 *
 * Copyright (c) 2023  Yi Zhang (yizhang-geo@zju.edu.cn)
 *
 * GCTL is distributed under a dual licensing scheme. You can redistribute 
 * it and/or modify it under the terms of the GNU Lesser General Public 
 * License as published by the Free Software Foundation, either version 2 
 * of the License, or (at your option) any later version. You should have 
 * received a copy of the GNU Lesser General Public License along with this 
 * program. If not, see <http://www.gnu.org/licenses/>.
 * 
 * If the terms and conditions of the LGPL v.2. would prevent you from using 
 * the GCTL, please consider the option to obtain a commercial license for a 
 * fee. These licenses are offered by the GCTL's original author. As a rule, 
 * licenses are provided "as-is", unlimited in time for a one time fee. Please 
 * send corresponding requests to: yizhang-geo@zju.edu.cn. Please do not forget 
 * to include some description of your company and the realm of its activities. 
 * Also add information on how to contact you by electronic and paper mail.
 ******************************************************/

#include "text_io.h"
#include "cmath"


gctl::text_descriptor::text_descriptor()
{
	set();
}

gctl::text_descriptor::text_descriptor(std::string filename, std::string file_exten, 
	std::string col_order, char deli, char att_sym, char tag_sym, int h_num, int io_psn)
{
	set(filename, file_exten, col_order, deli, att_sym, tag_sym, h_num, io_psn);
}

gctl::text_descriptor::~text_descriptor()
{
	clear();
}

void gctl::text_descriptor::set(std::string filename, std::string file_exten, 
	std::string col_order, char deli, char att_sym, char tag_sym, int h_num, int io_psn)
{
	file_name_ = filename;
	file_ext_  = file_exten;
	col_str_ = col_order;
	att_sym_ = att_sym;
	tag_sym_ = tag_sym;
	delimiter_ = deli;
	head_num_ = h_num;
	float_psn_ = io_psn;
	return;
}

void gctl::text_descriptor::clear()
{
	head_strs_.clear();
	annotates_.clear();
	tags_.clear();
	return;
}

void gctl::text_descriptor::set_float_precision(int psn)
{
	float_psn_ = psn;
	return;
}

void gctl::text_descriptor::set_head_number(int n)
{
	head_num_ = n;
	return;
}

void gctl::text_descriptor::set_annotation_symbol(char sym)
{
	att_sym_ = sym;
	return;
}

void gctl::text_descriptor::set_tag_symbol(char sym)
{
	tag_sym_ = sym;
	return;
}

void gctl::text_descriptor::set_column_delimeter(char sym)
{
	delimiter_ = sym;
	return;
}

void gctl::text_descriptor::set_file_extension(std::string ext_s)
{
	file_ext_ = ext_s;
	return;
}

void gctl::text_descriptor::set_column_order(std::string col_s)
{
	col_str_ = col_s;
	return;
}

void gctl::read_text_lines(text_descriptor &file_desc, std::vector<std::string> &out_vec, int start_line, int end_line)
{
	if (start_line < 0 || start_line > end_line)
	{
		throw invalid_argument("Invalid line numbers. From gctl::readlines(...)");
	}

	std::ifstream infile;
	open_infile(infile, file_desc.file_name_, file_desc.file_ext_);

	if (!out_vec.empty()) out_vec.clear();

	std::string tmp_line;
	if (start_line == 0 && end_line == 0)
	{
		for (size_t i = 0; i < file_desc.head_num_; i++)
		{
			getline(infile, tmp_line);
		}
		
		size_t tl = 0, al = 0, gl = 0;
		while (getline(infile, tmp_line))
		{
			if (tmp_line[0] == file_desc.att_sym_) al++;
			else if (tmp_line[0] == file_desc.tag_sym_) gl++;
			else tl++;
		}

		out_vec.reserve(tl);
		infile.clear(std::ios::goodbit);
    	infile.seekg(0, std::ios::beg);

		file_desc.annotates_.reserve(al);
		file_desc.tags_.reserve(gl);
		file_desc.head_strs_.reserve(1024);

		for (size_t i = 0; i < file_desc.head_num_; i++)
		{
			getline(infile, tmp_line);
			file_desc.head_strs_.push_back(tmp_line);
		}

		while (getline(infile, tmp_line))
		{
			if (tmp_line[0] == file_desc.att_sym_)
			{
				// remove first symbol and space
				tmp_line.erase(0, tmp_line.find_first_not_of(file_desc.att_sym_));
				tmp_line.erase(0, tmp_line.find_first_not_of(" \t"));
				file_desc.annotates_.push_back(tmp_line);
			}
			else if (tmp_line[0] == file_desc.tag_sym_)
			{
				// remove first symbol and space
				tmp_line.erase(0, tmp_line.find_first_not_of(file_desc.tag_sym_));
				tmp_line.erase(0, tmp_line.find_first_not_of(" \t"));
				file_desc.tags_.push_back(tmp_line);
			}
			else out_vec.push_back(tmp_line);
		}

		infile.close();
		return;
	}

	out_vec.reserve(end_line - start_line + 1);
	file_desc.annotates_.reserve(1024);
	file_desc.tags_.reserve(1024);
	file_desc.head_strs_.reserve(1024);

	for (int i = 0; i < start_line + file_desc.head_num_; i++)
	{
		std::getline(infile, tmp_line);
		file_desc.head_strs_.push_back(tmp_line);
	}

	for (int i = start_line; i <= end_line; i++)
	{
		std::getline(infile, tmp_line);
		if (tmp_line[0] == file_desc.att_sym_)
		{
			// remove first symbol and space
			tmp_line.erase(0, tmp_line.find_first_not_of(file_desc.att_sym_));
			tmp_line.erase(0, tmp_line.find_first_not_of(" \t"));
			file_desc.annotates_.push_back(tmp_line);
		}
		else if (tmp_line[0] == file_desc.tag_sym_)
		{
			// remove first symbol and space
			tmp_line.erase(0, tmp_line.find_first_not_of(file_desc.tag_sym_));
			tmp_line.erase(0, tmp_line.find_first_not_of(" \t"));
			file_desc.tags_.push_back(tmp_line);
		}
		else out_vec.push_back(tmp_line);
	}

	infile.close();
	return;
}

void gctl::read_annotations(std::string filename, std::vector<std::string> &out_vec, 
	std::string file_ext, char annotate)
{
	std::ifstream infile;
	open_infile(infile, filename, file_ext);

	if (!out_vec.empty()) out_vec.clear();

	std::string tmp_line;
	while (std::getline(infile, tmp_line))
	{
		if (!tmp_line.empty() && tmp_line[0] == annotate)
		{
			// 去掉注释符
			tmp_line = tmp_line.substr(1, tmp_line.length());
			// 去掉可能存在的头尾的空白符号
			tmp_line.erase(0, tmp_line.find_first_not_of(" \t"));
			tmp_line.erase(tmp_line.find_last_not_of(" \t") + 1);
			// 保存到字符串数组
			out_vec.push_back(tmp_line);
		}
	}

	if (out_vec.empty())
	{
		GCTL_ShowWhatError("No annotations found, From read_annotations(...)", 
			GCTL_WARNING_ERROR, 0, 0, 0);
	}

	infile.close();
	return;
}

void gctl::get_xyz_points(const _2d_vector &data_table, array<point3dc> &out_ps, std::string order)
{
	//默认的读入的数据列为前三列
	int orders[3] = {0, 1, 2};

	std::stringstream tmp_ss;
	if (order != "0,1,2")
	{
		str2ss(order, tmp_ss, ",");
		tmp_ss >> orders[0] >> orders[1] >> orders[2];

		if (tmp_ss.fail())
		{
			throw runtime_error("Fail to read column orders. From gctl::get_xyz_points(...)");
		}
	}

	int max_col = GCTL_MAX(GCTL_MAX(orders[0], orders[1]), orders[2]);
	for (int i = 0; i < data_table.size(); i++)
	{
		if (data_table[i].size() < max_col+1)
		{
			throw runtime_error("Invalid column size. From gctl::get_xyz_points(...)");
		}
	}

	// convert to point3dc
	out_ps.resize(data_table.size());
	for (int i = 0; i < out_ps.size(); i++)
	{
		out_ps.at(i).x = data_table.at(i).at(orders[0]);
		out_ps.at(i).y = data_table.at(i).at(orders[1]);
		out_ps.at(i).z = data_table.at(i).at(orders[2]);
	}
	return;
}

void gctl::get_xyz_points(const _2d_vector &data_table, array<point3ds> &out_ps, std::string order)
{
	//默认的读入的数据列为前三列
	int orders[3] = {0, 1, 2};

	std::stringstream tmp_ss;
	if (order != "0,1,2")
	{
		str2ss(order, tmp_ss, ",");
		tmp_ss >> orders[0] >> orders[1] >> orders[2];

		if (tmp_ss.fail())
		{
			throw runtime_error("Fail to read column orders. From gctl::get_xyz_points(...)");
		}
	}

	int max_col = GCTL_MAX(GCTL_MAX(orders[0], orders[1]), orders[2]);
	for (int i = 0; i < data_table.size(); i++)
	{
		if (data_table[i].size() < max_col+1)
		{
			throw runtime_error("Invalid column size. From gctl::get_xyz_points(...)");
		}
	}

	// convert to point3dc
	out_ps.resize(data_table.size());
	for (int i = 0; i < out_ps.size(); i++)
	{
		out_ps.at(i).lon = data_table.at(i).at(orders[0]);
		out_ps.at(i).lat = data_table.at(i).at(orders[1]);
		out_ps.at(i).rad = data_table.at(i).at(orders[2]);
	}
	return;
}

void gctl::get_xyz_points(std::string filename, array<point3dc> &out_ps, text_descriptor &desc, 
	std::string order, std::string file_ext)
{
	// read file
	_2d_vector points_vec;
	read_text2vector2d(desc, points_vec);
	get_xyz_points(points_vec, out_ps, order);
	destroy_vector(points_vec);
	return;
}

void gctl::get_xyz_points(std::string filename, array<point3ds> &out_ps, text_descriptor &desc, 
	std::string order, std::string file_ext)
{
	// read file
	_2d_vector points_vec;
	read_text2vector2d(desc, points_vec);
	get_xyz_points(points_vec, out_ps, order);
	destroy_vector(points_vec);
	return;
}

/**
 * @brief      读取数据列
 *
 * @param[in]  data_table  二维数据向量
 * @param[in]  dat_val     输入的数据数组列表
 * @param[in]  dat_col     输入的数据列（数据列不足时会按从左到右顺序读入）
 */
void gctl::get_data_column(const _2d_vector &data_table, std::initializer_list<array<double>*> dat_val, 
	std::initializer_list<int> dat_col)
{
	// 初始化数据列
	array<int> orders(dat_val.size());
	for (int i = 0; i < dat_val.size(); i++)
	{
		orders[i] = i;
	}

	int c_count = 0;
	std::initializer_list<int>::iterator ii;
	for (ii = dat_col.begin(); ii != dat_col.end(); ++ii)
	{
		orders[c_count] = *ii;
		c_count++;
	}

	c_count = 0;
	std::initializer_list<array<double>*>::iterator id;
	array<double> *dat_ptr;
	for (id = dat_val.begin(); id != dat_val.end(); ++id)
	{
		dat_ptr = *id;
		dat_ptr->resize(data_table.size());
		for (int i = 0; i < data_table.size(); i++)
		{
			dat_ptr->at(i) = data_table[i][orders[c_count]];
		}
		c_count++;
	}
	return;
}

void gctl::get_data_column(std::string filename, std::initializer_list<array<double>*> dat_val, 
	std::initializer_list<int> dat_col, text_descriptor &desc, std::string file_ext)
{
	// read file
	_2d_vector data_vec;
	read_text2vector2d(desc, data_vec);
	get_data_column(data_vec, dat_val, dat_col);
	destroy_vector(data_vec);
	return;
}


void gctl::save_data_column(std::ofstream &outfile, std::initializer_list<array<double>* > dat_val, 
	std::initializer_list<std::string> dat_name, char delimiter, int precision)
{
	std::initializer_list<std::string>::iterator is;
	outfile << "#";
	for (is = dat_name.begin(); is != dat_name.end(); ++is)
	{
		outfile << delimiter << *is;
	}
	outfile << std::endl;

	size_t d_num = dat_val.size(), d_size = 0;
	std::initializer_list<array<double>*>::iterator id;
	array<double> *dat_ptr;
	for (id = dat_val.begin(); id != dat_val.end(); ++id)
	{
		dat_ptr = *id;
		d_size = GCTL_MAX(d_size, dat_ptr->size());
	}

	_2d_matrix out_data(d_num, d_size, NAN);
	size_t d_count = 0;
	for (id = dat_val.begin(); id != dat_val.end(); ++id)
	{
		dat_ptr = *id;
		for (size_t j = 0; j < dat_ptr->size(); j++)
		{
			out_data[d_count][j] = dat_ptr->at(j);
		}
		d_count++;
	}

	for (size_t i = 0; i < d_size; i++)
	{
		outfile << i + 1;
		for (size_t j = 0; j < d_num; j++)
		{
			outfile << std::setprecision(precision) << delimiter << out_data[j][i];
		}
		outfile << std::endl;
	}
	return;
}

void gctl::save_data_column(std::ofstream &outfile, std::initializer_list<std::vector<double>* > dat_val, 
	std::initializer_list<std::string> dat_name, char delimiter, int precision)
{
	std::initializer_list<std::string>::iterator is;
	outfile << "#";
	for (is = dat_name.begin(); is != dat_name.end(); ++is)
	{
		outfile << delimiter << *is;
	}
	outfile << std::endl;

	size_t d_num = dat_val.size(), tmp_size, d_size = 0;
	std::initializer_list<std::vector<double>* >::iterator id;
	std::vector<double> *dat_ptr;
	for (id = dat_val.begin(); id != dat_val.end(); ++id)
	{
		dat_ptr = *id;
		tmp_size = dat_ptr->size(); // 超细节 size()返回的值类型为 unsigned int 不要直接和 int 做比较
		d_size = GCTL_MAX(d_size, tmp_size);
	}

	if (d_size <= 0)
	{
		std::string err_str = "Invalid maximal data size (" + std::to_string(d_size) + "), From gctl::save_data_column(...)";
		throw runtime_error(err_str);
	}

	_2d_matrix out_data(d_num, d_size, NAN);
	size_t d_count = 0;
	for (id = dat_val.begin(); id != dat_val.end(); ++id)
	{
		dat_ptr = *id;
		for (size_t j = 0; j < dat_ptr->size(); j++)
		{
			out_data[d_count][j] = dat_ptr->at(j);
		}
		d_count++;
	}

	for (size_t i = 0; i < d_size; i++)
	{
		outfile << i + 1;
		for (size_t j = 0; j < d_num; j++)
		{
			outfile << std::setprecision(precision) << delimiter << out_data[j][i];
		}
		outfile << std::endl;
	}
	return;
}
