#include "BasicTCPSocket.h"
#include "ConfigurationDatabase.h"
#include "DebugService.h"
#include "GlobalObjectsDatabase.h"
#include "ObjectRegistryDatabase.h"
#include "RealTimeApplication.h"
#include "StandardParser.h"
#include "StreamString.h"
#include "TestCommon.h"
#include "IOGAM.h"
#include "LinuxTimer.h"
#include "GAMDataSource.h"
#include "TimingDataSource.h"
#include "GAMScheduler.h"
#include <stdio.h>

using namespace MARTe;

void TestTreeCommand() {
  printf("--- Test: TREE Command Enhancement ---\n");

  ObjectRegistryDatabase::Instance()->Purge();
  Sleep::MSec(2000); // Wait for sockets from previous tests to clear

  ConfigurationDatabase cdb;
  // Use unique ports to avoid conflict with other tests
  const char8 * const tree_test_config = 
  "DebugService = {"
  "    Class = DebugService "
  "    ControlPort = 8110 "
  "    UdpPort = 8111 "
  "    StreamIP = \"127.0.0.1\" "
  "}"
  "App = {"
  "    Class = RealTimeApplication "
  "    +Functions = {"
  "        Class = ReferenceContainer "
  "        +GAM1 = {"
  "            Class = IOGAM "
  "            InputSignals = {"
  "                Counter = { DataSource = Timer Type = uint32 Frequency = 1000 }"
  "                Time = { DataSource = Timer Type = uint32 }"
  "            }"
  "            OutputSignals = {"
  "                Counter = { DataSource = DDB Type = uint32 }"
  "                Time = { DataSource = DDB Type = uint32 }"
  "            }"
  "        }"
  "    }"
  "    +Data = {"
  "        Class = ReferenceContainer "
  "        DefaultDataSource = DDB "
  "        +Timer = { Class = LinuxTimer SleepTime = 1000 Signals = { Counter = { Type = uint32 } Time = { Type = uint32 } } }"
  "        +DDB = { Class = GAMDataSource Signals = { Counter = { Type = uint32 } Time = { Type = uint32 } } }"
  "        +DAMS = { Class = TimingDataSource }"
  "    }"
  "    +States = {"
  "        Class = ReferenceContainer "
  "        +State1 = { Class = RealTimeState +Threads = { Class = ReferenceContainer +Thread1 = { Class = RealTimeThread Functions = {GAM1} } } }"
  "    }"
  "    +Scheduler = { Class = GAMScheduler TimingDataSource = DAMS }"
  "}";

  StreamString ss = tree_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: Request TREE
  StreamString reply;
  if (SendCommandGAM(8110, "TREE\n", reply)) {
    printf("TREE response received (len=%llu)\n", reply.Size());
    // ...
  }

  // Step 2: SERVICE_INFO
  printf("\n--- Step 2: SERVICE_INFO ---\n");
  reply = "";
  if (SendCommandGAM(8110, "SERVICE_INFO\n", reply)) {
    printf("SERVICE_INFO response: %s", reply.Buffer());
    if (StringHelper::SearchString(reply.Buffer(), "TCP_CTRL:8110") != NULL_PTR(const char8 *) &&
        StringHelper::SearchString(reply.Buffer(), "UDP_STREAM:8111") != NULL_PTR(const char8 *)) {
      printf("SUCCESS: SERVICE_INFO returned correct ports.\n");
    } else {
      printf("FAILURE: SERVICE_INFO returned incorrect data.\n");
    }
  }

  // Step 3: MONITOR
  printf("\n--- Step 3: MONITOR SIGNAL ---\n");
  reply = "";
  if (SendCommandGAM(8110, "MONITOR SIGNAL App.Data.Timer.Counter 10\n", reply)) {
    printf("MONITOR response: %s", reply.Buffer());
    if (StringHelper::SearchString(reply.Buffer(), "OK MONITOR 1") != NULL_PTR(const char8 *)) {
      printf("SUCCESS: Signal monitored.\n");
    } else {
      printf("FAILURE: Could not monitor signal.\n");
    }
  }

  // Step 4: UNMONITOR
  printf("\n--- Step 4: UNMONITOR SIGNAL ---\n");
  reply = "";
  if (SendCommandGAM(8110, "UNMONITOR SIGNAL App.Data.Timer.Counter\n", reply)) {
    printf("UNMONITOR response: %s", reply.Buffer());
    if (StringHelper::SearchString(reply.Buffer(), "OK UNMONITOR 1") != NULL_PTR(const char8 *)) {
      printf("SUCCESS: Signal unmonitored.\n");
    } else {
      printf("FAILURE: Could not unmonitor signal.\n");
    }
  }

  app->StopCurrentStateExecution();
  ObjectRegistryDatabase::Instance()->Purge();
}
