 /********************************************************************************
 * 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 
 ********************************************************************************/

#include "control.h"

CExampleControl::~CExampleControl()
{
    Shutdown();
}

bool CExampleControl::Initialize()
{
    if (m_bInitialized) return true;

    // Set the SDV V-API framework directory.
    if (!m_pathFramework.empty())
        m_appcontrol.SetFrameworkRuntimeDirectory(m_pathFramework);

    // Start the framework
    std::stringstream sstreamAppConfig;
    sstreamAppConfig << "[Application]" << std::endl;
    sstreamAppConfig << "Mode=\"" << (RunAsStandaloneApp() ? "Standalone" : "External") << "\"" << std::endl;
    sstreamAppConfig << "Instance=" << m_uiInstance << std::endl;
    sstreamAppConfig << "Retries=" << 6 << std::endl;    
    sstreamAppConfig << "[Console]" << std::endl;
    sstreamAppConfig << "Report=";
    switch (m_eReporting)
    {
    case EAppControlReporting::normal:
        sstreamAppConfig << "\"Normal\"";
        break;
    case EAppControlReporting::verbose:
        sstreamAppConfig << "\"Verbose\"";
        break;
    default:
        sstreamAppConfig << "\"Silent\"";
        break;
    }
    sstreamAppConfig << std::endl;
    if (!m_appcontrol.Startup(sstreamAppConfig.str())) return false;

    // Local configurations only when running as standalone.
    if (RunAsStandaloneApp())
    {
        // Start in configuration mode.
        m_appcontrol.SetConfigMode();
        if (!m_appcontrol.AddConfigSearchDir("config"))
        {
            m_appcontrol.Shutdown();
            return false;
        }
    }

    m_bInitialized = true;
    return true;
}

void CExampleControl::Shutdown()
{
    if (!m_bInitialized)
        m_appcontrol.Shutdown();
    m_bInitialized = false;
}

bool CExampleControl::IsInitialized() const
{
    return m_bInitialized;
}

bool CExampleControl::LoadConfigFile(const std::string& inputMsg, const std::string& configFileName)
{
    if (!m_bInitialized && m_eRunAs == ERunAs::standalone) return false;

    std::string msg = inputMsg;
    if (m_appcontrol.LoadConfig(configFileName) == sdv::core::EConfigProcessResult::successful)
    {
        msg.append("ok\n");
        std::cout << msg.c_str();
        return true;
    }

    msg.append("FAILED.\n");
    std::cout << msg.c_str();
    return false;
}

void CExampleControl::StartTestRun()
{
    if (!m_bInitialized) return;

    // Switch to running mode.
    m_appcontrol.SetRunningMode();

    // Should the datalink be simulated?
    if (m_eRunAs != ERunAs::standalone_simulated) return;

    // Start the simulated datalink
    m_bRunning = true;
    m_threadSimulateDatalink = sdv::core::secure_thread(&CExampleControl::SimulateDatalinkThreadFunc, this);
}

void CExampleControl::StopTestRun()
{
    // Stop running and wait for any thread to finalize
    m_bRunning = false;
    if (m_threadSimulateDatalink.joinable())
        m_threadSimulateDatalink.join();
}

bool CExampleControl::HasCommandLineError() const
{
    return m_bCmdLnError;
}

bool CExampleControl::HasRequestedCommandLineHelp() const
{
    return m_bCmdLnHelp;
}

CExampleControl::ERunAs CExampleControl::GetAppOperation() const
{
    return m_eRunAs;
}

bool CExampleControl::IsSimulationMode() const
{
    return m_eRunAs == ERunAs::standalone_simulated;
}

bool CExampleControl::RunAsStandaloneApp() const
{
    return m_eRunAs == ERunAs::standalone || m_eRunAs == ERunAs::standalone_simulated;
}

bool CExampleControl::RunAsServerApp() const
{
    return m_eRunAs == ERunAs::server_connect;
}

bool CExampleControl::RegisterSignalsSimDatalink()
{
    if (!m_bInitialized) return false;
    if (m_eRunAs != ERunAs::standalone_simulated) return true;  // Nothing to do...

    std::string msg = "Register all signals: ";
    sdv::core::CDispatchService dispatch;

    m_signalSteeringWheel = dispatch.RegisterRxSignal(demo::dsWheelAngle);
    m_signalSpeed = dispatch.RegisterRxSignal(demo::dsVehicleSpeed);
    m_signalRearAngle = dispatch.RegisterTxSignal(demo::dsAxleAngle, 0);
    m_signalCounter = dispatch.RegisterTxSignal(demo::dsLiveCounter, 0);

    if (m_signalSteeringWheel && m_signalSpeed && m_signalRearAngle && m_signalCounter)
        std::cout << "Registration was successful\n";
    else
        std::cout << "ATTENTION! Registration failed\n";

    auto allSignals = dispatch.GetRegisteredSignals();
    msg.append("(number of signals == ");
    msg.append(std::to_string(allSignals.size()));
    msg.append(") ok\n");
    std::cout << msg.c_str();
    return true;
}

void CExampleControl::ResetSignalsSimDatalink()
{
    if (m_eRunAs != ERunAs::standalone_simulated) return;  // Nothing to do...

    if (m_signalSteeringWheel)
        m_signalSteeringWheel.Reset();
    if (m_signalSpeed)
        m_signalSpeed.Reset();
    if (m_signalRearAngle)
        m_signalRearAngle.Reset();
    if (m_signalCounter)
        m_signalCounter.Reset();
}

void CExampleControl::SimulateDatalinkThreadFunc()
{
    if (m_eRunAs != ERunAs::standalone_simulated) return;  // Nothing to do...

    // Send fSteeringWheelAngle wheel angel from -16 to 16 radians and vice versa
    // Send fVehicleSpeed from 0 to 12 m/s (43.2 km/h) and vice versa

    float fSteeringWheelAngle = 0.0f;
    float fVehicleSpeed = 0.0f;
    m_signalSpeed.Write(fVehicleSpeed);
    m_signalSteeringWheel.Write(fSteeringWheelAngle);

    float fDeltaSteering = 0.1f;
    float fDeltaSpeed = 0.1f;

    while (m_bRunning)
    {
        fSteeringWheelAngle += fDeltaSteering;
        if (fSteeringWheelAngle >= 15.999f)
        {
            fSteeringWheelAngle = 16.0f;
            fDeltaSteering = -0.1f;
        }
        else if (fSteeringWheelAngle <= -16.0f)
        {
            fSteeringWheelAngle = -16.0f;
            fDeltaSteering = 0.1f;
        }

        fVehicleSpeed += fDeltaSpeed;
        if (fVehicleSpeed >= 12.0f)
        {
            fVehicleSpeed = 12.0f;
            fDeltaSpeed = -1.0f;
        }
        else if (fVehicleSpeed <= 0.0f)
        {
            fVehicleSpeed = 0.0f;
            fDeltaSpeed = 0.1f;
        }

        m_signalSteeringWheel.Write(fSteeringWheelAngle);
        m_signalSpeed.Write(fVehicleSpeed);
        std::this_thread::sleep_for(std::chrono::milliseconds(300));
    }
}
