/********************************************************
 *  ██████╗  ██████╗████████╗██╗
 * ██╔════╝ ██╔════╝╚══██╔══╝██║
 * ██║  ███╗██║        ██║   ██║
 * ██║   ██║██║        ██║   ██║
 * ╚██████╔╝╚██████╗   ██║   ███████╗
 *  ╚═════╝  ╚═════╝   ╚═╝   ╚══════╝
 * 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 tcription of your company and the realm of its activities. 
 * Also add information on how to contact you by electronic and paper mail.
 ******************************************************/

#include "dsv_io.h"

gctl::dsv_io::dsv_io()
{
    file_ = "";
    att_sym_ = '#';
	tag_sym_ = '!';
	deli_sym_ = ' ';
	head_num_ = 0;
    row_num_ = 0;
    col_num_ = 0;
}

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

gctl::dsv_io::dsv_io(std::string filename, std::string file_exten, table_headtype_e t)
{
    file_ = "";
    att_sym_ = '#';
	tag_sym_ = '!';
	deli_sym_ = ' ';
	head_num_ = 0;
    row_num_ = 0;
    col_num_ = 0;

    if (file_exten == ".csv") load_csv(filename, t);
    else load_text(filename, file_exten, t);
}

void gctl::dsv_io::clear()
{
    file_ = "";
    att_sym_ = '#';
	tag_sym_ = '!';
	deli_sym_ = ' ';
	head_num_ = 0;
    row_num_ = 0;
    col_num_ = 0;

    destroy_vector(heads_);
    destroy_vector(annotates_);
    destroy_vector(tags_);
    destroy_vector(table_);
	return;
}

void gctl::dsv_io::get_row_names(std::vector<std::string> &names)
{
    names.resize(row_num_);
    for (size_t i = 1; i <= row_num_; i++)
    {
        names[i - 1] = table_[i][0].str_;
    }
    return;
}

void gctl::dsv_io::get_column_names(std::vector<std::string> &names)
{
    names.resize(col_num_);
    for (size_t i = 1; i <= col_num_; i++)
    {
        names[i - 1] = table_[0][i].str_;
    }
    return;
}

void gctl::dsv_io::set_row_names(const std::vector<std::string> &names, std::string corner_name)
{
    for (size_t i = 1; i <= std::min(row_num_, (int) names.size()); i++)
    {
        table_[i][0].str_ = names[i - 1];
    }

    table_[0][0].str_ = corner_name;
    return;
}

void gctl::dsv_io::set_column_names(const std::vector<std::string> &names)
{
    for (size_t i = 1; i <= std::min(col_num_, (int) names.size()); i++)
    {
        table_[0][i].str_ = names[i - 1];
    }
    return;
}

void gctl::dsv_io::set_row_type(cell_type_e t, int idx)
{
    if (idx > row_num_ || idx <= 0)
    {
        throw std::runtime_error("[gctl::dsv_io] Invalid column index.");
    }

    for (size_t i = 0; i <= col_num_; i++)
    {
        table_[idx][i].type_ = t;
    }
}

void gctl::dsv_io::set_row_type(cell_type_e t, std::string name)
{
    set_row_type(t, name_index(name, true));
}

void gctl::dsv_io::set_column_type(cell_type_e t, int idx)
{
    if (idx > col_num_ || idx <= 0)
    {
        throw std::runtime_error("[gctl::dsv_io] Invalid column index.");
    }

    for (size_t i = 0; i <= row_num_; i++)
    {
        table_[i][idx].type_ = t;
    }
    return;
}

void gctl::dsv_io::set_column_type(cell_type_e t, std::string name)
{
    set_column_type(t, name_index(name, false));
}

void gctl::dsv_io::load_text(std::string filename, std::string file_exten, table_headtype_e t)
{
    std::ifstream infile;
    open_infile(infile, filename, file_exten);

    file_ = filename + file_exten;

    int h = 0;
    std::string tmp_line;
    std::vector<std::string> lines;
    while (std::getline(infile, tmp_line))
    {
        if (tmp_line.empty()) continue; // 跳过空行 空行不会并记入头信息计数中
        else if (tmp_line[0] == att_sym_) // 注释行或者标记行 # #!
        {
            if (tmp_line[1] == tag_sym_) // #!
            {
                tmp_line = tmp_line.substr(2); // 去掉前两个字符
                tmp_line.erase(0, tmp_line.find_first_not_of(" \t"));
                tmp_line.erase(tmp_line.find_last_not_of(" \t") + 1);
                tags_.push_back(tmp_line);
                continue;
            }
            
            // #
            tmp_line = tmp_line.substr(1); // 去掉第一个字符
            tmp_line.erase(0, tmp_line.find_first_not_of(" \t"));
            tmp_line.erase(tmp_line.find_last_not_of(" \t") + 1);
            annotates_.push_back(tmp_line);
        }
        else if (h < head_num_) //读入到头信息中
        {
            heads_.push_back(tmp_line);
            h++;
        }
        else lines.push_back(tmp_line);
    }
    infile.close();

    table_.resize(lines.size());

    int cn, cn_max = 0;
    std::vector<std::string> tmp_cols;
    for (size_t i = 0; i < lines.size(); i++)
    {
        tmp_cols.clear();
        parse_string_to_vector(lines[i], deli_sym_, tmp_cols);
        
        // 动态调整列数
        cn = tmp_cols.size();
        cn_max = std::max(cn, cn_max);

        table_[i].resize(tmp_cols.size());
        for (size_t j = 0; j < tmp_cols.size(); j++)
        {
            table_[i][j].value(tmp_cols[j]);
        }
    }

    // 补齐可能的空格
    table_cell empty_cell;
    for (size_t i = 0; i < table_.size(); i++)
    {
        cn = table_[i].size();
        for (size_t j = cn; j < cn_max; j++)
        {
            table_[i].push_back(empty_cell);
        }
    }

    std::vector<table_cell> empty_line;
    if (t == NoHead) // 无表头 需要补齐空白的列头和行头
    {
        row_num_ = table_.size();
        col_num_ = table_[0].size();

        table_.emplace(table_.begin(), empty_line);
        table_[0].resize(col_num_);

        for (size_t i = 0; i < table_.size(); i++)
        {
            table_[i].emplace(table_[i].begin(), empty_cell);
        }
    }

    if (t == ColumnHead) // 有列头 需要补齐空白的行头
    {
        row_num_ = table_.size() - 1;
        col_num_ = table_[0].size();

        for (size_t i = 0; i < table_.size(); i++)
        {
            table_[i].emplace(table_[i].begin(), empty_cell);
        }
    }

    if (t == RowHead) // 有行头 需要补齐空白的列头
    {
        row_num_ = table_.size();
        col_num_ = table_[0].size() - 1;

        table_.emplace(table_.begin(), empty_line);
        table_[0].resize(col_num_ + 1);
    }
    
    if (t == BothHead) // 有行头和列头
    {
        row_num_ = table_.size() - 1;
        col_num_ = table_[0].size() - 1;
    }

    destroy_vector(lines);
    return;
}

void gctl::dsv_io::load_csv(std::string filename, table_headtype_e t)
{
    set_delimeter(',');
    load_text(filename, ".csv", t);
    return;
}

void gctl::dsv_io::save_text(std::string filename, std::string file_exten)
{
    std::ofstream outfile;
    open_outfile(outfile, filename, file_exten);

    for (int i = 0; i < heads_.size(); i++)
    {
        outfile << heads_[i] << std::endl;
    }

    for (int i = 0; i < tags_.size(); i++)
    {
        outfile << "#! " << tags_[i] << std::endl;
    }

    for (int i = 0; i < annotates_.size(); i++)
    {
        outfile << "# " << annotates_[i] << std::endl;
    }

    // 单独处理第一行 即列头
    bool line_st = false;
    if (table_[0][0].out_ok_ && table_[0][0].str_ != "")
    {
        outfile << table_[0][0].str_;
        line_st = true;
    }

    for (int j = 1; j <= col_num_; j++)
    {
        if (line_st && table_[0][j].out_ok_ && table_[0][j].str_ != "") outfile << deli_sym_ << table_[0][j].str_; // line started
        else if (table_[0][j].out_ok_ && table_[0][j].str_ != "") // line not started
        {
            outfile << table_[0][j].str_;
            line_st = true; // start line
        }
    }

    if (line_st) outfile << std::endl;

    // 处理余下的行
    for (int i = 1; i <= row_num_; i++)
    {
        line_st = false;

        // 单独处理第一列 即行头
        if (table_[i][0].out_ok_ && table_[i][0].str_ != "")
        {
            outfile << table_[i][0].str_;
            line_st = true;
        }

        for (int j = 1; j <= col_num_; j++)
        {
            if (line_st && table_[i][j].out_ok_) outfile << deli_sym_ << table_[i][j].str_; // line started
            else if (table_[i][j].out_ok_) // line not started
            {
                outfile << table_[i][j].str_;
                line_st = true; // start line
            }
        }
        outfile << std::endl;
    }
    
    outfile.close();
    return;
}

void gctl::dsv_io::save_csv(std::string filename)
{
    set_delimeter(',');
    save_text(filename, ".csv");
    return;
}

void gctl::dsv_io::init_table(int row, int col)
{
    if (!table_.empty()) clear();
    
    row_num_ = row;
    col_num_ = col;
    
    // 初始的列头和行头均为空白
    table_.resize(row_num_ + 1);
    for (size_t i = 0; i < row_num_ + 1; i++)
    {
        table_[i].resize(col_num_ + 1);
    }
    return;
}

void gctl::dsv_io::info(table_headtype_e t)
{
    std::clog << "File: " << file_ << "\n------------\n";
    std::clog << "Head(s): " << head_num_ << "\n";
    std::clog << "Annotation(s): " << annotates_.size() << "\n";
    std::clog << "Tag(s): " << tags_.size() << "\n";
    std::clog << "------------\n";

    if (t == ColumnHead || t == BothHead)
    {
        std::clog << "Columns:\n";
        for (size_t i = 1; i <= col_num_; i++)
        {
            if (table_[0][i].str_ != "")
            {
                std::clog << table_[0][i].str_ << " | ";
                if (table_[1][i].type_ == String) std::clog << "String | ";
                if (table_[1][i].type_ == Int) std::clog << "Int | ";
                if (table_[1][i].type_ == Float) std::clog << "Float | ";
                std::clog << table_[1][i].str_ << " -> " << table_[row_num_][i].str_;
            }
            else
            {
                std::clog << "C" + std::to_string(i) << " | ";
                if (table_[1][i].type_ == String) std::clog << "String | ";
                if (table_[1][i].type_ == Int) std::clog << "Int | ";
                if (table_[1][i].type_ == Float) std::clog << "Float | ";
                std::clog << table_[1][i].str_ << " -> " << table_[row_num_][i].str_;
            }

            if (!table_[0][i].out_ok_) std::clog << " (No output)";
            std::clog << std::endl;
        }
        std::clog << "============\n";
    }
    
    if (t == RowHead || t == BothHead)
    {
        std::clog << "Rows:\n";
        for (size_t i = 1; i <= row_num_; i++)
        {
            if (table_[i][0].str_ != "")
            {
                std::clog << table_[i][0].str_ << " | ";
                if (table_[i][1].type_ == String) std::clog << "String | ";
                if (table_[i][1].type_ == Int) std::clog << "Int | ";
                if (table_[i][1].type_ == Float) std::clog << "Float | ";
                std::clog << table_[i][1].str_ << " -> " << table_[i][col_num_].str_;
            }
            else
            {
                std::clog << "R" + std::to_string(i) << " | ";
                if (table_[i][1].type_ == String) std::clog << "String | ";
                if (table_[i][1].type_ == Int) std::clog << "Int | ";
                if (table_[i][1].type_ == Float) std::clog << "Float | ";
                std::clog << table_[i][1].str_ << " -> " << table_[i][col_num_].str_;
            }

            if (!table_[i][0].out_ok_) std::clog << " (No output)";
            std::clog << std::endl;
        }
        std::clog << "============\n";
    }
    return;
}

int gctl::dsv_io::name_index(std::string name, bool iter_row)
{
    // 拾取行号或列号 格式为R<id>和C<id>
    std::smatch ret;
    std::regex patr("R(\\d*)"), patc("C(\\d*)");
    
    if (iter_row)
    {
        for (size_t i = 1; i <= row_num_; i++)
		{
			if (table_[i][0].str_ == name) return i;
        }

        if (regex_search(name, ret, patr))
        {
            int r = atoi(std::string(ret[1]).c_str());
            if (r >= 1 && r <= row_num_) return r;
            else return -1;
        }

		return -1;
    }
    else
    {
        for (size_t i = 1; i <= col_num_; i++)
		{
            if (table_[0][i].str_ == name) return i;
        }

        if (regex_search(name, ret, patc))
        {
            int c = atoi(std::string(ret[1]).c_str());
            if (c >= 1 && c <= col_num_) return c;
            else return -1;
        }

		return -1;
    }
}

void gctl::dsv_io::column_output(int idx, switch_type_e s)
{
    if (idx > col_num_ || idx <= 0)
    {
        throw std::runtime_error("[gctl::dsv_io] Invalid column index.");
    }

    for (size_t i = 0; i <= row_num_; i++)
    {
        if (s == Enable) table_[i][idx].out_ok_ = true;
        else table_[i][idx].out_ok_ = false;
    }
    return;
}

void gctl::dsv_io::column_output(std::string name, switch_type_e s)
{
    column_output(name_index(name), s);
    return;
}

void gctl::dsv_io::row_output(int idx, switch_type_e s)
{
    if (idx > row_num_ || idx <= 0)
    {
        throw std::runtime_error("[gctl::dsv_io] Invalid row index.");
    }

    for (size_t i = 0; i <= col_num_; i++)
    {
        if (s == Enable) table_[idx][i].out_ok_ = true;
        else table_[idx][i].out_ok_ = false;
    }
    return;
}

void gctl::dsv_io::row_output(std::string name, switch_type_e s)
{
    row_output(name_index(name), s);
    return;
}

void gctl::dsv_io::add_column(std::string name)
{
    table_cell empty_cell;
    for (size_t i = 0; i < table_.size(); i++)
    {
        table_[i].push_back(empty_cell);
    }

    table_[0].back().str_ = name;
    col_num_++;
    return;
}

void gctl::dsv_io::add_column(int idx, std::string name)
{
    table_cell empty_cell;
    if (idx <= 0)
    {
        for (size_t i = 0; i < table_.size(); i++)
        {
            table_[i].push_back(empty_cell);
        }

        table_[0].back().str_ = name;
        col_num_++;
    }
    else
    {
        for (size_t i = 0; i < table_.size(); i++)
        {
            table_[i].insert(table_[i].begin() + idx, empty_cell);
        }

        table_[0][idx].str_ = name;
        col_num_++;
    }
    return;
}

void gctl::dsv_io::add_column(std::string id_name, std::string name)
{
    add_column(name_index(id_name), name);
    return;
}

void gctl::dsv_io::add_row(std::string name)
{
    std::vector<table_cell> empty_line;
    table_.push_back(empty_line);
    table_.back().resize(col_num_ + 1);
    table_.back().front().str_ = name;
    row_num_++;
    return;
}

void gctl::dsv_io::add_row(int idx, std::string name)
{
    std::vector<table_cell> empty_line;
    if (idx <= 0)
    {
        table_.push_back(empty_line);
        table_.back().resize(col_num_ + 1);
        table_.back().front().str_ = name;
        row_num_++;
    }
    else
    {
        table_.insert(table_.begin() + idx, empty_line);
        table_[idx].resize(col_num_ + 1);
        table_[idx].front().str_ = name;
        row_num_++;
    }
    return;
}

void gctl::dsv_io::add_row(std::string id_name, std::string name)
{
    add_row(name_index(id_name), name);
    return;
}

void gctl::dsv_io::filt_column(std::string cnd_str, std::string cnd_col, 
	const std::vector<std::string> &out_col, dsv_io &out_table)
{
    int idx = name_index(cnd_col);
    if (idx < 0) throw std::runtime_error("[gctl::dsv_io::] Invalid column index or name.");

    array<int> odx;
    bool out_row = false;
    if (out_col.empty()) out_row = true;
    else
    {
        odx.resize(out_col.size());
        for (size_t i = 0; i < out_col.size(); i++)
        {
            odx[i] = name_index(out_col[i]);
            if (odx[i] < 0) throw std::runtime_error("[gctl::dsv_io::] Invalid column index or name.");
        }
    }

    std::smatch ret;
    std::regex pat(cnd_str);
    std::vector<std::string> str_line, row_names;
    std::vector<std::vector<std::string> > str_table; 

    for (size_t i = 1; i <= row_num_; i++)
    {
        if (regex_search(table_[i][idx].str_, ret, pat))
        {
            if (out_row)
            {
                str_line.clear();

                for (size_t j = 1; j <= col_num_; j++)
                {
                    str_line.push_back(table_[i][j].str_);
                }
                
                str_table.push_back(str_line);
            }
            else
            {
                str_line.clear();
                str_line.push_back(table_[i][idx].str_);
                
                for (size_t j = 0; j < odx.size(); j++)
                {
                    str_line.push_back(table_[i][odx[j]].str_);
                }
                
                str_table.push_back(str_line);
            }

            row_names.push_back(table_[i][0].str_);
        }
    }

    out_table.init_table(str_table);

    std::vector<std::string> io_col;
    if (out_row)
    {
        get_column_names(io_col);
        out_table.cell(table_[0][0].str_, 0, 0);
    }
    else
    {
        io_col.push_back(cnd_col);

        for (size_t j = 0; j < odx.size(); j++)
        {
            io_col.push_back(out_col[j]);
        }
    }

    out_table.set_column_names(io_col);
    out_table.set_row_names(row_names, table_[0][0].str_);

    destroy_vector(row_names);
    destroy_vector(io_col);
    destroy_vector(str_line);
    destroy_vector(str_table);
    return;
}

void gctl::dsv_io::filt_column(rowbool_func_t func, const std::vector<std::string> &out_col, dsv_io &out_table)
{
    array<int> odx;
    bool out_row = false;
    if (out_col.empty()) out_row = true;
    else
    {
        odx.resize(out_col.size());
        for (size_t i = 0; i < out_col.size(); i++)
        {
            odx[i] = name_index(out_col[i]);
            if (odx[i] < 0) throw std::runtime_error("[gctl::dsv_io::] Invalid column index or name.");
        }
    }

    std::vector<std::string> str_line, row_names;
    std::vector<std::vector<std::string> > str_table; 

    for (size_t i = 1; i <= row_num_; i++)
    {
        if (func(table_[i]))
        {
            if (out_row)
            {
                str_line.clear();

                for (size_t j = 1; j <= col_num_; j++)
                {
                    str_line.push_back(table_[i][j].str_);
                }
                
                str_table.push_back(str_line);
            }
            else
            {
                str_line.clear();
                
                for (size_t j = 0; j < odx.size(); j++)
                {
                    str_line.push_back(table_[i][odx[j]].str_);
                }
                
                str_table.push_back(str_line);
            }

            row_names.push_back(table_[i][0].str_);
        }
    }

    out_table.init_table(str_table);

    std::vector<std::string> io_col;
    if (out_row)
    {
        get_column_names(io_col);
        out_table.cell(table_[0][0].str_, 0, 0);
    }
    else
    {
        for (size_t j = 0; j < odx.size(); j++)
        {
            io_col.push_back(out_col[j]);
        }
    }

    out_table.set_column_names(io_col);
    out_table.set_row_names(row_names, table_[0][0].str_);

    destroy_vector(row_names);
    destroy_vector(io_col);
    destroy_vector(str_line);
    destroy_vector(str_table);
    return;
}

#ifdef GCTL_EXPRTK

void gctl::dsv_io::cal_column(std::string expr_str, const std::vector<std::string> &col_list, int p)
{
    array<int> idx(col_list.size());
    for (size_t i = 0; i < col_list.size(); i++)
    {
        idx[i] = name_index(col_list[i]);

        if (idx[i] < 0) throw std::runtime_error("[gctl::dsv_io::] Invalid column index or name.");

        if (table_[0][idx[i]].type_ != Int && table_[0][idx[i]].type_ != Float)
        {
            throw std::runtime_error("[gctl::dsv_io] Invalid column type for numerical calculating.");
        }
    }
    
    exprtk::symbol_table<double> symbol_table;
	array<double> var(col_list.size());

    for (size_t i = 0; i < var.size(); i++)
	{
		symbol_table.add_variable(col_list[i], var[i]);
	}

    exprtk::expression<double> expression;
	expression.register_symbol_table(symbol_table);

	exprtk::parser<double> parser;
	if (!parser.compile(expr_str, expression))
    {
        throw std::runtime_error("[gctl::dsv_io] Fail to compile the math expression.");
    }

    for (size_t i = 1; i <= row_num_; i++)
    {
        // Remeber we put the output in the first element
        for (size_t j = 1; j < var.size(); j++)
        {
            var[j] = table_[i][idx[j]].value<double>();
        }

        var[0] = expression.value();
        table_[i][idx[0]].value(var[0], p);
    }
    return;
}

void gctl::dsv_io::filt_column(std::string cnd_str, const std::vector<std::string> &cnd_col, 
	const std::vector<std::string> &out_col, dsv_io& out_table)
{
    array<int> idx(cnd_col.size());
    for (size_t i = 0; i < cnd_col.size(); i++)
    {
        idx[i] = name_index(cnd_col[i]);

        if (idx[i] < 0) throw std::runtime_error("[gctl::dsv_io::] Invalid column index or name.");

        if (table_[0][idx[i]].type_ != Int && table_[0][idx[i]].type_ != Float)
        {
            throw std::runtime_error("[gctl::dsv_io] Invalid column type for numerical calculating.");
        }
    }

    array<int> odx;
    bool out_row = false;
    if (out_col.empty()) out_row = true;
    else
    {
        odx.resize(out_col.size());
        for (size_t i = 0; i < out_col.size(); i++)
        {
            odx[i] = name_index(out_col[i]);
            if (odx[i] < 0) throw std::runtime_error("[gctl::dsv_io::] Invalid column index or name.");
        }
    }
    
    exprtk::symbol_table<double> symbol_table;
	array<double> var(cnd_col.size());

    for (size_t i = 0; i < var.size(); i++)
	{
        symbol_table.add_variable(cnd_col[i], var[i]);
	}

    exprtk::expression<double> expression;
	expression.register_symbol_table(symbol_table);

	exprtk::parser<double> parser;
	if (!parser.compile(cnd_str, expression))
    {
        throw std::runtime_error("[gctl::dsv_io] Fail to compile the math expression.");
    }   

    std::vector<std::string> str_line, row_names;
    std::vector<std::vector<std::string> > str_table; 

    for (size_t i = 1; i <= row_num_; i++)
    {
        for (size_t j = 0; j < var.size(); j++)
        {
            var[j] = table_[i][idx[j]].value<double>();
        }

        if (expression.value() > 0.5) // return 1 if matched or 0 if dismatched
        {
            if (out_row)
            {
                str_line.clear();

                for (size_t j = 1; j <= col_num_; j++)
                {
                    str_line.push_back(table_[i][j].str_);
                }
                
                str_table.push_back(str_line);
            }
            else
            {
                str_line.clear();
                for (size_t j = 0; j < idx.size(); j++)
                {
                    str_line.push_back(table_[i][idx[j]].str_);
                }
                
                for (size_t j = 0; j < odx.size(); j++)
                {
                    str_line.push_back(table_[i][odx[j]].str_);
                }
                
                str_table.push_back(str_line);
            }

            row_names.push_back(table_[i][0].str_);
        }
    }

    out_table.init_table(str_table);

    std::vector<std::string> io_col;
    if (out_row)
    {
        get_column_names(io_col);
        out_table.cell(table_[0][0].str_, 0, 0);
    }
    else
    {
        for (size_t j = 0; j < idx.size(); j++)
        {
            io_col.push_back(cnd_col[j]);
        }

        for (size_t j = 0; j < odx.size(); j++)
        {
            io_col.push_back(out_col[j]);
        }
    }

    out_table.set_column_names(io_col);
    out_table.set_row_names(row_names, table_[0][0].str_);

    destroy_vector(row_names);
    destroy_vector(io_col);
    destroy_vector(str_line);
    destroy_vector(str_table);
    return;
}

#endif // GCTL_EXPRTK

gctl::geodsv_io::geodsv_io(){}

gctl::geodsv_io::~geodsv_io(){}

gctl::geodsv_io::geodsv_io(std::string filename, std::string file_exten, table_headtype_e t)
{
    file_ = "";
    att_sym_ = '#';
	tag_sym_ = '!';
	deli_sym_ = ' ';
	head_num_ = 0;
    row_num_ = 0;
    col_num_ = 0;

    if (file_exten == ".csv") load_csv(filename, t);
    else load_text(filename, file_exten, t);
}

void gctl::geodsv_io::fill_column_point2dc(const array<point2dc> &data, int xid, int yid, int p)
{
    if (xid > col_num_ || yid > col_num_ || xid == yid || xid <= 0 || yid <= 0)
    {
        throw std::runtime_error("[gctl::geodsv_io] Invalid column index.");
    }

    std::stringstream ss;
    std::string s;
    for (size_t i = 1; i <= std::min(row_num_, (int) data.size()); i++)
    {
        table_[i][xid].value(data[i - 1].x, p);
        table_[i][yid].value(data[i - 1].y, p);
    }
    return;
}

void gctl::geodsv_io::fill_column_point2dc(const array<point2dc> &data, std::string xname, std::string yname, int p)
{
    fill_column_point2dc(data, name_index(xname, false), name_index(yname, false), p);
    return;
}

void gctl::geodsv_io::fill_column_point3dc(const array<point3dc> &data, int xid, int yid, int zid, int p)
{
    if (xid > col_num_ || yid > col_num_ || zid > col_num_ || xid == yid || yid == zid || xid == zid
        || xid <= 0 || yid <= 0 || zid <= 0)
    {
        throw std::runtime_error("[gctl::geodsv_io] Invalid column index.");
    }

    std::stringstream ss;
    std::string s;
    for (size_t i = 1; i <= std::min(row_num_, (int) data.size()); i++)
    {
        table_[i][xid].value(data[i - 1].x, p);
        table_[i][yid].value(data[i - 1].y, p);
        table_[i][zid].value(data[i - 1].z, p);
    }
    return;
}

void gctl::geodsv_io::fill_column_point3dc(const array<point3dc> &data, std::string xname, std::string yname, std::string zname, int p)
{
    fill_column_point3dc(data, name_index(xname, false), name_index(yname, false), name_index(zname, false), p);
    return;
}

void gctl::geodsv_io::fill_column_point3ds(const array<point3ds> &data, int rid, int pid, int tid, int p)
{
    if (rid > col_num_ || pid > col_num_ || tid > col_num_ || rid == pid || pid == tid || tid == rid
        || rid <= 0 || pid <= 0 || tid <= 0)
    {
        throw std::runtime_error("[gctl::geodsv_io] Invalid column index.");
    }

    std::stringstream ss;
    std::string s;
    for (size_t i = 1; i <= std::min(row_num_, (int) data.size()); i++)
    {
        table_[i][rid].value(data[i - 1].rad, p);
        table_[i][pid].value(data[i - 1].lon, p);
        table_[i][tid].value(data[i - 1].lat, p);
    }
    return;
}

void gctl::geodsv_io::fill_column_point3ds(const array<point3ds> &data, std::string rname, std::string pname, std::string tname, int p)
{
    fill_column_point3ds(data, name_index(rname, false), name_index(pname, false), name_index(tname, false), p);
    return;
}

void gctl::geodsv_io::get_column_point2dc(array<point2dc> &data, int xid, int yid)
{
    if (xid > col_num_ || yid > col_num_ || xid == yid || xid <= 0 || yid <= 0)
    {
        throw std::runtime_error("[gctl::geodsv_io] Invalid column index.");
    }

    data.resize(row_num_);
    for (size_t i = 1; i <= row_num_; i++)
    {
        data[i - 1].x = table_[i][xid].value<double>();
        data[i - 1].y = table_[i][yid].value<double>();
    }
    return;
}

void gctl::geodsv_io::get_column_point2dc(array<point2dc> &data, std::string xname, std::string yname)
{
    get_column_point2dc(data, name_index(xname, false), name_index(yname, false));
    return;
}

void gctl::geodsv_io::get_column_point3dc(array<point3dc> &data, int xid, int yid, int zid)
{
    if (xid > col_num_ || yid > col_num_ || zid > col_num_ || xid == yid || yid == zid || xid == zid
        || xid <= 0 || yid <= 0 || zid <= 0)
    {
        throw std::runtime_error("[gctl::geodsv_io] Invalid column index.");
    }

    data.resize(row_num_);
    for (size_t i = 1; i <= row_num_; i++)
    {
        data[i - 1].x = table_[i][xid].value<double>();
        data[i - 1].y = table_[i][yid].value<double>();
        data[i - 1].z = table_[i][zid].value<double>();
    }
    return;
}

void gctl::geodsv_io::get_column_point3dc(array<point3dc> &data, std::string xname, std::string yname, std::string zname)
{
    get_column_point3dc(data, name_index(xname, false), name_index(yname, false), name_index(zname, false));
    return;
}

void gctl::geodsv_io::get_column_point3ds(array<point3ds> &data, int rid, int pid, int tid)
{
    if (rid > col_num_ || pid > col_num_ || tid > col_num_ || rid == pid || pid == tid || tid == rid
        || rid <= 0 || pid <= 0 || tid <= 0)
    {
        throw std::runtime_error("[gctl::geodsv_io] Invalid column index.");
    }

    data.resize(row_num_);
    for (size_t i = 1; i <= row_num_; i++)
    {
        data[i - 1].rad = table_[i][rid].value<double>();
        data[i - 1].lon = table_[i][pid].value<double>();
        data[i - 1].lat = table_[i][tid].value<double>();
    }
    return;
}

void gctl::geodsv_io::get_column_point3ds(array<point3ds> &data, std::string rname, std::string pname, std::string tname)
{
    get_column_point3ds(data, name_index(rname, false), name_index(pname, false), name_index(tname, false));
    return;
}