/********************************************************************************
 * Copyright (c) 2025-2026 ZF Friedrichshafen AG
 *
 * This program and the accompanying materials are made available under the
 * terms of the Apache License Version 2.0 which is available at
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Contributors:
 *   Thomas Pfleiderer - initial API and implementation
 ********************************************************************************/

#include "csv_file_reader.h"

CCSVFileReader::CCSVFileReader(const std::string& ssFileName, const bool bSilent, const bool bVerbose)
{
    m_silent = bSilent;
    m_verbose = bVerbose;
    m_vdSignals.clear();
    m_bsSignals.clear();
    if (ssFileName.empty())
    {
        Help();
        return;
    }
    else if (!ReadCSVVSSFile(ssFileName))
    {
        std::cout << "\nError: '" << ssFileName.c_str() << "' file could not be oppened or has no vss definitions." << std::endl;
        Help();
        return;
    }
}

void CCSVFileReader::Help()
{
    std::cout << std::endl;
    std::cout << "-----------------------------------------------------------------------------------------------------------" << std::endl;
    std::cout << "Required: An excel text file in csv format, the separator should be ';'" << std::endl;
    std::cout << "    [required] Cells must not contain spaces." << std::endl;
    std::cout << "    [required] Column 'Device Type' must be either 'VD' or 'BS'." << std::endl;
    std::cout << "    [required] Column 'vss' must contain a character '.' or can be empty." << std::endl;
    std::cout << "    [required] Column signal direction, either 'RX' or 'TX' or empty." << std::endl;
    std::cout << "    [required] Column signal value type must not be empty." << std::endl;
    std::cout << "    [required] Column 'CAN name' must contain a character '.' (for vehicle device signal)." << std::endl;
    std::cout << "    [required] Last column: in case of vehicle device it is 'CAN name'." << std::endl;
    std::cout << "    [required] Last column: in case of basic service it is avss from vehicle device." << std::endl;
    std::cout << "    [optional] additonal column: c++ code to recalulate the signal value in the vehicle device." << std::endl;
    std::cout << "    [recommended] Cells may be empty. In that case either a default string is created or previous entry is used." << std::endl;
    std::cout << "    [recommended] Column Class name should start with uppercase character." << std::endl;
    std::cout << "    [recommended] Column Function name should start with uppercase character." << std::endl;
    std::cout << "    [recommended] Column Signal name should start with lowercase character." << std::endl;
    std::cout << "Summary can be found in file 'summary.txt'." << std::endl;
    std::cout << "Columns:" << std::endl;
    std::cout << "Device Type ';' Class name         ';' Function name   ';' Signal name  ';' Interface (vss)                             ';' Direction ';' Signal value type ';' 'CAN name' or Interface(vss); Formula (is optional, normally empty)" << std::endl;
    std::cout << std::endl;
    std::cout << "e.g." << std::endl;
    std::cout << "VD          ';' FrontWheel         ';' SetAverageAngle ';' averageAngle ';' Vehicle.Chassis.Axle.Row.Wheel.AverageAngle ';' RX        ';' float              ';' CANMsg.Angle" << std::endl;
    std::cout << "BS          ';' VehicleChassisAxle ';' SetAngleStatus  ';' angleStatus  ';' Vehicle.Chassis.Axle.Row.Wheel              ';' TX        ';' uint8_t            ';' Vehicle.Chassis.Axle.Row.Wheel" << std::endl;
    std::cout << "-----------------------------------------------------------------------------------------------------------" << std::endl;
}

bool CCSVFileReader::ReadCSVVSSFile(const std::string& ssFileName)
{
    std::ifstream file(ssFileName);
    if (!file.is_open()) 
    {
        std::cerr << "'" << ssFileName << "' could not be opened." << std::endl;
        return false;
    }
    std::string line;
    uint32_t index = 1;
    while (std::getline(file, line))
    {
        line.erase(std::remove(line.begin(), line.end(), '\r'), line.end());
        ReadLine(line, index++, vssVDColumns::column_end);
    }
    file.close();

    if ((m_vdSignals.size() == 0) && (m_bsSignals.size() == 0))
    {
        if (!m_silent)
        {
            std::cout << "No valid signal definitions found." << std::endl;
        }
    }
    return true;
}

void CCSVFileReader::ReadLine(const std::string& ssLine, const uint32_t index, size_t endColumn)
{
    std::stringstream ss(ssLine);
    std::string item;
    std::vector<std::string> parts;
    while (std::getline(ss, item, ';'))
    {
        item = Trim(item);
        auto cType = GetCTypeFromIDLType(item); // maybe its a IDL type

        if ((parts.size() == (vssVDColumns::column_vdFormula)) && !item.empty())
        {
            // we have the formula column, if not empty, collect everything
            auto collectItem = item;
            while (std::getline(ss, item, ';'))
            {
                collectItem.append(";");
                collectItem.append(item);
            }

            // Remove " from front and end
            if (!collectItem.empty() && collectItem.front() == '"' && collectItem.back() == '"') {
                collectItem = collectItem.substr(1, collectItem.size() - 2);
            }

            // Replace all occurrences of "" with "
            collectItem = std::regex_replace(collectItem, std::regex("\"\""), "\"");

            parts.push_back(collectItem);
        }
        else if (MustNotContainSpaces(item) || (cType.size() != 0))
        {
            parts.push_back(item);
        }
        else
        {
            if ((m_vdSignals.size() != 0) && (m_bsSignals.size() && !m_silent))
            {
                std::cout << "Invalid, contains spaces: '" << item << "'" << std::endl;
            }
        }
    }
    if (parts.size() == (endColumn - 1))
    {
        parts.push_back("");
    }
    if (parts.size() >= endColumn)
    {
        ParseColumns(parts, index);
    }
}

    
void CCSVFileReader::ParseColumns(const std::vector<std::string>& parts, const uint32_t index)
{
    static std::string vss = "";
    static sdv::core::ESignalDirection signalDirection = sdv::core::ESignalDirection::sigdir_rx;

    if (parts.size() >= vssVDColumns::column_end)
    {
        if (parts[vssVDColumns::column_vssDefinition].size() != 0)  // if vss is empty use previous one
        {
            vss = parts[vssVDColumns::column_vssDefinition];
        }
        if (!MustContainDotOrIsEmpty(vss, false)) // vss can be empty
        {
            if ((m_vdSignals.size() != 0) && (m_bsSignals.size() && !m_silent))
            {
                std::cout << "Invalid, contains no charatcer '.': '" << vss << "', line number " << std::to_string(index)<< std::endl;
            }
            return;
        }
        vss = ValidateVSSFormatNumbers(vss);

        if (!parts[vssVDColumns::column_direction].compare("RX"))
            signalDirection = sdv::core::ESignalDirection::sigdir_rx;
        if (!parts[vssVDColumns::column_direction].compare("TX"))
            signalDirection = sdv::core::ESignalDirection::sigdir_tx;

        if (!parts[vssVDColumns::column_type].compare("VD"))
        {
            if (AddToExistingVDSignal(m_vdSignals, vss, signalDirection, parts, index))
            {
                return;
            }

            SSignalVDDefinition signal;
            signal.vssDefinition = vss;
            signal.signalDirection = signalDirection;
            signal.className = GenerateDefaultIfEmpty(parts[vssVDColumns::column_className], "ClassName", index);

            if (AddFunctionVDDefinition(signal, parts[vssVDColumns::column_functionName], parts[vssVDColumns::column_signalName],
                parts[vssVDColumns::column_canSignalName], parts[vssVDColumns::column_signalCType], parts[vssVDColumns::column_vdFormula], index))
            {
                ValidateVDCodeStyle(signal, m_verbose);
                m_vdSignals.push_back(signal);
            }
        }
        else
        {
            if (AddToExistingBSSignal(m_bsSignals, vss, signalDirection, parts, index))
            {
                return;
            }

            std::string vssOfVehicleDevice = parts[vssVDColumns::column_vdDefinition];
            vssOfVehicleDevice = ValidateVSSFormatNumbers(vssOfVehicleDevice);

            SSignalBSDefinition signal;
            signal.vssDefinition = vss;
            signal.signalDirection = signalDirection;
            signal.className = GenerateDefaultIfEmpty(parts[vssVDColumns::column_className], "ClassName", index);
            signal.vssVDDefinition = vssOfVehicleDevice;
            if (AddFunctionBSDefinition(signal, parts[vssVDColumns::column_functionName], parts[vssVDColumns::column_signalName],
                parts[vssVDColumns::column_canSignalName], parts[vssVDColumns::column_signalCType], index))
            {
                ValidateBSCodeStyle(signal, m_verbose);
                m_bsSignals.push_back(signal);
            }
        }
    }
}

bool CCSVFileReader::AddToExistingVDSignal(std::vector <SSignalVDDefinition>& signals, const std::string& vssDefinition, 
    const sdv::core::ESignalDirection& directionX, const std::vector<std::string>& parts, const uint32_t index)
{
    for (auto& signal : signals)
    {
        if ((signal.vssDefinition.compare(vssDefinition) == 0) && (signal.signalDirection == directionX))
        {
            if ((signal.className != parts[vssVDColumns::column_className]) && !m_silent)
            {
                std::cout << "Signal already found, ignore class name '" << parts[vssVDColumns::column_className] << "', line number " << std::to_string(index) << std::endl;
            }


            AddFunctionVDDefinition(signal, parts[vssVDColumns::column_functionName], parts[vssVDColumns::column_signalName],
                parts[vssVDColumns::column_canSignalName], parts[vssVDColumns::column_signalCType], parts[vssVDColumns::column_vdFormula], index);
            return true;
        }
    }

    return false;
}

bool CCSVFileReader::AddToExistingBSSignal(std::vector <SSignalBSDefinition>& signals, const std::string& vssDefinition, 
    const sdv::core::ESignalDirection& directionX, const std::vector<std::string>& parts, const uint32_t index)
{
    for (auto& signal : signals)
    {
        if ((signal.vssDefinition.compare(vssDefinition) == 0) && (signal.signalDirection == directionX))
        {
            if ((signal.className != parts[vssVDColumns::column_className]) && !m_silent)
            {
                std::cout << "Signal already found, ignore class name '" << parts[vssVDColumns::column_className] << "', line number " << std::to_string(index) << std::endl;
            }

            AddFunctionBSDefinition(signal, parts[vssVDColumns::column_functionName], parts[vssVDColumns::column_signalName],
                parts[vssVDColumns::column_vdDefinition], parts[vssVDColumns::column_signalCType], index);
            return true;
        }
    }

    return false;
}

bool CCSVFileReader::AddFunctionVDDefinition(SSignalVDDefinition& signal, const std::string& functionName, const std::string& signalName, 
    const std::string& canSignalName, const std::string& idlType, const std::string& formula, const uint32_t index)
{
    if (!MustContainDotOrIsEmpty(canSignalName, true)) // can signal must must not be empty
    {
        if ((m_vdSignals.size() != 0) && (m_bsSignals.size() && !m_silent))
        {
            std::cout << "Invalid, contains no charatcer '.' or is empty: '" << canSignalName << "', line number " << std::to_string(index) << std::endl;
        }
        return false;
    }

    auto cType = GetCTypeFromIDLType(idlType);
    if ((cType.size() == 0) && !m_silent)
    {
        std::cout << "Invalid, idl type unknown: '" << idlType << "', line number " << std::to_string(index) << std::endl;
        return false;
    }

    SFunctionVDDefinition func;
    func.functionName = GenerateDefaultIfEmpty(functionName, "FunctionName", index);
    func.signalName = GenerateDefaultIfEmpty(signalName, "variableName", index);
    func.canSignalName = canSignalName;
    func.idlType = idlType;
    func.formula = formula;

    ValidateVDCodeStyle(func, m_verbose);
    signal.vecFunctions.push_back(func);

    return true;
}
bool CCSVFileReader::AddFunctionBSDefinition(SSignalBSDefinition& signal, const std::string& functionName, const std::string& signalName,
    const std::string& canSignalName, const std::string& idlType, const uint32_t index)
{
    if (!MustContainDotOrIsEmpty(canSignalName, true)) // can signal must must not be empty
    {
        if ((m_vdSignals.size() != 0) && (m_bsSignals.size() && !m_silent))
        {
            std::cout << "Invalid, contains no charatcer '.' or is empty: '" << canSignalName << "', line number " << std::to_string(index) << std::endl;
        }
        return false;
    }

    auto cType = GetCTypeFromIDLType(idlType);
    if ((cType.size() == 0) && !m_silent)
    {
        std::cout << "Invalid, idl type unknown: '" << idlType << "', line number " << std::to_string(index) << std::endl;
        return false;
    }

    SFunctionBSDefinition func;
    func.functionName = GenerateDefaultIfEmpty(functionName, "FunctionName", index);
    func.signalName = GenerateDefaultIfEmpty(signalName, "variableName", index);
    func.idlType = idlType;

    ValidateBSCodeStyle(func, m_verbose);
    signal.vecFunctions.push_back(func);

    return true;
}
