/**
  * Creates an new MetaData or replaces its data if it already exists.
  *
  * This method constructs a MetaData without value, with the label @a 
  * label, an empty description and is unlocked.
  *
  * @ingroup BTKCommon
  */
 MetaData::Pointer MetaDataCreateChild(MetaData::Pointer parent, const std::string& label)
 {
   MetaData::Pointer entry = MetaData::New(label);
   if (parent != MetaData::Null)
   {
     MetaData::Iterator it = parent->FindChild(label);
     if (it != parent->End())
     {
       (*it)->SetDescription("");
       (*it)->SetInfo(MetaDataInfo::Pointer());
       (*it)->SetUnlockState(true);
       return (*it);
     }
     else
     {
       parent->AppendChild(entry);
       return entry;
     }
   }
   else
   {
     btkWarningMacro("No parent.");
     return entry;
   }
 };
 /**
  * Sets the threshold value.
  *
  * The threshold must be activated (see GroundReactionWrenchFilter::SetThresholdState) to be used during the computation of the PWA.
  */
 void GroundReactionWrenchFilter::SetThresholdValue(double v)
 {
   if (fabs(this->m_ThresholdValue - v) <= std::numeric_limits<double>::epsilon())
     return;
   if (v < 0.0)
     btkWarningMacro("Negative threshold has no effect on the algorithm because it compares the threshold value with the absolute value of Fz.");
   this->m_ThresholdValue = v;
   this->Modified();
 };
 /**
  * Finish the computation of the ground reaction wrench for the Kislter force platform.
  */
 void GroundReactionWrenchFilter::FinishKistler(Wrench::Pointer wrh, ForcePlatform::Pointer fp, int index)
 {
   ForcePlatform::Origin origin;
   origin << 0, 0, fp->GetOrigin().z();
   if (origin.z()  > 0)
   {
     btkWarningMacro("Vertical offset between the origin of the force platform #" + ToString(index) + " and the center of the working surface seems to be misconfigured (positive value). The opposite of this offset is used.");
     origin.z() *= -1;
   }
   this->FinishGRWComputation(wrh, origin);
 };
  /**
   * Finish the computation of the ground reaction wrench for the AMTI force platforms.
   */
  void GroundReactionWrenchFilter::FinishAMTI(Wrench::Pointer wrh, ForcePlatform::Pointer fp, int index)
  {
    ForcePlatform::Origin origin = fp->GetOrigin();
    if (origin.z() > 0)
    {
      btkWarningMacro("Origin for the force platform #" + ToString(index) + " seems to be located from the center of the working surface instead of the inverse. Data are inverted to locate the center of the working surface from the platform's origin.");
      origin *= -1;
    }
    this->FinishGRWComputation(wrh, origin);

  };
 /**
  * Try to set gain from the given value @a g.
  * The input must represent one of the value associated with the enum Analog::Gain.
  * These value can be interpreted as the half ot voltage range expressed in mV.
  */
 void Analog::SetGainFromValue(int g)
 {
   switch(g)
   {
   case Analog::PlusMinus10:
   case Analog::PlusMinus5:
   case Analog::PlusMinus2Dot5:
   case Analog::PlusMinus1Dot65:
   case Analog::PlusMinus1Dot25:
   case Analog::PlusMinus1:
   case Analog::PlusMinus0Dot5:  
   case Analog::PlusMinus0Dot25:
   case Analog::PlusMinus0Dot1:
   case Analog::PlusMinus0Dot05:
     this->SetGain(static_cast<Analog::Gain>(g));
     break;
   default:
     btkWarningMacro("Unknown gain. Replaced by a gain of +/- 10 volts.");
     this->SetGain(Analog::PlusMinus10);
     break;
   }
 };
  /**
   * Read the file designated by @a filename and fill @a output.
   */
  void ANCFileIO::Read(const std::string& filename, Acquisition::Pointer output)
  {
    output->Reset();
    // Open the stream
    std::ifstream ifs;
    ifs.exceptions(std::ios_base::eofbit | std::ios_base::failbit | std::ios_base::badbit);
    try
    {
      std::string line;
      ifs.open(filename.c_str());
    // Check the first header keyword: "File_Type:"
      std::getline(ifs, line);
      if (line.substr(0,41).compare("File_Type:	Analog R/C ASCII	Generation#:	") != 0)
        throw(ANCFileIOException("Invalid ANC file."));
    // Check the file generation.
      this->m_Generation = FromString<int>(line.substr(41,1));
      if ((this->m_Generation != 1) && (this->m_Generation != 2))
        throw(ANCFileIOException("Unknown ANC file generation: " + line.substr(42,43) + "."));
    // Extract header data
      // Board_Type & Polarity
      std::getline(ifs, line);
      std::string boardType = this->ExtractKeywordValue(line, "Board_Type:	");
      std::string polarity = this->ExtractKeywordValue(line, "Polarity:	");
      // Trial_Name, Trial#, Duration(Sec.), #Channels
      std::getline(ifs, line);
      double duration = FromString<double>(this->ExtractKeywordValue(line, "Duration(Sec.):	"));
      size_t numberOfChannels = FromString<size_t>(this->ExtractKeywordValue(line, "#Channels:	"));

      // BitDepth & PreciseRate
      std::getline(ifs, line);
      int bitDepth = FromString<int>(this->ExtractKeywordValue(line, "BitDepth:	"));
      double preciseRate = FromString<double>(this->ExtractKeywordValue(line, "PreciseRate:	"));
      // Four next lines are empty
      std::getline(ifs, line);
      std::getline(ifs, line);
      std::getline(ifs, line);
      std::getline(ifs, line);

      // DEVELOPER CHECK
      // Check polarity's value. Only Bipolar is supported for the moment.
      if (polarity.compare("Bipolar") != 0)
        throw(ANCFileIOException("Unsupported ANC file. Only Bipolar board type is supported for the moment. Please, send an email to the developers to explain the problem."));

    // Extract data       
      if (numberOfChannels != 0)
      {
        // Analog channels' label
        std::list<std::string> labels, rates, ranges;
        std::getline(ifs, line);
        this->ExtractDataInfo(line, "Name", labels);
        size_t numberOfLabels = labels.size();
        if (numberOfChannels != numberOfLabels)
        {
          btkWarningMacro(filename, "Mismatch between the number of analog channels and the number of labels extracted. Final number of analog channels corresponds to the number of labels extracted.");
          numberOfChannels = numberOfLabels;
        }
        // Analog channels' rate
        std::getline(ifs, line);
        this->ExtractDataInfo(line, "Rate", rates);
        // Analog channels' range
        std::getline(ifs, line);
        this->ExtractDataInfo(line, "Range", ranges);
        double nf = duration * preciseRate; // Must be separate in two step due to some rounding errors
        size_t numberOfFrames = static_cast<size_t>(nf) + 1;
        // Data conversion
        std::vector<std::string> channelLabel(labels.size());
        std::vector<uint16_t> channelRate(rates.size());
        std::vector<uint16_t> channelRange(ranges.size());
        int inc = 0;
        for (std::list<std::string>::const_iterator it = rates.begin() ; it != rates.end() ; ++it)
          FromString(*it, channelRate[inc++]);
        inc = 0;
        for (std::list<std::string>::const_iterator it = ranges.begin() ; it != ranges.end() ; ++it)
          FromString(*it, channelRange[inc++]);
        inc = 0;
        for (std::list<std::string>::const_iterator it = labels.begin() ; it != labels.end() ; ++it)
          channelLabel[inc++] = *it;

        ANxFileIOCheckHeader_p(preciseRate, numberOfChannels, channelRate, channelRange);
        ANxFileIOStoreHeader_p(output, filename, preciseRate, numberOfFrames, numberOfChannels, channelLabel, channelRate, channelRange, boardType, bitDepth, this->m_Generation);
        
        // Extract values
        std::string buf;
        double val = 0.0;
        for(size_t i = 0 ; i < numberOfFrames ; ++i)
        {
          ifs >> buf; // Time's value          
          for (AnalogCollection::Iterator it = output->BeginAnalog() ; it != output->EndAnalog() ; ++it)
          {
            ifs >> val;
            (*it)->GetValues().coeffRef(static_cast<int>(i)) = val * (*it)->GetScale();
          }
        }
      }
    // Add a metadata to notify that the first frame was not set.
      MetaData::Pointer btkPointConfig = MetaDataCreateChild(output->GetMetaData(), "BTK_POINT_CONFIG");
      MetaDataCreateChild(btkPointConfig, "NO_FIRST_FRAME", static_cast<int8_t>(1));
    }
 /**
  * Read the file designated by @a filename and fill @a output.
  */
 void BSFFileIO::Read(const std::string& filename, Acquisition::Pointer output)
 {
   output->Reset();
   IEEELittleEndianBinaryFileStream bifs;
   bifs.SetExceptions(BinaryFileStream::EndFileBit | BinaryFileStream::FailBit | BinaryFileStream::BadBit);
   try
   {
     bifs.Open(filename, BinaryFileStream::In);
     // Main header (SMnHeaderTag)
     if (bifs.ReadI32() != 100)
       throw BSFFileIOException("Invalid BSF file.");
     int32_t headerSize = bifs.ReadI32();
     int32_t numberOfActivePlatforms = bifs.ReadI32();
     int32_t numberOfActiveInstruments = bifs.ReadI32();
     std::string subjectName = btkTrimString(bifs.ReadString(100));
     std::string testDate = btkTrimString(bifs.ReadString(12));
     std::string subjectDateOfBirth = btkTrimString(bifs.ReadString(12));
     double weight = bifs.ReadDouble();
     double height = bifs.ReadDouble();
     std::string sex = btkTrimString(bifs.ReadString(1));
     bifs.SeekRead(3, BinaryFileStream::Current); // Because of "pragma pack(4)": minimum of 4 bytes in the alignment of a member.
     int32_t numberOfTrials = bifs.ReadI32();
     double totaleTimeTrial = bifs.ReadDouble(); // seconds
     int32_t zeroMethod = bifs.ReadI32();
     int32_t weightMethod = bifs.ReadI32();
     int32_t delayAfterKeystroke = bifs.ReadI32();
     int32_t triggerMethod = bifs.ReadI32();
     int32_t triggerPlatform = bifs.ReadI32();
     int32_t preTriggerValue = bifs.ReadI32();
     int32_t postTriggerValue = bifs.ReadI32();
     double triggerValue = bifs.ReadDouble();
     bifs.SeekRead(4, BinaryFileStream::Current); // FIXME: There is 4 extra bytes in the file used to test this reader! What are they?
     int32_t rate = bifs.ReadI32();
     std::string protocol = btkTrimString(bifs.ReadString(150));
     std::string testType = btkTrimString(bifs.ReadString(200));
     std::string commentFile = btkTrimString(bifs.ReadString(150));
     std::string trialDescriptionFile = btkTrimString(bifs.ReadString(150));
     std::string examinerName = btkTrimString(bifs.ReadString(100));
     bifs.SeekRead(2, BinaryFileStream::Current); // WARNING: Two (2) extra bytes in the file tested. Could be for alignment?
     int32_t units = bifs.ReadI32(); // 0: english, 1: metric
     
     if (rate == 0)
       throw BSFFileIOException("Invalid frame rate.");
     
     // Instrument headers (SInstrHeaderTag)
     std::vector<InstrumentHeader> instrumentHeaders(numberOfActivePlatforms + numberOfActiveInstruments);
     for (int i = 0 ; i < numberOfActivePlatforms ; ++i)
       this->extractInstrumentHeader(&bifs, &(instrumentHeaders[i]));
     for (int i = 0 ; i < numberOfActiveInstruments ; ++i)
       this->extractInstrumentHeader(&bifs, &(instrumentHeaders[i+numberOfActivePlatforms]));
     
     // Internal check to verify the compatibility between BTK acquisition's format and data
     for (size_t i = 0 ; i < instrumentHeaders.size() ; ++i)
     {
       if ((instrumentHeaders[i].rate != 0) && (instrumentHeaders[i].rate != rate))
         throw BSFFileIOException("Unsupported file: An instrument has a different rate. Contact the developers to improve this reader.");
     }
     for (int i = 0 ; i < numberOfActivePlatforms ; ++i)
     {
       if (instrumentHeaders[i].numberOfChannels != 6)
         throw BSFFileIOException("Unsupported file: A force platform has more than 6 channels. Contact the developers to improve this reader.");
     }
     
     // Init
     int totalNumberOfChannels = 0;
     for (size_t i = 0 ; i < instrumentHeaders.size() ; ++i)
       totalNumberOfChannels += instrumentHeaders[i].numberOfChannels;
     double* scale = new double[totalNumberOfChannels]; // array to transform ADC values to real values
     size_t inc = 0;
     for (size_t i = 0 ; i < instrumentHeaders.size() ; ++i)
     {
       for (int j = 0 ; j < instrumentHeaders[i].numberOfChannels ; ++j)
         scale[j+inc] = 1000000.0 / (instrumentHeaders[i].sensitivity[j] * instrumentHeaders[i].amplifierGain[j] * instrumentHeaders[i].excitationVoltage[j] * instrumentHeaders[i].acquisitionCardRange[j]);
       inc += instrumentHeaders[i].numberOfChannels;
     }
     output->Init(0, static_cast<int>(totaleTimeTrial * static_cast<double>(rate)), totalNumberOfChannels);
     output->SetPointFrequency(static_cast<double>(rate));
     if (units == 0) // english
     {
       output->SetPointUnit(Point::Marker, "in");
       output->SetPointUnit(Point::Force, "lb");
       output->SetPointUnit(Point::Moment, "in-lb");
     }
     else
     {
       // The FP length/width seems to be set everytime in inch even if the header is set 
       // to metric units (so only used for measured value?).
       for (size_t i = 0 ; i < instrumentHeaders.size() ; ++i)
       {
          // Convert from inch to meter
         instrumentHeaders[i].length *= 0.0254f;
         instrumentHeaders[i].width *= 0.0254f;
         instrumentHeaders[i].offset[0] *= 0.0254f;
         instrumentHeaders[i].offset[1] *= 0.0254f;
         instrumentHeaders[i].offset[2] *= 0.0254f;
       }
       // Same for the content of the measure. The BSF file format seems to save the data in pound and pound-inch
       for (int i = 0 ; i < numberOfActivePlatforms ; ++i)
       {
         scale[i*6] *= 4.4482216152605;
         scale[i*6+1] *= 4.4482216152605;
         scale[i*6+2] *= 4.4482216152605;
         scale[i*6+3] *= 0.1129848290276167;
         scale[i*6+4] *= 0.1129848290276167;
         scale[i*6+5] *= 0.1129848290276167;
       }
       output->SetPointUnit(Point::Marker, "m");
       output->SetPointUnit(Point::Force, "N");
       output->SetPointUnit(Point::Moment, "Nm");
     }
     Acquisition::AnalogIterator it = output->BeginAnalog();
     std::vector<float> corners(12*numberOfActivePlatforms, 0.0f);
     std::vector<float> origin(3*numberOfActivePlatforms, 0.0f);
     std::vector<int16_t> channel(6*numberOfActivePlatforms);
     std::string suffix = ((numberOfActivePlatforms == 1) ? "" : "1");
     float globalOrigin[3] = {0.0f, 0.0f, 0.0f};
     int numToAdaptChannelIndex = 0;
     for (int i = 0 ; i < numberOfActivePlatforms ; ++i)
     {
       (*it)->SetLabel("Fx" + suffix);
       (*it)->SetUnit(output->GetPointUnit(Point::Force));
       (*it)->SetScale(scale[i*6]);
       ++it;
       (*it)->SetLabel("Fy" + suffix);
       (*it)->SetUnit(output->GetPointUnit(Point::Force));
       (*it)->SetScale(scale[i*6+1]);
       ++it;
       (*it)->SetLabel("Fz" + suffix);
       (*it)->SetUnit(output->GetPointUnit(Point::Force));
       (*it)->SetScale(scale[i*6+2]);
       ++it;
       (*it)->SetLabel("Mx" + suffix);
       (*it)->SetUnit(output->GetPointUnit(Point::Moment));
       (*it)->SetScale(scale[i*6+3]);
       ++it;
       (*it)->SetLabel("My" + suffix);
       (*it)->SetUnit(output->GetPointUnit(Point::Moment));
       (*it)->SetScale(scale[i*6+4]);
       ++it;
       (*it)->SetLabel("Mz" + suffix);
       (*it)->SetUnit(output->GetPointUnit(Point::Moment));
       (*it)->SetScale(scale[i*6+5]);
       ++it;
       if (i > 0)
       {
         if ((instrumentHeaders[i].interDistance[0] == 0.0f) && (instrumentHeaders[i].interDistance[1] == 0.0f) && (instrumentHeaders[i].interDistance[2] == 0.0f))
         {
           btkWarningMacro(filename, "The distance with the previous force platform is set to 0. The platform is automatically shifted in the front of the previous. You might have to modify the origin of the force platform #" + ToString(i) + "in the metadata FORCE_PLATFORM:ORIGIN to locate it correctly in the global frame.");
           instrumentHeaders[i].interDistance[1] = static_cast<float>(instrumentHeaders[i].length + instrumentHeaders[i-1].length) / 2.0f;
         }
       }
       globalOrigin[0] += instrumentHeaders[i].interDistance[0];
       globalOrigin[1] += instrumentHeaders[i].interDistance[1];
       globalOrigin[2] += instrumentHeaders[i].interDistance[2];
       this->extractConfiguration(&(instrumentHeaders[i]), globalOrigin, &(channel[i*6]), &(corners[i*12]), &(origin[i*3]));
       for (int j = 0 ; j < 6 ; ++j)
         channel[i*6+j] += numToAdaptChannelIndex;
       numToAdaptChannelIndex += instrumentHeaders[i].numberOfChannels;
       suffix = ToString(i+2);
     }
     // Create the metadata FORCE_PLATFORM
     btk::MetaData::Pointer forcePlatform = btk::MetaData::New("FORCE_PLATFORM");
     output->GetMetaData()->AppendChild(forcePlatform);
     // - FORCE_PLATFORM:USED
     forcePlatform->AppendChild(btk::MetaData::New("USED", static_cast<int16_t>(numberOfActivePlatforms)));
     // - FORCE_PLATFORM:TYPE
     forcePlatform->AppendChild(btk::MetaData::New("TYPE", std::vector<int16_t>(numberOfActivePlatforms,2)));
     // - FORCE_PLATFORM:ZERO
     std::vector<int16_t> zero(2,0); zero[0] = 1;
     forcePlatform->AppendChild(btk::MetaData::New("ZERO", zero));
     // - FORCE_PLATFORM:CORNERS
     std::vector<uint8_t> dims(3); dims[0] = 3; dims[1] = 4; dims[2] = numberOfActivePlatforms;
     forcePlatform->AppendChild(btk::MetaData::New("CORNERS", dims, corners));
     // - FORCE_PLATFORM:ORIGIN
     dims.resize(2); dims[0] = 3; dims[1] = numberOfActivePlatforms;
     forcePlatform->AppendChild(btk::MetaData::New("ORIGIN", dims, origin));
     // - FORCE_PLATFORM:CHANNEL
     dims.resize(2); dims[0] = 6; dims[1] = numberOfActivePlatforms;
     forcePlatform->AppendChild(btk::MetaData::New("CHANNEL", dims, channel));
     // - FORCE_PLATFORM:CAL_MATRIX
     dims.resize(3); dims[0] = 6; dims[1] = 6; dims[2] = 0;
     forcePlatform->AppendChild(btk::MetaData::New("CAL_MATRIX", dims, std::vector<float>()));
     // Add a metadata to notify that the first frame was not set.
     MetaData::Pointer btkPointConfig = MetaDataCreateChild(output->GetMetaData(), "BTK_POINT_CONFIG");
     MetaDataCreateChild(btkPointConfig, "NO_FIRST_FRAME", static_cast<int8_t>(1));
     
     // Data
     // Note: We want the reaction of the measure, so all the data are multiplied by -1.
     for (int i = 0 ; i < output->GetAnalogFrameNumber() ; ++i)
     {
       int inc = 0;
       for (Acquisition::AnalogIterator it = output->BeginAnalog() ; it != output->EndAnalog() ; ++it)
         (*it)->GetValues().coeffRef(i) = -1.0f * static_cast<double>(bifs.ReadI16()) * scale[inc++]; 
     }
     
     // Cleaning
     delete[] scale;
   }
   catch (BinaryFileStreamFailure& )
   {
     std::string excmsg; 
     if (bifs.EndFile())
       excmsg = "Unexpected end of file.";
     else if (!bifs.IsOpen())
       excmsg = "Invalid file path.";
     else if(bifs.Bad())
       excmsg = "Loss of integrity of the file stream.";
     else if(bifs.Fail())
       excmsg = "Internal logic operation error on the stream associated with the file.";
     else
       excmsg = "Unknown error associated with the file stream.";
     
     if (bifs.IsOpen()) bifs.Close();
     throw(BSFFileIOException(excmsg));
   }
   catch (BSFFileIOException& )
   {
     if (bifs.IsOpen()) bifs.Close();
     throw;
   }
   catch (std::exception& e)
   {
     if (bifs.IsOpen()) bifs.Close();
     throw(BSFFileIOException("Unexpected exception occurred: " + std::string(e.what())));
   }
   catch(...)
   {
     if (bifs.IsOpen()) bifs.Close();
     throw(BSFFileIOException("Unknown exception"));
   }
 };