/********************************************************************************
 * 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:
 *   Erik Verhoeven - initial API and implementation
 ********************************************************************************/

#include "ascwriter.h"
#include <fstream>

namespace asc
{
    CAscWriter::CAscWriter()
    {
        StartTimer();
    }

    void CAscWriter::AddSample(const SCanMessage& rsSample)
    {
        SCanMessage sSampleCopy(rsSample);
        if (sSampleCopy.dTimestamp == 0.0000000)
            sSampleCopy.dTimestamp =
            std::chrono::duration<double>(std::chrono::high_resolution_clock::now() - m_timepointStart).count();
        else
        {
            if (!m_lstMessages.empty() && m_lstMessages.back().dTimestamp > sSampleCopy.dTimestamp) return;
        }
        m_lstMessages.push_back(std::move(sSampleCopy));
    }

    bool CAscWriter::HasSamples() const
    {
        return !m_lstMessages.empty();
    }

    void CAscWriter::StartTimer()
    {
        if (m_lstMessages.empty())
        {
            m_timepointStart = std::chrono::high_resolution_clock::now();
            m_timepointSystem = std::chrono::system_clock::now();
        }
    }

    void CAscWriter::Clear()
    {
        m_lstMessages.clear();
        m_timepointStart = std::chrono::high_resolution_clock::now();
    }

    bool CAscWriter::Write(const std::filesystem::path& rpathFile)
    {
        // Open and write the file
        std::ofstream fstream(rpathFile, std::ios::out | std::ios::trunc);
        if (!fstream.is_open()) return false;

        // Fill in header
        time_t tmSystemTime = std::chrono::system_clock::to_time_t(m_timepointSystem);
        fstream << "date " << std::put_time(std::localtime(&tmSystemTime), "%c") << std::endl;
        fstream << "base hex  timestamps absolute" << std::endl;

        // Stream trigger block
        fstream << "Begin TriggerBlock " << std::put_time(std::localtime(&tmSystemTime), "%c") << std::endl;
        fstream << "   0.000000 Start of measurement" << std::endl;
        for (const SCanMessage& rsSample : m_lstMessages)
        {
            std::stringstream sstreamTimestamp;
            sstreamTimestamp << std::fixed << std::setprecision(6) << rsSample.dTimestamp;
            fstream << std::string(11ull - sstreamTimestamp.str().length(), ' ') << sstreamTimestamp.str();
            if (rsSample.bCanFd)
            {
                fstream << " " << "CANFD" << " " << rsSample.uiChannel << " " <<
                    (rsSample.eDirection == SCanMessage::EDirection::rx ? "Rx" : "Tx") <<
                    std::hex << std::uppercase << rsSample.uiId << (rsSample.bExtended ? "x" : "") << " 1 0 ";
                const size_t rgnDLCLength[16] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64 };
                size_t nDLC = 0;
                for (size_t n = 0; n < 16; n++)
                    if (rgnDLCLength[n] == rsSample.uiLength) nDLC = n;
                fstream << nDLC << " " << rsSample.uiLength;
            }
            else // Normal CAN
            {
                fstream << " " << rsSample.uiChannel;
                std::stringstream sstreamID;
                sstreamID << std::hex << std::uppercase << rsSample.uiId;
                fstream << "  " << sstreamID.str() << (rsSample.bExtended ? "x" : "");
                fstream << std::string(16ull - sstreamID.str().length(), ' ') <<
                    (rsSample.eDirection == SCanMessage::EDirection::rx ? "Rx" : "Tx");
                fstream << "   " << "d";
                fstream << " " << rsSample.uiLength;
            }
            for (uint32_t uiIndex = 0; uiIndex < rsSample.uiLength; uiIndex++)
                fstream << " " << std::hex << std::setw(2) << std::setfill('0') << std::uppercase << static_cast<size_t>(rsSample.rguiData[uiIndex]);
            fstream << std::endl;
        }
        fstream << "End TriggerBlock" << std::endl;

        // Done
        fstream.close();

        return true;
    }
} // namespace asc