#include "ClassRegistryDatabase.h"
#include "ConfigurationDatabase.h"
#include "DebugService.h"
#include "ObjectRegistryDatabase.h"
#include "ErrorManagement.h"
#include "BasicTCPSocket.h"
#include "BasicUDPSocket.h"
#include "RealTimeApplication.h"
#include "StandardParser.h"
#include "TestCommon.h"
#include <stdio.h>

using namespace MARTe;

#include <signal.h>
#include <unistd.h>

void timeout_handler(int sig) {
  printf("Test timed out!\n");
  _exit(1);
}

void ErrorProcessFunction(const MARTe::ErrorManagement::ErrorInformation &errorInfo, const char8 * const errorDescription) {
    printf("[MARTe Error] %s: %s\n", errorInfo.className, errorDescription);
}

// Forward declarations of other tests
void TestSchedulerControl();
void TestFullTracePipeline();
void RunValidationTest();
void TestConfigCommands();
void TestGAMSignalTracing();
void TestTreeCommand();

int main() {
  signal(SIGALRM, timeout_handler);
  alarm(180); 
  
  MARTe::ErrorManagement::SetErrorProcessFunction(&ErrorProcessFunction);
  
  printf("MARTe2 Debug Suite Integration Tests\n");

  printf("\n--- Test 1: Registry Patching ---\n");
  {
    ObjectRegistryDatabase::Instance()->Purge();
    DebugService service;
    ConfigurationDatabase serviceData;
    serviceData.Write("ControlPort", (uint32)9090);
    service.Initialise(serviceData);
    printf("DebugService initialized and Registry Patched.\n");

    ClassRegistryItem *item =
        ClassRegistryDatabase::Instance()->Find("MemoryMapInputBroker");
    if (item != NULL_PTR(ClassRegistryItem *)) {
      Object *obj = item->GetObjectBuilder()->Build(
          GlobalObjectsDatabase::Instance()->GetStandardHeap());
      if (obj != NULL_PTR(Object *)) {
        printf("Instantiated Broker Class: %s\n",
               obj->GetClassProperties()->GetName());
        printf("Success: Broker patched and instantiated.\n");
      } else {
        printf("Failed to build broker\n");
      }
    } else {
      printf("MemoryMapInputBroker not found in registry\n");
    }
  }
  Sleep::MSec(1000);

  printf("\n--- Test 2: Full Trace Pipeline ---\n");
  TestFullTracePipeline();
  Sleep::MSec(1000);

  printf("\n--- Test 3: Scheduler Control ---\n");
  TestSchedulerControl();
  Sleep::MSec(1000);

  printf("\n--- Test 4: 1kHz Lossless Trace Validation ---\n");
  RunValidationTest();
  Sleep::MSec(1000);

  printf("\n--- Test 5: Config & Metadata Enrichment ---\n");
  TestConfigCommands();
  Sleep::MSec(1000);

  printf("\n--- Test 6: TREE Command Enhancement ---\n");
  TestTreeCommand();
  Sleep::MSec(1000);

  printf("\n--- Test 7: Custom MARTe Message (MSG) ---\n");
  TestMessageCommand();
  Sleep::MSec(1000);
  
  printf("\nAll Integration Tests Finished.\n");

  return 0;
}

// --- Test Implementation ---

void TestGAMSignalTracing() {
    printf("--- Test: GAM Signal Tracing Issue ---\n");

    ObjectRegistryDatabase::Instance()->Purge();

    ConfigurationDatabase cdb;
    StreamString ss = debug_test_config;
    ss.Seek(0);
    StandardParser parser(ss, cdb);
    if (!parser.Parse()) {
        printf("ERROR: Failed to parse config\n");
        return;
    }
    
    cdb.MoveToRoot();
    uint32 n = cdb.GetNumberOfChildren();
    for (uint32 i=0; i<n; i++) {
        const char8* name = cdb.GetChildName(i);
        ConfigurationDatabase child;
        cdb.MoveRelative(name);
        cdb.Copy(child);
        cdb.MoveToAncestor(1u);
        
        StreamString className;
        child.Read("Class", className);
        
        Reference ref(className.Buffer(), GlobalObjectsDatabase::Instance()->GetStandardHeap());
        if (!ref.IsValid()) {
            printf("ERROR: Could not create object %s of class %s\n", name, className.Buffer());
            continue;
        }
        ref->SetName(name);
        if (!ref->Initialise(child)) {
            printf("ERROR: Failed to initialise object %s\n", name);
            continue;
        }
        ObjectRegistryDatabase::Instance()->Insert(ref);
    }

    ReferenceT<DebugService> service = ObjectRegistryDatabase::Instance()->Find("DebugService");
    if (!service.IsValid()) {
        printf("ERROR: DebugService not found\n");
        return;
    }
    service->SetFullConfig(cdb);

    ReferenceT<RealTimeApplication> app = ObjectRegistryDatabase::Instance()->Find("App");
    if (!app.IsValid()) {
        printf("ERROR: App not found\n");
        return;
    }

    if (!app->ConfigureApplication()) {
        printf("ERROR: ConfigureApplication failed.\n");
        return;
    }

    if (app->PrepareNextState("State1") != ErrorManagement::NoError) {
        printf("ERROR: PrepareNextState failed.\n");
        return;
    }

    if (app->StartNextStateExecution() != ErrorManagement::NoError) {
        printf("ERROR: StartNextStateExecution failed.\n");
        return;
    }

    printf("Application started.\n");
    Sleep::MSec(1000);

    // Step 1: Discover signals
    {
        StreamString reply;
        if (SendCommandGAM(8095, "DISCOVER\n", reply)) {
            printf("DISCOVER response received (len=%llu)\n", reply.Size());
        } else {
            printf("ERROR: DISCOVER failed\n");
        }
    }
    Sleep::MSec(500);

    // Step 2: Trace a DataSource signal (Timer.Counter)
    printf("\n--- Step 1: Trace DataSource signal (Timer.Counter) ---\n");
    {
        StreamString reply;
        if (SendCommandGAM(8095, "TRACE App.Data.Timer.Counter 1\n", reply)) {
            printf("TRACE response: %s", reply.Buffer());
        } else {
            printf("ERROR: TRACE failed\n");
        }
    }
    Sleep::MSec(500);

    // Step 3: Trace a GAM input signal (GAM1.In.Counter)
    printf("\n--- Step 2: Trace GAM input signal (GAM1.In.Counter) ---\n");
    {
        StreamString reply;
        if (SendCommandGAM(8095, "TRACE App.Functions.GAM1.In.Counter 1\n", reply)) {
            printf("TRACE response: %s", reply.Buffer());
        } else {
            printf("ERROR: TRACE failed\n");
        }
    }
    Sleep::MSec(500);

    // Step 4: Try to trace another DataSource signal (TimerSlow.Counter)
    printf("\n--- Step 3: Try to trace another signal (TimerSlow.Counter) ---\n");
    {
        StreamString reply;
        if (SendCommandGAM(8095, "TRACE App.Data.TimerSlow.Counter 1\n", reply)) {
            printf("TRACE response: %s", reply.Buffer());
        } else {
            printf("ERROR: TRACE failed\n");
        }
    }
    Sleep::MSec(500);

    // Step 5: Check if we can still trace more signals
    printf("\n--- Step 4: Try to trace Logger.Counter ---\n");
    {
        StreamString reply;
        if (SendCommandGAM(8095, "TRACE App.Data.Logger.Counter 1\n", reply)) {
            printf("TRACE response: %s", reply.Buffer());
        } else {
            printf("ERROR: TRACE failed\n");
        }
    }
    Sleep::MSec(500);

    // Verify UDP is still receiving data
    BasicUDPSocket listener;
    listener.Open();
    listener.Listen(8096);
    
    char buffer[1024];
    uint32 size = 1024;
    TimeoutType timeout(1000);
    int packetCount = 0;
    while (listener.Read(buffer, size, timeout)) {
        packetCount++;
        size = 1024;
    }

    printf("\n--- Results ---\n");
    if (packetCount > 0) {
        printf("SUCCESS: Received %d UDP packets.\n", packetCount);
    } else {
        printf("FAILURE: No UDP packets received. Possible deadlock or crash.\n");
    }

    app->StopCurrentStateExecution();
}
