/*
    SPDX-FileCopyrightText: 2010-2015 Allan Anderson <agander93@gmail.com>
    SPDX-License-Identifier: GPL-2.0-or-later
*/

#include "csvutil.h"
//#include <QStringList>
//#include <QVector>
#include <QRegularExpression>
#include <QLocale>

Parse::Parse() :
    m_lastLine(0),
    m_symbolFound(false),
    m_invalidConversion(false)
{
    m_fieldDelimiters = {QLatin1Char(','), QLatin1Char(';'), QLatin1Char(':'), QLatin1Char('\t')};
    m_textDelimiters = {QLatin1Char('"'), QLatin1Char('\'')};
    m_decimalSymbols = {QLatin1Char('.'), QLatin1Char(',')};
    m_thousandsSeparators = {QLatin1Char(','), QLatin1Char('.')};

    setFieldDelimiter(FieldDelimiter::Comma);
    setTextDelimiter(TextDelimiter::DoubleQuote);
    setDecimalSymbol(DecimalSymbol::Dot);
}

Parse::~Parse()
{
}

QStringList Parse::parseLine(const QString& data)
{
    QStringList listOut;
    const QStringList listIn = data.split(m_fieldDelimiter);  // firstly, split on m_fieldDelimiterCharacter
    QString cell;
    foreach (const auto it, listIn) {
        cell.append(it);
        // detect where a "quoted" string has been erroneously split, because of a comma,
        // or in a value, a 'thousand separator' being mistaken for a field delimiter.
        //Also, where a 'field separator' is within quotes and the quotes don't include the whole of the field.
        if (cell.startsWith(m_textDelimiter)) {
            if (!cell.endsWith(m_textDelimiter)) {
                cell.append(m_fieldDelimiter);
                continue;
            }
            cell.remove(m_textDelimiter);
        }
        listOut.append(cell.trimmed());
        cell.clear();
    }
    return listOut;
}

QStringList Parse::parseFile(const QString &buf)
{
    int lineCount = 0;
    bool inQuotes = false;
    QString line;
    QStringList lines;

    foreach (const auto chr, buf) {
        if (chr == m_textDelimiter) {
            line += chr;
            inQuotes = !inQuotes;
            continue;
        } else if (chr == QLatin1Char('\r') || chr == QLatin1Char('\n')) {
            if (inQuotes) {
                line += QLatin1Char('~');
                continue;
            }
            if (line.isEmpty())
                continue;
            ++lineCount;
            lines += line;
            line.clear();
        } else {
            line += chr;
            continue;
        }
    }
    // in case the buffer does not end with a CR or LF we
    // end up here and add the line nevertheless
    if (!line.isEmpty()) {
        ++lineCount;
        lines.append(line);
    }
    m_lastLine = lineCount;
    return lines;
}

void Parse::setFieldDelimiter(const FieldDelimiter _d)
{
    if (_d == FieldDelimiter::Auto)
        return;
    m_fieldDelimiter = m_fieldDelimiters.at((int)_d);
}

void Parse::setTextDelimiter(const TextDelimiter _d)
{
    m_textDelimiter = m_textDelimiters.at((int)_d);
}

void Parse::setDecimalSymbol(const DecimalSymbol _d)
{
    if (_d == DecimalSymbol::Auto)
        return;
    m_decimalSymbol = m_decimalSymbols.at((int)_d);
    if (_d == DecimalSymbol::Comma)
        m_thousandsSeparator = m_thousandsSeparators.at((int)ThousandSeparator::Dot);
    else
        m_thousandsSeparator = m_thousandsSeparators.at((int)ThousandSeparator::Comma);
}

QChar Parse::decimalSymbol(const DecimalSymbol _d)
{
    if (_d == DecimalSymbol::Auto)
        return QChar();
    return m_decimalSymbols.at((int)_d);
}

int Parse::lastLine()
{
    return m_lastLine;
}

QString Parse::possiblyReplaceSymbol(const QString&  str)
{
    // examples given if decimal symbol is '.' and thousand symbol is ','
    m_symbolFound = false;
    m_invalidConversion = true;

    QString txt = str.trimmed();
    if (txt.isEmpty())  // empty strings not allowed
        return txt;

    bool parentheses = false;
    if (txt.contains(QLatin1Char('('))) // (1.23) is in fact -1.23
        parentheses = true;

    int length = txt.length();
    int decimalIndex = txt.indexOf(m_decimalSymbol);
    int thouIndex = txt.lastIndexOf(m_thousandsSeparator);

    txt.remove(QRegularExpression(QStringLiteral("\\D.,-+")));     // remove all non-digits
    txt.remove(m_thousandsSeparator);

    if (txt.isEmpty())  // empty strings not allowed
        return txt;

    if (decimalIndex == -1) {                                         // e.g. 1 ; 1,234 ; 1,234,567; 12,
        if (thouIndex == -1 || thouIndex == length - 4)  {              // e.g. 1 ; 1,234 ; 1,234,567
            txt.append(QLocale().decimalPoint() + QLatin1String("00"));   // e.g. 1.00 ; 1234.00 ; 1234567.00
            m_invalidConversion = false;
        }
        return txt;
    }
    m_symbolFound = true;             // decimal symbol found

    if (decimalIndex < thouIndex)     // e.g. 1.234,567 ; 1.23,45
        return txt;

    m_invalidConversion = false;      // it cannot be true after this point
    txt.replace(m_decimalSymbol, QLocale().decimalPoint());  // so swap it

    if (decimalIndex == length - 1)   // e.g. 1. ; 123.
        txt.append(QLatin1String("00"));

    if (parentheses)
        txt.prepend(QLatin1Char('-'));

    return txt;
}

bool Parse::invalidConversion()
{
    return m_invalidConversion;
}

//--------------------------------------------------------------------------------------------------------------------------------
