////////////////////////////////////////////////////////////////////////////////////
///
///   \brief Simple simulation of a component that subscribes to any Local
///          Pose Sensor data on network using JTCP.
///
///   \param[in] subsystemID Subsystem ID number.
///   \param[in] nodeID Node ID number.
///
////////////////////////////////////////////////////////////////////////////////////
int RunClient(JAUS::UShort subsystemID, JAUS::Byte nodeID)
{
    JAUS::Component component;
    JAUS::Address id(subsystemID, nodeID, 1);

    if(id.IsValid() == false)
    {
        std::cout << "Invalid JAUS ID: " << id.ToString() << std::endl;
    }

    // Setup identification data.
    component.DiscoveryService()->SetSubsystemIdentification(JAUS::Subsystem::Vehicle,
                                                             "Subscriber JTCP Tutorial");

    //  Change the transport service to use JTCP
    component.AddService(new JAUS::JTCPClient());
    //((JAUS::JTCPClient*)component.TransportService())->AddConnection("10.171.190.61", JAUS::Address(subsystemID, 1, 1));
    //((JAUS::JTCPClient*)component.TransportService())->AddConnection("127.0.0.1", JAUS::Address(subsystemID, 1, 1));

    // Add support for reading Local Pose messages without having to
    // add a custom service.
    component.TransportService()->AddMessageTemplate(new JAUS::ReportLocalPose());
    component.TransportService()->AddMessageTemplate(new JAUS::QueryLocalPose());

    // Add a callback to get messages when they arrive.
    LocalPoseCallback localPoseCallback;
    component.TransportService()->RegisterCallback(JAUS::REPORT_LOCAL_POSE, &localPoseCallback);

    // Initialize component and communication.
    if(component.Initialize(id) == false)
    {
        std::cout << "Failed to initialize JAUS component with ID: " << id.ToString() << std::endl;
        return 0;
    }

    JAUS::Time printTime(true);

    while(CxUtils::GetChar() != 27 && component.ManagementService()->GetStatus() != JAUS::Management::Status::Shutdown)
    {
        // Look for any subsystems.
        JAUS::Subsystem::Map subsystems;
        JAUS::Subsystem::Map::iterator subsystem;

        component.DiscoveryService()->GetSubsystems(subsystems);

        for(subsystem = subsystems.begin();
            subsystem != subsystems.end();
            subsystem++)
        {
            // Look for local pose sensors to subscribe to.
            JAUS::Address::List sensors = subsystem->second->GetComponentsWithService(JAUS::LocalPoseSensor::Name);
            if(sensors.size() > 0)
            {
                JAUS::Address::List::iterator id;
                for(id = sensors.begin();
                    id != sensors.end();
                    id++)
                {
                    if(*id != component.GetComponentID() && 
                       component.EventsService()->HaveSubscription(JAUS::REPORT_LOCAL_POSE, *id) == false)
                    {
                        std::cout << "Found new Local Pose Sensors on " << id->ToString() << std::endl;
                        JAUS::QueryLocalPose queryLocalPose;
                        // Request all fields.
                        queryLocalPose.SetPresenceVector(queryLocalPose.GetPresenceVectorMask());
                        component.EventsService()->RequestPeriodicEvent(*id, &queryLocalPose, 5);
                    }
                }
            }
        }

        JAUS::Subsystem::DeleteSubsystemMap(subsystems);
        // Only print to screen every now and then.
        if(JAUS::Time(true) - printTime > .5)
        {
            //std::cout << "=======================================================\n";
            //component.EventsService()->PrintStatus();
            //printTime.SetCurrentTime();
        }
        // Don't overload the CPU
        CxUtils::SleepMs(1);
    }

    // Do shutdown
    component.Shutdown();

    return 0;
}
////////////////////////////////////////////////////////////////////////////////////
///
///   \brief Simple simulation of a component with a Local Pose Sensor using
///          JTCP.
///
///   \param[in] subsystemID Subsystem ID number.
///   \param[in] nodeID Node ID number.
///
////////////////////////////////////////////////////////////////////////////////////
int RunServer(JAUS::UShort subsystemID, JAUS::Byte nodeID)
{
    JAUS::Component component;
    JAUS::Address id(subsystemID, nodeID, 1);
    if(id.IsValid() == false)
    {
        std::cout << "Invalid JAUS ID: " << id.ToString() << std::endl;
    }

    // Setup identification data.
    component.DiscoveryService()->SetSubsystemIdentification(JAUS::Subsystem::Vehicle,
                                                             "Local Pose JTCP Tutorial");

    //  Change the transport service to use JTCP
    component.AddService(new JAUS::JTCPClient());

    // Add any other services.  In this case local pose.
    JAUS::LocalPoseSensor* localPoseSensor = new JAUS::LocalPoseSensor();
    component.AddService(localPoseSensor);


    // Initialize component and communication.
    if(component.Initialize(id) == false)
    {
        std::cout << "Failed to initialize JAUS component with ID: " << id.ToString() << std::endl;
        return 0;
    }

    // Configure local pose information.
    JAUS::LocalPose localPose;
    localPose.SetX(0);
    localPose.SetY(0);
    localPose.SetZ(0);
    localPose.SetYaw(0);
    localPose.SetTimeStamp(JAUS::Time(true));

    JAUS::Time printTime(true);
    while(CxUtils::GetChar() != 27 && component.ManagementService()->GetStatus() != JAUS::Management::Status::Shutdown)
    {
        // Fake a position change.
        localPose.SetY(localPose.GetY() + .01);
        localPose.SetTimeStamp(JAUS::Time(true));
        // Save the data.  Anyone who is subscribing will be
        // notified of the change automatically.
        localPoseSensor->SetLocalPose(localPose);

        // Only print to screen every now and then.
        if(JAUS::Time(true) - printTime > .5)
        {
            std::cout << "=======================================================\n";
            localPoseSensor->PrintStatus();
            component.EventsService()->PrintStatus();
            printTime.SetCurrentTime();
        }
        // Don't overload the CPU
        CxUtils::SleepMs(1);
    }

    // Do shutdown
    component.Shutdown();

    return 0;
}
int main(int argc, char* argv[])
{
    JAUS::Component component; 

    // Testing prioritized message data
    component.TransportService()->AddPriorityMessage(JAUS::REPORT_GLOBAL_POSE);
    
    // Add the services our component needs.  This has to
    // be done before component initialization.
    // Second, you cannot add the same service twice.

    JAUS::GlobalPoseSensor* globalPoseSensor = new JAUS::GlobalPoseSensor();
    JAUS::LocalPoseSensor* localPoseSensor = new JAUS::LocalPoseSensor();
    JAUS::VelocityStateSensor* velocityStateSensor = new JAUS::VelocityStateSensor();
    JAUS::AccelerationStateSensor* accelerationStateSensor = new JAUS::AccelerationStateSensor();
    FakeMicrocontroller* mcu = new FakeMicrocontroller();

    // Add to our component.
    component.AddService(globalPoseSensor);
    component.AddService(localPoseSensor);
    component.AddService(velocityStateSensor);
    component.AddService(accelerationStateSensor);
    component.AddService(mcu);

    // Tell our sensor services what component we want them
    // to sync there data with (i.e. who to subscribe to.
    JAUS::Address syncID(gSubsystemID, gSyncNodeID, gSyncComponentID);
    globalPoseSensor->SynchronizeToComponent(syncID);
    localPoseSensor->SynchronizeToComponent(syncID);
    velocityStateSensor->SynchronizeToComponent(syncID);
    accelerationStateSensor->SynchronizeToComponent(syncID);
    mcu->SynchronizeToComponent(syncID);

    // Example of how to use events callback
    EventCallback callback;
    component.EventsService()->RegisterCallback(&callback);


    // Try load settings files.
    // These files determine your UDP network 
    // settings, what Services to turn on/off 
    // or any Service specific settings. See the
    // example file for settings file format.
    if(component.LoadSettings("settings/services.xml") == false)
    {
        // Working directory probably not set (or not running from output directory), so
        // use default values.
        component.DiscoveryService()->SetSubsystemIdentification(JAUS::Subsystem::Vehicle,
                                                                 "Simulation");
        // Set optional identification info for this component.
        component.DiscoveryService()->SetComponentIdentification("Synchronize");
    }

    // Initialize component with component given ID.
    if(component.Initialize(JAUS::Address(gSubsystemID, gNodeID, gComponentID)) == false)
    {
        std::cout << "Failed to Initialize Component.\n";
        return 0;
    }
    
    bool foundSyncID = false;
    CxUtils::Time::Stamp printTimeMs = 0;
    while(CxUtils::GetChar() != 27)
    {
        // This is a short example code to dynamically lookup a
        // component ID for the service you want to synchronize on your
        // subsystem (like in your AI program if the services may run on different computers).
#if 0
        // Enable synchronization for each sensor.
        if(globalPoseSensor->IsSynchronizing() == false)
        {
            // Find sensor to sync to.
            JAUS::Address::List idList = component.DiscoveryService()->GetSubsystem(gSubsystemID)->GetComponentsWithService(JAUS::GlobalPoseSensor::Name);
            if(idList.size() > 0)
            {
                globalPoseSensor->SynchronizeToComponent(idList.front());
            }
        }
#endif
        if(CxUtils::Time::GetUtcTimeMs() - printTimeMs > 500)
        {
            // Print status of services.
            std::cout << "\n======================================================\n";
            globalPoseSensor->PrintStatus(); std::cout << std::endl;
            localPoseSensor->PrintStatus(); std::cout << std::endl;
            velocityStateSensor->PrintStatus(); std::cout << std::endl;
            accelerationStateSensor->PrintStatus(); std::cout << std::endl;
            mcu->PrintStatus(); std::cout << std::endl;
            printTimeMs = CxUtils::Time::GetUtcTimeMs();
        }
        CxUtils::SleepMs(1);
    }

    // Don't delete services, they are
    // deleted by the Component class.

    // Shutdown any components associated with our subsystem.
    component.Shutdown();

    return 0;
}