//------------------------------------------------------------------------------
// entityStateManager() --  (Output support)
//    -- Update the entity object for this NIB(Player)
//------------------------------------------------------------------------------
bool Nib::entityStateManager(const LCreal curExecTime)
{
   bool ok = false;

   // Get the player pointer
   const Simulation::Player* player = getPlayer();
   if (player == nullptr) return ok;

   // Dummy weapon?
   const Simulation::Weapon* ww = dynamic_cast<const Simulation::Weapon*>( player );
   if (ww != nullptr) {
      if (ww->isDummy()) return ok;
   }

   if (isPlayerStateUpdateRequired(curExecTime)) {

      //
      // Send an entity state PDU
      //   1) create buffer
      //   2) set state information
      //   3) send data
      //

      // Get our NetIO and the main simulation
      NetIO* disIO = static_cast<NetIO*>(getNetIO());
      Simulation::Simulation* sim = disIO->getSimulation();

      // Capture the player data, reset the dead reckoning and
      // mark the current time.
      playerState2Nib();

      // ---
      // Create buffer and cast it as an entity state PDU
      // ---
      char pduBuffer[NetIO::MAX_PDU_SIZE];
      EntityStatePDU* pdu = (EntityStatePDU*) &pduBuffer[0];

      //
      // Entity state PDU structure
      // =========================================================
      // PDUHeader            header;
      // EntityIdentifierDIS  entityID;
      // uint8_t              forceID;
      // uint8_t              numberOfArticulationParameters;
      // EntityType           entityType;
      // EntityType           alternativeType;
      // VectorDIS            entityLinearVelocity;
      // WorldCoordinates     entityLocation;
      // EulerAngles          entityOrientation;
      // uint32_t             appearance;
      // uint8_t              deadReckoningAlgorithm;
      // uint8_t              otherParameters[15];
      // VectorDIS            DRentityLinearAcceleration;
      // AngularVelocityVectorDIS DRentityAngularVelocity;
      // EntityMarking        entityMarking;
      // uint32_t             capabilities;
      // =========================================================
      //

      // ---
      // Standard header (PDUHeader)
      // ---
      pdu->header.protocolVersion = disIO->getVersion();
      pdu->header.exerciseIdentifier = disIO->getExerciseID();
      pdu->header.PDUType = NetIO::PDU_ENTITY_STATE;
      pdu->header.protocolFamily = NetIO::PDU_FAMILY_ENTITY_INFO;
      //
      if (disIO->getTimeline() == Simulation::NetIO::UTC)
         pdu->header.timeStamp = disIO->makeTimeStamp( getTimeUtc(), true );
      else
         pdu->header.timeStamp = disIO->makeTimeStamp( getTimeExec(), false );
      //
      pdu->header.status = 0;
      pdu->header.padding = 0;

      // ---
      // Entity ID (EntityIdentifierID)
      // ---
      pdu->entityID.simulationID.siteIdentification = getSiteID();
      pdu->entityID.simulationID.applicationIdentification = getApplicationID();
      pdu->entityID.ID = getPlayerID();

      // ---
      // Force ID: When mapping Player side to force IDs ...
      // ---
      if (getSide() == Simulation::Player::BLUE) {
         // blue's are friendly, ...
         pdu->forceID = NetIO::FRIENDLY_FORCE;
      }
      else if (getSide() == Simulation::Player::RED) {
         // red's are not, ...
         pdu->forceID = NetIO::OPPOSING_FORCE;
      }
      else if (getSide() == Simulation::Player::WHITE) {
         // white is neutral, ...
         pdu->forceID = NetIO::NEUTRAL_FORCE;
      }
      else {
         // and everyone else is type OTHER.
         pdu->forceID = NetIO::OTHER_FORCE;
      }

      // ---
      // Entity type (EntityType)
      // ---
      pdu->entityType.kind                 = getEntityKind();
      pdu->entityType.domain               = getEntityDomain();
      pdu->entityType.country              = getEntityCountry();
      pdu->entityType.category             = getEntityCategory();
      pdu->entityType.subcategory          = getEntitySubcategory();
      pdu->entityType.specific             = getEntitySpecific();
      pdu->entityType.extra                = getEntityExtra();
      // ---
      // Alternative type (EntityType)
      // ---
      pdu->alternativeType.kind            = getEntityKind();
      pdu->alternativeType.domain          = getEntityDomain();
      pdu->alternativeType.country         = getEntityCountry();
      pdu->alternativeType.category        = getEntityCategory();
      pdu->alternativeType.subcategory     = getEntitySubcategory();
      pdu->alternativeType.specific        = getEntitySpecific();
      pdu->alternativeType.extra           = getEntityExtra();

      // ---
      // Player position and orientation state data data
      // 1) All data is geocentric (ECEF)
      // 2) The playerState2Nib() function, which was called above, captures
      //    the state data and passed the state data to the dead reckoning
      //    system, and we're using this DR captured data.
      // ---
      {
         // ---
         // Entity linear velocity (VectorDIS)
         // ---
         osg::Vec3d geocVel = getDrVelocity();
         pdu->entityLinearVelocity.component[0] = static_cast<float>(geocVel[0]);
         pdu->entityLinearVelocity.component[1] = static_cast<float>(geocVel[1]);
         pdu->entityLinearVelocity.component[2] = static_cast<float>(geocVel[2]);

         // ---
         // Entity location (WorldCoordinates)
         // ---
         osg::Vec3d geocPos = getDrPosition();
         pdu->entityLocation.X_coord = geocPos[Basic::Nav::IX];
         pdu->entityLocation.Y_coord = geocPos[Basic::Nav::IY];
         pdu->entityLocation.Z_coord = geocPos[Basic::Nav::IZ];

         // ---
         // Entity orientation (EulerAngles)
         // ---
         osg::Vec3d geocAngles = getDrEulerAngles();
         pdu->entityOrientation.phi   = static_cast<float>(geocAngles[Basic::Nav::IPHI]);
         pdu->entityOrientation.theta = static_cast<float>(geocAngles[Basic::Nav::ITHETA]);
         pdu->entityOrientation.psi   = static_cast<float>(geocAngles[Basic::Nav::IPSI]);
      }

      // ---
      // Appearance bits generic to all domains (except munitions)
      // ---
      {
         pdu->appearance = 0x0;

         // ---
         // Frozen?
         // ---
         if ( isFrozen() || sim->isFrozen() ) {
            pdu->appearance |= FROZEN_BIT;
         }

         // Deactive this entity?
         {
            if (isMode(Simulation::Player::DELETE_REQUEST) || player->isDead() )
               pdu->appearance |= DEACTIVATE_BIT;
         }


         // Damage or health?  (Bits 3-4)
         {
            unsigned int bits = 0;
            if (getDamage() > 0.9f) bits = 3;       // Destroyed or Fatality
            else if (getDamage() > 0.5) bits = 2;   // Moderate
            else if (getDamage() > 0.0) bits = 1;   // Slight
            else bits = 0;                          // None
            pdu->appearance |= (bits << 3);
         }


         // Camouflage type
         // Note: air platform appearance bits 17 and 18 are not used, but we're using them the same as land platforms
         {
            unsigned int bits = getCamouflageType();
            if (bits > 0 && bits <= 4) {
               pdu->appearance |= CAMOUFLAGE_BIT;

               // Land based camouflage bits
               if (player->isMajorType(Simulation::Player::GROUND_VEHICLE)) {
                  // Subtract one to match DIS camouflage bits.
                  // Our camouflage type for DIS is the camouflage appearance bits
                  // plus one because our camouflage type of zero is no camouflage.
                  bits--;
                  pdu->appearance |= (bits << 17);
               }
            }
         }

         // Life forms appearance bits
         if (player->isMajorType(Simulation::Player::LIFE_FORM)) {
            const Simulation::LifeForm* lf = dynamic_cast<const Simulation::LifeForm*>(player);
            if (lf != nullptr) {
               // Health (aka damaged for other domains) same bits (3-4) - this is from the NIB, because it IS
               // updated
               // bits 5-8 compliance (not implemented)
               // bits 9 - 11 unused
               // bit 12 flashlight (not implemented)
               // bits 13-15 unused
               // bits 16 - 19 life form state
               // data is from the player, because NIB doesn't have actions associated with it
               {
                  unsigned int bits = 1;      // upright, standing still
                  if (lf->getActionState() == Simulation::LifeForm::UPRIGHT_STANDING) bits = 1;       // standing
                  else if (lf->getActionState() == Simulation::LifeForm::UPRIGHT_WALKING) bits = 2;   // walking
                  else if (lf->getActionState() == Simulation::LifeForm::UPRIGHT_RUNNING) bits = 3;   // running
                  else if (lf->getActionState() == Simulation::LifeForm::KNEELING) bits = 4;          // kneeling
                  else if (lf->getActionState() == Simulation::LifeForm::PRONE) bits = 5;             // prone
                  else if (lf->getActionState() == Simulation::LifeForm::CRAWLING) bits = 6;          // crawling
                  else if (lf->getActionState() == Simulation::LifeForm::SWIMMING) bits = 7;          // swimming
                  else if (lf->getActionState() == Simulation::LifeForm::PARACHUTING) bits = 8;       // parachuting
                  else if (lf->getActionState() == Simulation::LifeForm::JUMPING) bits = 9;           // jumping
                  else if (lf->getActionState() == Simulation::LifeForm::SITTING) bits = 10;          // sitting
                  else if (lf->getActionState() == Simulation::LifeForm::SQUATTING) bits = 11;        // squatting
                  else if (lf->getActionState() == Simulation::LifeForm::CROUCHING) bits = 12;        // crouching
                  else if (lf->getActionState() == Simulation::LifeForm::WADING) bits = 13;           // wading
                  else if (lf->getActionState() == Simulation::LifeForm::SURRENDER) bits = 14;        // surrender
                  else if (lf->getActionState() == Simulation::LifeForm::DETAINED) bits = 15;         // detained
                  else bits = 1;
                  pdu->appearance |= (bits << 16);
               }
               // bit 20 unused
               // bit 21 frozen status (taken care of above)
               // bits 24 - 25 weapon 1 (not implemented)
               // bits 26-27 weapon 2 (N/I)
               // bits 28-29
            }
         }

         // Common Non-life form appearance bits
         else {

            // Smoking?  (Bits 5-6) Standard (IST-CF-03-01, May 5, 2003)
            {
               unsigned int bits = 0;
               if (getSmoke() > 0.9f) bits = 3;
               else if (getSmoke() > 0.5) bits = 2;
               else if (getSmoke() > 0.0) bits = 1;
               else bits = 0;
               pdu->appearance |= (bits << 5);
            }

            // Flames? (Bit 15) Standard (IST-CF-03-01, May 5, 2003)
            {
               if (getFlames() > 0.5f)
                  pdu->appearance |= FLAMES_BIT;
            }

            // Power plant status bit (just leave ON for now)
            pdu->appearance |= POWER_PLANT_BIT;
         }


      }

      // ---
      // Dead reckoning algorithm
      // ---
      pdu->deadReckoningAlgorithm = static_cast<unsigned char>(getDeadReckoning());

      // ---
      // Other parameters
      // ---
      for (unsigned int i=0; i<15; i++) {
          pdu->otherParameters[i] = 0;
      }

      // ---
      // Dead reckoning information
      // ---
      {
         // ---
         // Dead reckoning linear acceleration (VectorDIS)
         // ---
         osg::Vec3d geocAcc = getDrAcceleration();
         pdu->DRentityLinearAcceleration.component[0] = static_cast<float>(geocAcc[0]);
         pdu->DRentityLinearAcceleration.component[1] = static_cast<float>(geocAcc[1]);
         pdu->DRentityLinearAcceleration.component[2] = static_cast<float>(geocAcc[2]);

         // ---
         // Dead reckoning angular velocity (AngularVelocityVectorDIS)
         // ---
         osg::Vec3d geocAngVel = getDrAngularVelocities();
         pdu->DRentityAngularVelocity.x_axis = static_cast<float>(geocAngVel[Basic::Nav::IX]);
         pdu->DRentityAngularVelocity.y_axis = static_cast<float>(geocAngVel[Basic::Nav::IY]);
         pdu->DRentityAngularVelocity.z_axis = static_cast<float>(geocAngVel[Basic::Nav::IZ]);
      }

      // ---
      // Entity marking (EntityMarking)
      // ---
      {
         const char* const pName = getPlayerName();
         size_t nameLen = std::strlen(pName);
         for (unsigned int i = 0; i < EntityMarking::BUFF_SIZE; i++) {
            if (i < nameLen) {
               pdu->entityMarking.marking[i] = pName[i];
            }
            else {
               pdu->entityMarking.marking[i] = '\0';
            }
         }
         pdu->entityMarking.characterSet = 1;
      }

      // ---
      // Capabilities
      // ---
      pdu->capabilites = 0x0;

      // ---
      // Articulation parameters
      // ---
      pdu->numberOfArticulationParameters = manageArticulationParameters(pdu);

      // Size of the PDU package
      unsigned short length = sizeof(EntityStatePDU) + (pdu->numberOfArticulationParameters * sizeof(VpArticulatedPart));
      pdu->header.length = length;

      if (Basic::NetHandler::isNotNetworkByteOrder()) pdu->swapBytes();
      ok = disIO->sendData( reinterpret_cast<char*>(pdu), length );
   }
   return ok;
}
//------------------------------------------------------------------------------
// updateBasicEntity() -- (Output support)
//    -- sets the BasicEntity attribute values that need to be updated
//
//    -- We send all published BasicEntity attributes every time.
//
//       The function isPlayerStateUpdateRequired() checking if the player's state
//       needs to be resent.  Therefore, we're not check the individual update
//       required flags with isAttributeUpdateRequired(), but we do clear them
//       with setAttributeUpdateRequiredFlag().
//
//   (Also handles the host to network byte swapping)
//------------------------------------------------------------------------------
void Nib::updateBasicEntity(
         RTI::AttributeHandleValuePairSet* attrs, 
         const double)
{
   if (baseEntity != nullptr) {
      // Our handler 
      NetIO* netIO = static_cast<NetIO*>(getNetIO());

      // Our simulation
      simulation::Simulation* ourSim = netIO->getSimulation();

      // Get the player data
      playerState2Nib();

      // Entity ID
      if (isAttributeUpdateEnabled(NetIO::ENTITY_IDENTIFIER_AI)) {
         EntityIdentifierStruct* entityId = &baseEntity->entityIdentifier;
         entityId->federateIdentifier.siteID = getSiteID();
         entityId->federateIdentifier.applicationID = getApplicationID();
         entityId->entityNumber = getPlayerID();
         //std::cout << "Send Federate ID: (" << entityId->entityNumber << "," << entityId->federateIdentifier.applicationID << "," << entityId->federateIdentifier.siteID << ")" << std::endl;

         EntityIdentifierStruct netBuffer;
         base::NetHandler::toNetOrder(&netBuffer.federateIdentifier.applicationID, entityId->federateIdentifier.applicationID );
         base::NetHandler::toNetOrder(&netBuffer.federateIdentifier.siteID, entityId->federateIdentifier.siteID );
         base::NetHandler::toNetOrder(&netBuffer.entityNumber, entityId->entityNumber );
         attrs->add(netIO->getObjectAttributeHandle(
               NetIO::ENTITY_IDENTIFIER_AI),
               reinterpret_cast<char*>(&netBuffer),
               sizeof(EntityIdentifierStruct)
            );

         setAttributeUpdateRequiredFlag(NetIO::ENTITY_IDENTIFIER_AI, false);
      }

      // Entity Type
      if (isAttributeUpdateEnabled(NetIO::ENTITY_TYPE_AI)) {

         EntityTypeStruct* entityType = &baseEntity->entityType;
         entityType->entityKind   = getEntityKind();
         entityType->domain       = getEntityDomain();
         entityType->countryCode  = getEntityCountry();
         entityType->category     = getEntityCategory();
         entityType->subcategory  = getEntitySubcategory();
         entityType->specific     = getEntitySpecific();
         entityType->extra        = getEntityExtra();

         // Network byte order: all bytes except country code which is unsigned short.
         EntityTypeStruct netBuffer = *entityType;
         base::NetHandler::toNetOrder(&netBuffer.countryCode, entityType->countryCode );
         attrs->add(netIO->getObjectAttributeHandle(
               NetIO::ENTITY_TYPE_AI),
               reinterpret_cast<char*>(&netBuffer),
               sizeof(EntityTypeStruct)
            );

         setAttributeUpdateRequiredFlag(NetIO::ENTITY_TYPE_AI, false);
      }

      // Spatial Structure
      if (isAttributeUpdateEnabled(NetIO::SPATIAL_AI)) {

         osg::Vec3d pos = getDrPosition();
         osg::Vec3d vel = getDrVelocity();
         osg::Vec3d accel = getDrAcceleration();
         osg::Vec3d angles = getDrEulerAngles();
         osg::Vec3d arates = getDrAngularVelocities();

         // NIB's base entity structures
         SpatialStruct* spatial = &(baseEntity->spatial);
         SpatialRVStruct* spatialRvw = &(baseEntity->spatialRvw);

         // Net order buffer (used to send the attribute to the RTI)
         const unsigned int SPATIAL_NET_BUFFER_SIZE = sizeof(SpatialStruct) + sizeof(SpatialRVStruct);
         unsigned char netBuffer[SPATIAL_NET_BUFFER_SIZE];

         SpatialStruct* netSpatial = reinterpret_cast<SpatialStruct*>(&netBuffer[0]);
         SpatialRVStruct* netSpatialRvw = reinterpret_cast<SpatialRVStruct*>(&netBuffer[sizeof(SpatialStruct)]);

         // Ref Position 
         double refLat = 0.0;
         double refLon = 0.0;
         if (ourSim != nullptr) {
            refLat = ourSim->getRefLatitude();
            refLon = ourSim->getRefLongitude();
         }

         // Convert position vector to Lat/Lon/Alt
         double alt = 0.0;
         double simCoord[3] = { 0.0, 0.0, 0.0 };
         base::Nav::convertPosVec2LL(
               refLat, refLon, 
               pos,
               &simCoord[base::Nav::ILAT], &simCoord[base::Nav::ILON], &alt
            );
         simCoord[base::Nav::IALT] = alt;

         //std::cout << "RprFom::Nib::entityState2Nib(): simCoord(" << simCoord[Basic::Nav::ILAT] << "," << simCoord[Basic::Nav::ILON] << "," << simCoord[Basic::Nav::IALT] << ")"  << std::endl;

         // Convert to geocentric coordinates
         double geocPos[3] = { 0.0, 0.0, 0.0 };
         double geocVel[3] = { 0.0, 0.0, 0.0 };
         double geocAcc[3] = { 0.0, 0.0, 0.0 };
         base::Nav::getWorldPosAccVel(simCoord, vel.ptr(), accel.ptr(), geocPos, geocVel, geocAcc);

         // Dead reckoning algorithm
         {
            spatial->deadReckoningAlgorithm = DRM_RVW;

            netSpatial->deadReckoningAlgorithm = spatial->deadReckoningAlgorithm;
         }

         // Is Frozen?
         {
            bool simFrz = false;
            if (ourSim != nullptr) simFrz = ourSim->isFrozen();
            if (isFrozen() || simFrz) spatialRvw->isFrozen = RTI::RTI_TRUE;  // Is this object or the simulation frozen?
            else spatialRvw->isFrozen = RTI::RTI_FALSE;

            netSpatialRvw->isFrozen = spatialRvw->isFrozen;
         }

         // World Location
         {
            WorldLocationStruct* worldLocation = &spatialRvw->worldLocation;
            WorldLocationStruct* netWorldLocation = &netSpatialRvw->worldLocation;

            worldLocation->x = geocPos[base::Nav::IX];
            worldLocation->y = geocPos[base::Nav::IY];
            worldLocation->z = geocPos[base::Nav::IZ];

            base::NetHandler::toNetOrder(&netWorldLocation->x, worldLocation->x);
            base::NetHandler::toNetOrder(&netWorldLocation->y, worldLocation->y);
            base::NetHandler::toNetOrder(&netWorldLocation->z, worldLocation->z);
         }

         // Velocity vector
         {
            VelocityVectorStruct* velocityVector = &spatialRvw->velocityVector;
            VelocityVectorStruct* netVelocityVector = &netSpatialRvw->velocityVector;

            velocityVector->xVelocity = static_cast<RTI::Float>(geocVel[base::Nav::IX]);
            velocityVector->yVelocity = static_cast<RTI::Float>(geocVel[base::Nav::IY]);
            velocityVector->zVelocity = static_cast<RTI::Float>(geocVel[base::Nav::IZ]);

            base::NetHandler::toNetOrder(&netVelocityVector->xVelocity, velocityVector->xVelocity);
            base::NetHandler::toNetOrder(&netVelocityVector->yVelocity, velocityVector->yVelocity);
            base::NetHandler::toNetOrder(&netVelocityVector->zVelocity, velocityVector->zVelocity);
         }

         // Acceleration vector
         {
            AccelerationVectorStruct* accelerationVector = &spatialRvw->accelerationVector;
            AccelerationVectorStruct* netAccelerationVector = &netSpatialRvw->accelerationVector;

            accelerationVector->xAcceleration = static_cast<RTI::Float>(geocAcc[base::Nav::IX]);
            accelerationVector->yAcceleration = static_cast<RTI::Float>(geocAcc[base::Nav::IY]);
            accelerationVector->zAcceleration = static_cast<RTI::Float>(geocAcc[base::Nav::IZ]);

            base::NetHandler::toNetOrder(&netAccelerationVector->xAcceleration, accelerationVector->xAcceleration);
            base::NetHandler::toNetOrder(&netAccelerationVector->yAcceleration, accelerationVector->yAcceleration);
            base::NetHandler::toNetOrder(&netAccelerationVector->zAcceleration, accelerationVector->zAcceleration);
         }

         // Orientation
         {
            OrientationStruct* orientation = &spatialRvw->orientation;
            OrientationStruct* netOrientation = &netSpatialRvw->orientation;

            // Convert Euler angles to geocentric angles
            double geocAngles[3] = { 0.0, 0.0, 0.0 };
            base::Nav::getGeocAngle(simCoord, angles.ptr(), geocAngles);

            orientation->phi   = static_cast<RTI::Float>(geocAngles[base::Nav::IPHI]);
            orientation->theta = static_cast<RTI::Float>(geocAngles[base::Nav::ITHETA]);
            orientation->psi   = static_cast<RTI::Float>(geocAngles[base::Nav::IPSI]);

            base::NetHandler::toNetOrder(&netOrientation->phi, orientation->phi);
            base::NetHandler::toNetOrder(&netOrientation->theta, orientation->theta);
            base::NetHandler::toNetOrder(&netOrientation->psi, orientation->psi);
         }

         // Angular velocity vector (all zeros for now)
         {
            AngularVelocityVectorStruct* angularVelocityVector = &spatialRvw->angularVelocity;
            AngularVelocityVectorStruct* netAngularVelocityVector = &netSpatialRvw->angularVelocity;

            angularVelocityVector->xAngularVelocity = 0;
            angularVelocityVector->yAngularVelocity = 0;
            angularVelocityVector->zAngularVelocity = 0;

            base::NetHandler::toNetOrder(&netAngularVelocityVector->xAngularVelocity, angularVelocityVector->xAngularVelocity);
            base::NetHandler::toNetOrder(&netAngularVelocityVector->yAngularVelocity, angularVelocityVector->yAngularVelocity);
            base::NetHandler::toNetOrder(&netAngularVelocityVector->zAngularVelocity, angularVelocityVector->zAngularVelocity);
         }

         attrs->add(
               netIO->getObjectAttributeHandle(NetIO::SPATIAL_AI),
               reinterpret_cast<char*>(&netBuffer),
               SPATIAL_NET_BUFFER_SIZE
            );

         setAttributeUpdateRequiredFlag(NetIO::SPATIAL_AI, false);
      }
   }
}