#include "DebugService.h"
#include "GlobalObjectsDatabase.h"
#include "ObjectRegistryDatabase.h"
#include "RealTimeApplication.h"
#include "StandardParser.h"
#include "StreamString.h"
#include "StringHelper.h"
#include "TestCommon.h"
#include <stdio.h>

using namespace MARTe;

namespace {

const char8 * const debug_commands_config =
"DebugService = {"
"    Class = DebugService "
"    ControlPort = 8120 "
"    UdpPort = 8121 "
"    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 }"
"}";

const uint16 CONTROL_PORT = 8120u;
const char8 * const SIGNAL_NAME = "App.Data.DDB.Counter";

// Pulls the value out of a `{"Name":...,"Value":"<v>", ...}` VALUE reply.
// The command replies are fixed-format enough that a plain substring scan
// is sufficient (no need for a full JSON parser in these integration tests).
bool ExtractValueStr(const char8 *reply, StreamString &val) {
  const char8 *key = "\"Value\":\"";
  const char8 *p = StringHelper::SearchString(reply, key);
  if (p == NULL_PTR(const char8 *)) return false;
  p += StringHelper::Length(key);
  const char8 *end = StringHelper::SearchString(p, "\"");
  if (end == NULL_PTR(const char8 *)) return false;
  val = "";
  while (p < end) {
    val += *p;
    p++;
  }
  return true;
}

bool ExtractValueUint32(const char8 *reply, uint32 &val) {
  StreamString s;
  if (!ExtractValueStr(reply, s)) return false;
  const char8 *p = s.Buffer();
  if (p == NULL_PTR(const char8 *) || *p == '\0') return false;
  val = 0u;
  while (*p != '\0') {
    if (*p < '0' || *p > '9') return false;
    val = (val * 10u) + (uint32)(*p - '0');
    p++;
  }
  return true;
}

bool QueryValueUint32(uint32 &val) {
  StreamString reply;
  if (!SendCommandGAM(CONTROL_PORT, "VALUE App.Data.DDB.Counter\n", reply))
    return false;
  return ExtractValueUint32(reply.Buffer(), val);
}

}  // namespace

void TestDebugCommands() {
  printf("--- Test: FORCE/UNFORCE/BREAK/STEP/STEP_STATUS/VALUE/LS Commands ---\n");

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

  ConfigurationDatabase cdb;
  StreamString ss = debug_commands_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: VALUE baseline.
  printf("\n--- Step 1: VALUE ---\n");
  {
    StreamString reply;
    if (SendCommandGAM(CONTROL_PORT, "VALUE App.Data.DDB.Counter\n", reply)) {
      printf("VALUE response: %s", reply.Buffer());
      uint32 v = 0u;
      if (StringHelper::SearchString(reply.Buffer(), "OK VALUE") !=
              NULL_PTR(const char8 *) &&
          ExtractValueUint32(reply.Buffer(), v)) {
        printf("SUCCESS: VALUE returned a parseable counter value (%u).\n", v);
      } else {
        printf("FAILURE: VALUE reply malformed.\n");
      }
    } else {
      printf("FAILURE: VALUE command failed.\n");
    }
  }

  // Step 2: VALUE on an unknown signal should report an error, not crash.
  printf("\n--- Step 2: VALUE on unknown signal ---\n");
  {
    StreamString reply;
    if (SendCommandGAM(CONTROL_PORT, "VALUE App.Data.DDB.NoSuchSignal\n",
                        reply)) {
      printf("VALUE response: %s", reply.Buffer());
      if (StringHelper::SearchString(reply.Buffer(), "Error") !=
          NULL_PTR(const char8 *)) {
        printf("SUCCESS: Unknown signal reported as an error.\n");
      } else {
        printf("FAILURE: Unknown signal did not report an error.\n");
      }
    } else {
      printf("FAILURE: VALUE command failed.\n");
    }
  }

  // Step 3: LS with an explicit path.
  printf("\n--- Step 3: LS App.Data ---\n");
  {
    StreamString reply;
    if (SendCommandGAM(CONTROL_PORT, "LS App.Data\n", reply)) {
      printf("LS response: %s", reply.Buffer());
      if (StringHelper::SearchString(reply.Buffer(), "DDB") !=
              NULL_PTR(const char8 *) &&
          StringHelper::SearchString(reply.Buffer(), "OK LS") !=
              NULL_PTR(const char8 *)) {
        printf("SUCCESS: LS App.Data listed the DDB data source.\n");
      } else {
        printf("FAILURE: LS App.Data did not list expected children.\n");
      }
    } else {
      printf("FAILURE: LS command failed.\n");
    }
  }

  // Step 4: bare LS (root). Regression test: this used to wrap the
  // ObjectRegistryDatabase singleton in a Reference, which decremented its
  // refcount to zero and deleted it when the Reference went out of scope,
  // permanently killing the TCP control server. Verify both that LS itself
  // succeeds AND that the control server is still alive afterwards.
  printf("\n--- Step 4: bare LS (root) ---\n");
  {
    StreamString reply;
    bool lsOk = SendCommandGAM(CONTROL_PORT, "LS\n", reply);
    if (lsOk) {
      printf("LS response: %s", reply.Buffer());
    }
    bool lsGood =
        lsOk &&
        StringHelper::SearchString(reply.Buffer(), "App") !=
            NULL_PTR(const char8 *) &&
        StringHelper::SearchString(reply.Buffer(), "DebugService") !=
            NULL_PTR(const char8 *) &&
        StringHelper::SearchString(reply.Buffer(), "OK LS") !=
            NULL_PTR(const char8 *);

    StreamString postReply;
    bool postOk = SendCommandGAM(CONTROL_PORT, "VALUE App.Data.DDB.Counter\n",
                                  postReply);
    bool postGood = postOk && StringHelper::SearchString(postReply.Buffer(),
                                                          "OK VALUE") !=
                                  NULL_PTR(const char8 *);

    if (lsGood && postGood) {
      printf("SUCCESS: bare LS listed the root and control server survived.\n");
    } else {
      printf("FAILURE: bare LS broke the control server (lsGood=%d postGood=%d).\n",
             lsGood, postGood);
    }
  }

  // Step 5: FORCE holds the signal at a fixed value.
  printf("\n--- Step 5: FORCE ---\n");
  {
    StreamString reply;
    bool ok = SendCommandGAM(CONTROL_PORT,
                              "FORCE App.Data.DDB.Counter 424242\n", reply);
    printf("FORCE response: %s", ok ? reply.Buffer() : "(no reply)");
    bool forceAccepted =
        ok && StringHelper::SearchString(reply.Buffer(), "OK FORCE 1") !=
                  NULL_PTR(const char8 *);

    Sleep::MSec(150);
    StreamString v1;
    bool v1Ok = SendCommandGAM(CONTROL_PORT, "VALUE App.Data.DDB.Counter\n", v1);
    Sleep::MSec(150);
    StreamString v2;
    bool v2Ok = SendCommandGAM(CONTROL_PORT, "VALUE App.Data.DDB.Counter\n", v2);

    bool bothForced =
        v1Ok && v2Ok &&
        StringHelper::SearchString(v1.Buffer(), "\"Value\":\"424242\"") !=
            NULL_PTR(const char8 *) &&
        StringHelper::SearchString(v2.Buffer(), "\"Value\":\"424242\"") !=
            NULL_PTR(const char8 *);

    if (forceAccepted && bothForced) {
      printf("SUCCESS: FORCE held the signal at 424242 across two reads.\n");
    } else {
      printf("FAILURE: FORCE did not hold the forced value.\n");
    }
  }

  // Step 6: UNFORCE lets the signal resume advancing.
  printf("\n--- Step 6: UNFORCE ---\n");
  {
    StreamString reply;
    bool ok =
        SendCommandGAM(CONTROL_PORT, "UNFORCE App.Data.DDB.Counter\n", reply);
    printf("UNFORCE response: %s", ok ? reply.Buffer() : "(no reply)");
    bool unforceAccepted =
        ok && StringHelper::SearchString(reply.Buffer(), "OK UNFORCE 1") !=
                  NULL_PTR(const char8 *);

    Sleep::MSec(200);
    uint32 v1 = 0u;
    bool v1Ok = QueryValueUint32(v1);
    Sleep::MSec(200);
    uint32 v2 = 0u;
    bool v2Ok = QueryValueUint32(v2);

    bool advancing = v1Ok && v2Ok && v1 != 424242u && v2 > v1;

    if (unforceAccepted && advancing) {
      printf("SUCCESS: UNFORCE resumed live updates (%u -> %u).\n", v1, v2);
    } else {
      printf("FAILURE: UNFORCE did not resume live updates (%u -> %u).\n", v1,
             v2);
    }
  }

  // Step 7: PAUSE holds execution, STEP advances one command-cycle at a
  // time while STEP_STATUS reports the paused state, RESUME lets it run
  // free again.
  //
  // NOTE: OutputPauseAndStep() (DebugBrokerWrapper.h) blocks the RT thread
  // in a nested pause/step spin *after* the current cycle's data has
  // already been committed. As a result, the very first STEP issued right
  // after a manual PAUSE only moves the thread from the first spin to the
  // second spin without producing a new committed cycle (no observable
  // value change) -- it takes a second STEP to see the counter actually
  // advance. This is verified/tolerated below rather than asserted away.
  printf("\n--- Step 7: PAUSE / STEP / STEP_STATUS / RESUME ---\n");
  {
    StreamString pauseReply;
    bool pauseOk = SendCommandGAM(CONTROL_PORT, "PAUSE\n", pauseReply);
    printf("PAUSE response: %s", pauseOk ? pauseReply.Buffer() : "(no reply)");

    Sleep::MSec(150);
    uint32 vPaused1 = 0u;
    QueryValueUint32(vPaused1);
    Sleep::MSec(150);
    uint32 vPaused2 = 0u;
    QueryValueUint32(vPaused2);
    bool heldWhilePaused = (vPaused1 == vPaused2);
    if (heldWhilePaused) {
      printf("SUCCESS: Counter held steady while paused (%u).\n", vPaused1);
    } else {
      printf("FAILURE: Counter moved while paused (%u -> %u).\n", vPaused1,
             vPaused2);
    }

    StreamString statusReply;
    bool statusOk = SendCommandGAM(CONTROL_PORT, "STEP_STATUS\n", statusReply);
    printf("STEP_STATUS response: %s",
           statusOk ? statusReply.Buffer() : "(no reply)");
    bool pausedReported =
        statusOk && StringHelper::SearchString(statusReply.Buffer(),
                                                "\"Paused\":true") !=
                        NULL_PTR(const char8 *);
    if (pausedReported) {
      printf("SUCCESS: STEP_STATUS reports Paused=true.\n");
    } else {
      printf("FAILURE: STEP_STATUS did not report Paused=true.\n");
    }

    // "Primer" STEP: settles the one-time spin-transition quirk described
    // above; no value-change assertion here by design.
    StreamString step1Reply;
    SendCommandGAM(CONTROL_PORT, "STEP 1\n", step1Reply);
    Sleep::MSec(150);
    uint32 vAfterPrimer = 0u;
    QueryValueUint32(vAfterPrimer);

    StreamString step2Reply;
    bool step2Ok = SendCommandGAM(CONTROL_PORT, "STEP 2\n", step2Reply);
    printf("STEP 2 response: %s", step2Ok ? step2Reply.Buffer() : "(no reply)");
    Sleep::MSec(200);
    uint32 vAfterStep = 0u;
    bool vAfterStepOk = QueryValueUint32(vAfterStep);

    bool stepAdvanced = vAfterStepOk && vAfterStep > vAfterPrimer;
    if (stepAdvanced) {
      printf("SUCCESS: STEP advanced the counter (%u -> %u).\n", vAfterPrimer,
             vAfterStep);
    } else {
      printf("FAILURE: STEP did not advance the counter (%u -> %u).\n",
             vAfterPrimer, vAfterStep);
    }

    StreamString resumeReply;
    bool resumeOk = SendCommandGAM(CONTROL_PORT, "RESUME\n", resumeReply);
    printf("RESUME response: %s",
           resumeOk ? resumeReply.Buffer() : "(no reply)");

    Sleep::MSec(200);
    uint32 vResumed1 = 0u;
    QueryValueUint32(vResumed1);
    Sleep::MSec(200);
    uint32 vResumed2 = 0u;
    bool resumedOk = QueryValueUint32(vResumed2);
    bool freeRunning = resumedOk && vResumed2 > vResumed1;
    if (freeRunning) {
      printf("SUCCESS: RESUME let execution run free again (%u -> %u).\n",
             vResumed1, vResumed2);
    } else {
      printf("FAILURE: Execution did not resume (%u -> %u).\n", vResumed1,
             vResumed2);
    }
  }

  // Step 8: BREAK auto-pauses when the condition is met and holds the
  // value; BREAK ... OFF clears the condition without auto-resuming.
  printf("\n--- Step 8: BREAK / BREAK OFF ---\n");
  {
    uint32 baseline = 0u;
    QueryValueUint32(baseline);
    // Counter increments ~1 per RT cycle (1 kHz); +300 is reachable well
    // within the retry budget below without being trivially already-true.
    uint32 target = baseline + 300u;

    StreamString cmd;
    cmd.Printf("BREAK App.Data.DDB.Counter > %u\n", target);
    StreamString breakReply;
    bool breakOk = SendCommandGAM(CONTROL_PORT, cmd.Buffer(), breakReply);
    printf("BREAK response: %s", breakOk ? breakReply.Buffer() : "(no reply)");
    bool breakAccepted =
        breakOk && StringHelper::SearchString(breakReply.Buffer(),
                                              "OK BREAK 1") !=
                       NULL_PTR(const char8 *);

    bool pausedByBreak = false;
    for (int retry = 0; retry < 30 && !pausedByBreak; retry++) {
      Sleep::MSec(100);
      StreamString statusReply;
      if (SendCommandGAM(CONTROL_PORT, "STEP_STATUS\n", statusReply)) {
        if (StringHelper::SearchString(statusReply.Buffer(),
                                        "\"Paused\":true") !=
            NULL_PTR(const char8 *)) {
          pausedByBreak = true;
        }
      }
    }
    if (pausedByBreak) {
      printf("SUCCESS: BREAK auto-paused once the condition was met.\n");
    } else {
      printf("FAILURE: BREAK never paused execution (target=%u).\n", target);
    }

    uint32 held1 = 0u, held2 = 0u;
    QueryValueUint32(held1);
    Sleep::MSec(200);
    QueryValueUint32(held2);
    bool heldAfterBreak = (held1 == held2) && (held1 >= target);
    if (heldAfterBreak) {
      printf("SUCCESS: Value held at/above the break target (%u).\n", held1);
    } else {
      printf("FAILURE: Value not held after break (%u -> %u, target=%u).\n",
             held1, held2, target);
    }

    StreamString offReply;
    bool offOk = SendCommandGAM(CONTROL_PORT,
                                 "BREAK App.Data.DDB.Counter OFF\n", offReply);
    printf("BREAK OFF response: %s", offOk ? offReply.Buffer() : "(no reply)");
    bool offAccepted =
        offOk && StringHelper::SearchString(offReply.Buffer(), "OK BREAK") !=
                     NULL_PTR(const char8 *);

    StreamString statusAfterOff;
    bool statusOk =
        SendCommandGAM(CONTROL_PORT, "STEP_STATUS\n", statusAfterOff);
    bool stillPaused =
        statusOk && StringHelper::SearchString(statusAfterOff.Buffer(),
                                                "\"Paused\":true") !=
                        NULL_PTR(const char8 *);
    if (stillPaused) {
      printf("SUCCESS: BREAK OFF cleared the condition without auto-resuming.\n");
    } else {
      printf("FAILURE: Clearing BREAK unexpectedly changed the paused state.\n");
    }

    if (!breakAccepted || !offAccepted) {
      printf("FAILURE: BREAK/BREAK OFF command replies malformed.\n");
    }

    StreamString resumeReply;
    SendCommandGAM(CONTROL_PORT, "RESUME\n", resumeReply);
    Sleep::MSec(200);
    uint32 vFinal1 = 0u, vFinal2 = 0u;
    QueryValueUint32(vFinal1);
    Sleep::MSec(200);
    bool finalOk = QueryValueUint32(vFinal2);
    if (finalOk && vFinal2 > vFinal1) {
      printf("SUCCESS: Execution resumed after BREAK OFF + RESUME.\n");
    } else {
      printf("FAILURE: Execution did not resume after BREAK OFF + RESUME.\n");
    }
  }

  // Step 9: TREE on a DataSource path exercises the signal-listing branch
  // of ExportTreeNode (as opposed to the plain child-container branch that
  // a bare/root TREE hits).
  printf("\n--- Step 9: TREE App.Data.Timer (DataSource) ---\n");
  {
    StreamString reply;
    if (SendCommandGAM(CONTROL_PORT, "TREE App.Data.Timer\n", reply)) {
      printf("TREE response: %s", reply.Buffer());
      if (StringHelper::SearchString(reply.Buffer(), "\"Class\":\"Signal\"") !=
              NULL_PTR(const char8 *) &&
          StringHelper::SearchString(reply.Buffer(), "OK TREE") !=
              NULL_PTR(const char8 *)) {
        printf("SUCCESS: TREE on a DataSource listed its signals.\n");
      } else {
        printf("FAILURE: TREE on a DataSource did not list signals.\n");
      }
    } else {
      printf("FAILURE: TREE command failed.\n");
    }
  }

  // Step 10: TREE on a GAM path exercises the InputSignal/OutputSignal
  // listing branches of ExportTreeNode.
  printf("\n--- Step 10: TREE App.Functions.GAM1 (GAM) ---\n");
  {
    StreamString reply;
    if (SendCommandGAM(CONTROL_PORT, "TREE App.Functions.GAM1\n", reply)) {
      printf("TREE response: %s", reply.Buffer());
      if (StringHelper::SearchString(reply.Buffer(),
                                      "\"Class\":\"InputSignal\"") !=
              NULL_PTR(const char8 *) &&
          StringHelper::SearchString(reply.Buffer(),
                                      "\"Class\":\"OutputSignal\"") !=
              NULL_PTR(const char8 *) &&
          StringHelper::SearchString(reply.Buffer(), "OK TREE") !=
              NULL_PTR(const char8 *)) {
        printf("SUCCESS: TREE on a GAM listed its input and output signals.\n");
      } else {
        printf("FAILURE: TREE on a GAM did not list In/Out signals.\n");
      }
    } else {
      printf("FAILURE: TREE command failed.\n");
    }
  }

  // Step 11: INFO on a name that matches neither a registry object nor a
  // signal alias must report an error rather than crash.
  printf("\n--- Step 11: INFO on unknown name ---\n");
  {
    StreamString reply;
    if (SendCommandGAM(CONTROL_PORT, "INFO App.Data.DDB.NoSuchThing\n",
                        reply)) {
      printf("INFO response: %s", reply.Buffer());
      if (StringHelper::SearchString(reply.Buffer(), "Object not found") !=
              NULL_PTR(const char8 *) &&
          StringHelper::SearchString(reply.Buffer(), "OK INFO") !=
              NULL_PTR(const char8 *)) {
        printf("SUCCESS: INFO on an unknown name reported an error.\n");
      } else {
        printf("FAILURE: INFO on an unknown name did not report an error.\n");
      }
    } else {
      printf("FAILURE: INFO command failed.\n");
    }
  }

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

// ---------------------------------------------------------------------------
// TestDebugConfigAutoRebuild
// ---------------------------------------------------------------------------
//
// CONFIG normally serves back the ConfigurationDatabase handed to
// SetFullConfig() by the application's own startup code. If SetFullConfig()
// is never called (manualConfigSet stays false), ServeConfig() instead
// falls back to RebuildConfigFromRegistry(), which walks the live
// ObjectRegistryDatabase tree (BuildCDBFromContainer) to reconstruct an
// equivalent configuration on demand. This exercises that fallback path.
void TestDebugConfigAutoRebuild() {
  printf("--- Test: CONFIG auto-rebuild from registry (no SetFullConfig) ---\n");

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

  ConfigurationDatabase cdb;
  StreamString ss = debug_commands_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;
  }
  // Deliberately do NOT call service->SetFullConfig(cdb) here: CONFIG must
  // still work by rebuilding from the registry on the fly.

  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);

  printf("\n--- Step 1: CONFIG without a prior SetFullConfig ---\n");
  {
    StreamString reply;
    if (SendCommandGAM(CONTROL_PORT, "CONFIG\n", reply)) {
      printf("CONFIG response (len=%llu)\n", reply.Size());
      if (StringHelper::SearchString(reply.Buffer(), "GAM1") !=
              NULL_PTR(const char8 *) &&
          StringHelper::SearchString(reply.Buffer(), "DebugService") !=
              NULL_PTR(const char8 *) &&
          StringHelper::SearchString(reply.Buffer(), "OK CONFIG") !=
              NULL_PTR(const char8 *)) {
        printf("SUCCESS: CONFIG rebuilt the configuration from the "
               "registry.\n");
      } else {
        printf("FAILURE: CONFIG (auto-rebuild) reply missing expected "
               "content.\n");
      }
    } else {
      printf("FAILURE: CONFIG command failed.\n");
    }
  }

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