//
//  TsvParser.cpp
//  eagle0
//
//  Created by Dan Crosby on 4/7/18.
//  Copyright © 2018 none. All rights reserved.
//

#include "TsvParser.hpp"

#include <sstream>

using std::string;
using std::stringstream;
using std::unordered_map;
using std::vector;

static vector<string> ParseLine(string line);

vector<string> ParseLine(string line) {
    vector<string> values;

    while (isspace(line.back())) { line = string(line.begin(), line.end() - 1); }
    stringstream linestr(line);
    while (!linestr.eof()) {
        string value;
        std::getline(linestr, value, '\t');
        values.push_back(value);
    }

    return values;
}

vector<StringMap> TsvParser::ParseTsv(string tsv, bool allowUneven) {
    vector<StringMap> maps;
    stringstream str(tsv);

    string keysLine;
    std::getline(str, keysLine, '\n');
    vector<string> keys = ParseLine(keysLine);

    while (!str.eof()) {
        string valuesLine;
        std::getline(str, valuesLine, '\n');

        if (valuesLine.size() > 1 && valuesLine[0] == '/' && valuesLine[1] == '/') {
            // comment line; skip
            continue;
        }

        vector<string> values = ParseLine(valuesLine);

        if (values.size() > keys.size()) {
            throw std::out_of_range("More values in this line than keys: " + valuesLine);
        }
        if (!allowUneven && keys.size() > values.size()) {
            throw std::out_of_range("Not enough values in this line: " + valuesLine);
        }

        unordered_map<string, string> newLineMap;
        for (size_t i = 0; i < values.size(); i++) { newLineMap[keys[i]] = values[i]; }

        maps.push_back(newLineMap);
    }

    return maps;
}

vector<StringMap> TsvParser::ParseColumnEntryTsv(string tsv) {
    vector<StringMap> maps;
    stringstream str(tsv);

    while (!str.eof()) {
        string mapEntryLine;
        std::getline(str, mapEntryLine, '\n');
        if (mapEntryLine.empty()) continue;
        vector<string> keyAndValues = ParseLine(mapEntryLine);

        string key = keyAndValues[0];
        vector<string> values = vector<string>(keyAndValues.begin() + 1, keyAndValues.end());

        if (values.size() > maps.size()) { maps.resize(keyAndValues.size() - 1); }
        if (values.size() < maps.size()) {
            throw std::out_of_range("Too few values in this line: " + mapEntryLine);
        }

        for (size_t i = 0; i < values.size(); i++) { maps[i][key] = values[i]; }
    }

    return maps;
}
