/* Distributed under the Apache License, Version 2.0.
   See accompanying NOTICE file for details.*/

#include "stdafx.h"
#include "io/protobuf/PBPulseConfiguration.h"
#include "io/protobuf/PBEngine.h"
#include "io/protobuf/PBEnvironment.h"
#include "io/protobuf/PBPatientNutrition.h"
#include "io/protobuf/PBProperties.h"
#include "io/protobuf/PBUtils.h"
#include "bind/cpp/pulse/PulseConfiguration.pb.h"
#include "PulseConfiguration.h"
#include "patient/SENutrition.h"
#include "engine/SEDynamicStabilization.h"
#include "engine/SETimedStabilization.h"
#include "engine/SEAutoSerialization.h"
#include "system/environment/SEEnvironmentalConditions.h"
#include "properties/SEScalarMassPerTime.h"
#include "utils/FileUtils.h"

using namespace pulse::proto;


void PBPulseConfiguration::Merge(const PulseConfiguration& src, PulseConfiguration& dst)
{
  ConfigurationData data;
  PBPulseConfiguration::Serialize(src, data);
  PBPulseConfiguration::Serialize(data, dst,true);
}

void PBPulseConfiguration::Load(const ConfigurationData& src, PulseConfiguration& dst)
{
  PBPulseConfiguration::Serialize(src, dst);
}
void PBPulseConfiguration::Serialize(const ConfigurationData& src, PulseConfiguration& dst, bool merge)
{
  if (!merge)
    dst.Clear();

  if (src.has_timestep())
    PBProperty::Load(src.timestep(), dst.GetTimeStep());
  if (src.has_timedstabilization())
    PBEngine::Load(src.timedstabilization(), dst.GetTimedStabilization());
  else if (src.has_dynamicstabilization())
    PBEngine::Load(src.dynamicstabilization(), dst.GetDynamicStabilization());
  else if (!src.stabilizationfilename().empty())
  {
    if (!dst.GetTimedStabilization().SerializeFromFile(src.stabilizationfilename(),JSON))
      if (!dst.GetDynamicStabilization().SerializeFromFile(src.stabilizationfilename(),JSON))
      {
        dst.Error("Unable to load stabilization file");
        dst.RemoveStabilization();
      }
  }
  if (src.has_autoserialization())
    PBEngine::Load(src.autoserialization(), dst.GetAutoSerialization());
  if (src.writepatientbaselinefile() != cdm::eSwitch::NullSwitch)
    dst.EnableWritePatientBaselineFile((eSwitch)src.writepatientbaselinefile());

  //Barorecptors
  if (src.has_baroreceptorconfiguration())
  {
    const ConfigurationData_BaroreceptorConfigurationData& config = src.baroreceptorconfiguration();
    if (config.has_responseslope())
      PBProperty::Load(config.responseslope(), dst.GetResponseSlope());
    if (config.has_heartratedistributedtimedelay())
      PBProperty::Load(config.heartratedistributedtimedelay(), dst.GetHeartRateDistributedTimeDelay());
    if (config.has_heartelastancedistributedtimedelay())
      PBProperty::Load(config.heartelastancedistributedtimedelay(), dst.GetHeartElastanceDistributedTimeDelay());
    if (config.has_systemicresistancedistributedtimedelay())
      PBProperty::Load(config.systemicresistancedistributedtimedelay(), dst.GetSystemicResistanceDistributedTimeDelay());
    if (config.has_venouscompliancedistributedtimedelay())
      PBProperty::Load(config.venouscompliancedistributedtimedelay(), dst.GetVenousComplianceDistributedTimeDelay());
    if (config.has_normalizedheartrateintercept())
      PBProperty::Load(config.normalizedheartrateintercept(), dst.GetNormalizedHeartRateIntercept());
    if (config.has_normalizedheartratesympatheticslope())
      PBProperty::Load(config.normalizedheartratesympatheticslope(), dst.GetNormalizedHeartRateSympatheticSlope());
    if (config.has_normalizedheartrateparasympatheticslope())
      PBProperty::Load(config.normalizedheartrateparasympatheticslope(), dst.GetNormalizedHeartRateParasympatheticSlope());
    if (config.has_normalizedheartelastanceintercept())
      PBProperty::Load(config.normalizedheartelastanceintercept(), dst.GetNormalizedHeartElastanceIntercept());
    if (config.has_normalizedheartelastancesympatheticslope())
      PBProperty::Load(config.normalizedheartelastancesympatheticslope(), dst.GetNormalizedHeartElastanceSympatheticSlope());
    if (config.has_normalizedresistanceintercept())
      PBProperty::Load(config.normalizedresistanceintercept(), dst.GetNormalizedResistanceIntercept());
    if (config.has_normalizedresistancesympatheticslope())
      PBProperty::Load(config.normalizedresistancesympatheticslope(), dst.GetNormalizedResistanceSympatheticSlope());
    if (config.has_normalizedcomplianceintercept())
      PBProperty::Load(config.normalizedcomplianceintercept(), dst.GetNormalizedComplianceIntercept());
    if (config.has_normalizedcomplianceparasympatheticslope())
      PBProperty::Load(config.normalizedcomplianceparasympatheticslope(), dst.GetNormalizedComplianceParasympatheticSlope());
  }

  // Blood Chemistry
  if (src.has_bloodchemistryconfiguration())
  {
    const ConfigurationData_BloodChemistryConfigurationData& config = src.bloodchemistryconfiguration();
    if (config.has_meancorpuscularvolume())
      PBProperty::Load(config.meancorpuscularvolume(), dst.GetMeanCorpuscularVolume());
    if (config.has_meancorpuscularhemoglobin())
      PBProperty::Load(config.meancorpuscularhemoglobin(), dst.GetMeanCorpuscularHemoglobin());
    if (config.has_standarddiffusiondistance())
      PBProperty::Load(config.standarddiffusiondistance(), dst.GetStandardDiffusionDistance());
    if (config.has_standardoxygendiffusioncoefficient())
      PBProperty::Load(config.standardoxygendiffusioncoefficient(), dst.GetStandardOxygenDiffusionCoefficient());
  }

  // Cardiovascular
  if (src.has_cardiovascularconfiguration())
  {
    const ConfigurationData_CardiovascularConfigurationData& config = src.cardiovascularconfiguration();
    if (config.has_leftheartelastancemaximum())
      PBProperty::Load(config.leftheartelastancemaximum(), dst.GetLeftHeartElastanceMaximum());
    if (config.has_leftheartelastanceminimum())
      PBProperty::Load(config.leftheartelastanceminimum(), dst.GetLeftHeartElastanceMinimum());
    if (config.has_minimumbloodvolumefraction())
      PBProperty::Load(config.minimumbloodvolumefraction(), dst.GetMinimumBloodVolumeFraction());
    if (config.has_rightheartelastancemaximum())
      PBProperty::Load(config.rightheartelastancemaximum(), dst.GetRightHeartElastanceMaximum());
    if (config.has_rightheartelastanceminimum())
      PBProperty::Load(config.rightheartelastanceminimum(), dst.GetRightHeartElastanceMinimum());
    if (config.has_standardpulmonarycapillarycoverage())
      PBProperty::Load(config.standardpulmonarycapillarycoverage(), dst.GetStandardPulmonaryCapillaryCoverage());
  }

  // Circuit
  if (src.has_circuitconfiguration())
  {
    const ConfigurationData_CircuitConfigurationData& config = src.circuitconfiguration();
    if (config.has_cardiovascularopenresistance())
      PBProperty::Load(config.cardiovascularopenresistance(), dst.GetCardiovascularOpenResistance());
    if (config.has_defaultopenelectricresistance())
      PBProperty::Load(config.defaultopenelectricresistance(), dst.GetDefaultOpenElectricResistance());
    if (config.has_defaultopenflowresistance())
      PBProperty::Load(config.defaultopenflowresistance(), dst.GetDefaultOpenFlowResistance());
    if (config.has_defaultopenheatresistance())
      PBProperty::Load(config.defaultopenheatresistance(), dst.GetDefaultOpenHeatResistance());
    if (config.has_defaultclosedelectricresistance())
      PBProperty::Load(config.defaultclosedelectricresistance(), dst.GetDefaultClosedElectricResistance());
    if (config.has_defaultclosedflowresistance())
      PBProperty::Load(config.defaultclosedflowresistance(), dst.GetDefaultClosedFlowResistance());
    if (config.has_defaultclosedheatresistance())
      PBProperty::Load(config.defaultclosedheatresistance(), dst.GetDefaultClosedHeatResistance());
    if (config.has_machineclosedresistance())
      PBProperty::Load(config.machineclosedresistance(), dst.GetMachineClosedResistance());
    if (config.has_machineopenresistance())
      PBProperty::Load(config.machineopenresistance(), dst.GetMachineOpenResistance());
    if (config.has_respiratoryclosedresistance())
      PBProperty::Load(config.respiratoryclosedresistance(), dst.GetRespiratoryClosedResistance());
    if (config.has_respiratoryopenresistance())
      PBProperty::Load(config.respiratoryopenresistance(), dst.GetRespiratoryOpenResistance());
  }

  // Constants
  if (src.has_constantsconfiguration())
  {
    const ConfigurationData_ConstantsConfigurationData& config = src.constantsconfiguration();
    if (config.has_oxygenmetabolicconstant())
      PBProperty::Load(config.oxygenmetabolicconstant(), dst.GetOxygenMetabolicConstant());
    if (config.has_stefanboltzmann())
      PBProperty::Load(config.stefanboltzmann(), dst.GetStefanBoltzmann());
    if (config.has_universalgasconstant())
      PBProperty::Load(config.universalgasconstant(), dst.GetUniversalGasConstant());
  }

  // Drugs
  if (src.has_drugsconfiguration())
  {
    const ConfigurationData_DrugsConfigurationData& config = src.drugsconfiguration();
    if (config.pdmodel() != cdm::eSwitch::NullSwitch)
      dst.UsePDModel((eSwitch)config.pdmodel());
  }

  // Energy
  if (src.has_energyconfiguration())
  {
    const ConfigurationData_EnergyConfigurationData& config = src.energyconfiguration();
    if (config.has_bodyspecificheat())
      PBProperty::Load(config.bodyspecificheat(), dst.GetBodySpecificHeat());
    if (config.has_carbondioxideproductionfromoxygenconsumptionconstant())
      PBProperty::Load(config.carbondioxideproductionfromoxygenconsumptionconstant(), dst.GetCarbonDioxideProductionFromOxygenConsumptionConstant());
    if (config.has_coretemperaturelow())
      PBProperty::Load(config.coretemperaturelow(), dst.GetCoreTemperatureLow());
    if (config.has_coretemperaturehigh())
      PBProperty::Load(config.coretemperaturehigh(), dst.GetCoreTemperatureHigh());
    if (config.has_deltacoretemperaturelow())
      PBProperty::Load(config.deltacoretemperaturelow(), dst.GetDeltaCoreTemperatureLow());
    if (config.has_energyperatp())
      PBProperty::Load(config.energyperatp(), dst.GetEnergyPerATP());
    if (config.has_sweatheattransfer())
      PBProperty::Load(config.sweatheattransfer(), dst.GetSweatHeatTransfer());
    if (config.has_vaporizationenergy())
      PBProperty::Load(config.vaporizationenergy(), dst.GetVaporizationEnergy());
    if (config.has_vaporspecificheat())
      PBProperty::Load(config.vaporspecificheat(), dst.GetVaporSpecificHeat());
  }

  // Environment
  if (src.has_environmentconfiguration())
  {
    const ConfigurationData_EnvironmentConfigurationData& config = src.environmentconfiguration();
    if (config.has_airdensity())
      PBProperty::Load(config.airdensity(), dst.GetAirDensity());
    if (config.has_airspecificheat())
      PBProperty::Load(config.airspecificheat(), dst.GetAirSpecificHeat());
    if (config.has_molarmassofdryair())
      PBProperty::Load(config.molarmassofdryair(), dst.GetMolarMassOfDryAir());
    if (config.has_molarmassofwatervapor())
      PBProperty::Load(config.molarmassofwatervapor(), dst.GetMolarMassOfWaterVapor());
    if (!config.initialconditionsfile().empty())
    {
      if (!dst.GetInitialEnvironmentalConditions().SerializeFromFile(config.initialconditionsfile(),JSON))
      {
        dst.Error("Unable to load InitialEnvironmentalConditions file");
      }
    }
    else if (config.has_initialconditions())
    {
      PBEnvironment::Load(config.initialconditions(), dst.GetInitialEnvironmentalConditions());
    }
    if (config.has_waterdensity())
      PBProperty::Load(config.waterdensity(), dst.GetWaterDensity());
  }

  // Gastrointestinal
  if (src.has_gastrointestinalconfiguration())
  {
    const ConfigurationData_GastrointestinalConfigurationData& config = src.gastrointestinalconfiguration();
    if (config.has_calciumabsorptionfraction())
      PBProperty::Load(config.calciumabsorptionfraction(), dst.GetCalciumAbsorptionFraction());
    if (config.has_calciumdigestionrate())
      PBProperty::Load(config.calciumdigestionrate(), dst.GetCalciumDigestionRate());
    if (config.has_carbohydrateabsorptionfraction())
      PBProperty::Load(config.carbohydrateabsorptionfraction(), dst.GetCarbohydrateAbsorptionFraction());
    if (config.has_defaultcarbohydratedigestionrate())
      PBProperty::Load(config.defaultcarbohydratedigestionrate(), dst.GetDefaultCarbohydrateDigestionRate());
    if (config.has_defaultfatdigestionrate())
      PBProperty::Load(config.defaultfatdigestionrate(), dst.GetDefaultFatDigestionRate());
    if (config.has_defaultproteindigestionrate())
      PBProperty::Load(config.defaultproteindigestionrate(), dst.GetDefaultProteinDigestionRate());
    if (!config.initialstomachcontentsfile().empty())
    {
      if (!dst.GetDefaultStomachContents().SerializeFromFile(config.initialstomachcontentsfile(),JSON))
      {
        dst.Error("Unable to load Standard Stomach Contents file");
      }
    }
    else if (config.has_initialstomachcontents())
    {
      PBPatientNutrition::Load(config.initialstomachcontents(), dst.GetDefaultStomachContents());
    }
    // Use default rate if they are not set
    if(dst.m_DefaultStomachContents != nullptr)
    {
      if (dst.m_DefaultStomachContents->HasCarbohydrate() && !dst.m_DefaultStomachContents->HasCarbohydrateDigestionRate())
        dst.m_DefaultStomachContents->GetCarbohydrateDigestionRate().Set(dst.GetDefaultCarbohydrateDigestionRate());
      if (dst.m_DefaultStomachContents->HasFat() && !dst.m_DefaultStomachContents->HasFatDigestionRate())
        dst.m_DefaultStomachContents->GetFatDigestionRate().Set(dst.GetDefaultFatDigestionRate());
      if (dst.m_DefaultStomachContents->HasProtein() && !dst.m_DefaultStomachContents->HasProteinDigestionRate())
        dst.m_DefaultStomachContents->GetProteinDigestionRate().Set(dst.GetDefaultProteinDigestionRate());
    }
    if (config.has_fatabsorptionfraction())
      PBProperty::Load(config.fatabsorptionfraction(), dst.GetFatAbsorptionFraction());
    if (config.has_proteintoureafraction())
      PBProperty::Load(config.proteintoureafraction(), dst.GetProteinToUreaFraction());
    if (config.has_waterdigestionrate())
      PBProperty::Load(config.waterdigestionrate(), dst.GetWaterDigestionRate());
  }

  // Nervous
  if (src.has_nervousconfiguration())
  {
    const ConfigurationData_NervousConfigurationData& config = src.nervousconfiguration();
    if (config.has_pupildiameterbaseline())
      PBProperty::Load(config.pupildiameterbaseline(), dst.GetPupilDiameterBaseline());
  }

  // Renal
  if (src.has_renalconfiguration())
  {
    const ConfigurationData_RenalConfigurationData& config = src.renalconfiguration();

    if (config.enablerenal() != cdm::eSwitch::NullSwitch)
      dst.EnableRenal((eSwitch)config.enablerenal());

    if (config.has_plasmasodiumconcentrationsetpoint())
      PBProperty::Load(config.plasmasodiumconcentrationsetpoint(), dst.GetPlasmaSodiumConcentrationSetPoint());

    if (config.has_leftglomerularfluidpermeabilitybaseline())
      PBProperty::Load(config.leftglomerularfluidpermeabilitybaseline(), dst.GetLeftGlomerularFluidPermeabilityBaseline());
    if (config.has_leftglomerularfilteringsurfaceareabaseline())
      PBProperty::Load(config.leftglomerularfilteringsurfaceareabaseline(), dst.GetLeftGlomerularFilteringSurfaceAreaBaseline());
    if (config.has_lefttubularreabsorptionfluidpermeabilitybaseline())
      PBProperty::Load(config.lefttubularreabsorptionfluidpermeabilitybaseline(), dst.GetLeftTubularReabsorptionFluidPermeabilityBaseline());
    if (config.has_lefttubularreabsorptionfilteringsurfaceareabaseline())
      PBProperty::Load(config.lefttubularreabsorptionfilteringsurfaceareabaseline(), dst.GetLeftTubularReabsorptionFilteringSurfaceAreaBaseline());

    if (config.has_maximumafferentresistance())
      PBProperty::Load(config.maximumafferentresistance(), dst.GetMaximumAfferentResistance());
    if (config.has_minimumafferentresistance())
      PBProperty::Load(config.minimumafferentresistance(), dst.GetMinimumAfferentResistance());

    if (config.has_rightglomerularfluidpermeabilitybaseline())
      PBProperty::Load(config.rightglomerularfluidpermeabilitybaseline(), dst.GetRightGlomerularFluidPermeabilityBaseline());
    if (config.has_rightglomerularfilteringsurfaceareabaseline())
      PBProperty::Load(config.rightglomerularfilteringsurfaceareabaseline(), dst.GetRightGlomerularFilteringSurfaceAreaBaseline());
    if (config.has_righttubularreabsorptionfluidpermeabilitybaseline())
      PBProperty::Load(config.righttubularreabsorptionfluidpermeabilitybaseline(), dst.GetRightTubularReabsorptionFluidPermeabilityBaseline());
    if (config.has_righttubularreabsorptionfilteringsurfaceareabaseline())
      PBProperty::Load(config.righttubularreabsorptionfilteringsurfaceareabaseline(), dst.GetRightTubularReabsorptionFilteringSurfaceAreaBaseline());

    if (config.has_targetsodiumdelivery())
      PBProperty::Load(config.targetsodiumdelivery(), dst.GetTargetSodiumDelivery());
  }

  // Respiratory
  if (src.has_respiratoryconfiguration())
  {
    const ConfigurationData_RespiratoryConfigurationData& config = src.respiratoryconfiguration();
    if (config.has_centralcontrollerco2pressuresetpoint())
      PBProperty::Load(config.centralcontrollerco2pressuresetpoint(), dst.GetCentralControllerCO2PressureSetPoint());
    if (config.has_centralventilatorycontrollergain())
      PBProperty::Load(config.centralventilatorycontrollergain(), dst.GetCentralVentilatoryControllerGain());
    if (config.has_minimumallowableinpiratoryandexpiratoryperiod())
      PBProperty::Load(config.minimumallowableinpiratoryandexpiratoryperiod(), dst.GetMinimumAllowableInpiratoryAndExpiratoryPeriod());
    if (config.has_minimumallowabletidalvolume())
      PBProperty::Load(config.minimumallowabletidalvolume(), dst.GetMinimumAllowableTidalVolume());
    if (config.has_peripheralcontrollerco2pressuresetpoint())
      PBProperty::Load(config.peripheralcontrollerco2pressuresetpoint(), dst.GetPeripheralControllerCO2PressureSetPoint());
    if (config.has_peripheralventilatorycontrollergain())
      PBProperty::Load(config.peripheralventilatorycontrollergain(), dst.GetPeripheralVentilatoryControllerGain());
    if (config.has_pleuralcompliancesensitivity())
      PBProperty::Load(config.pleuralcompliancesensitivity(), dst.GetPleuralComplianceSensitivity());
    if (config.has_pulmonaryventilationratemaximum())
      PBProperty::Load(config.pulmonaryventilationratemaximum(), dst.GetPulmonaryVentilationRateMaximum());
    if (config.has_ventilationtidalvolumeintercept())
      PBProperty::Load(config.ventilationtidalvolumeintercept(), dst.GetVentilationTidalVolumeIntercept());
    if (config.has_ventilatoryocclusionpressure())
      PBProperty::Load(config.ventilatoryocclusionpressure(), dst.GetVentilatoryOcclusionPressure());
  }

  // Tissue
  if (src.has_tissueconfiguration())
  {
    const ConfigurationData_TissueConfigurationData& config = src.tissueconfiguration();
    if (config.enabletissue() != cdm::eSwitch::NullSwitch)
      dst.EnableTissue((eSwitch)config.enabletissue());
  }
}

ConfigurationData* PBPulseConfiguration::Unload(const PulseConfiguration& src)
{
  ConfigurationData* dst = new ConfigurationData();
  PBPulseConfiguration::Serialize(src, *dst);
  return dst;
}
void PBPulseConfiguration::Serialize(const PulseConfiguration& src, ConfigurationData& dst)
{
  if (src.HasTimedStabilization())
    dst.set_allocated_timedstabilization(PBEngine::Unload(*src.m_TimedStabilization));
  else if (src.HasDynamicStabilization())
    dst.set_allocated_dynamicstabilization(PBEngine::Unload(*src.m_DynamicStabilization));
  if (src.HasTimeStep())
    dst.set_allocated_timestep(PBProperty::Unload(*src.m_TimeStep));
  if (src.HasAutoSerialization())
    dst.set_allocated_autoserialization(PBEngine::Unload(*src.m_AutoSerialization));
  dst.set_writepatientbaselinefile((cdm::eSwitch)src.m_WritePatientBaselineFile);

  // Barorecptor
  ConfigurationData_BaroreceptorConfigurationData* baro = dst.mutable_baroreceptorconfiguration();
  if (src.HasResponseSlope())
    baro->set_allocated_responseslope(PBProperty::Unload(*src.m_ResponseSlope));
  if (src.HasHeartRateDistributedTimeDelay())
    baro->set_allocated_heartratedistributedtimedelay(PBProperty::Unload(*src.m_HeartRateDistributedTimeDelay));
  if (src.HasHeartElastanceDistributedTimeDelay())
    baro->set_allocated_heartelastancedistributedtimedelay(PBProperty::Unload(*src.m_HeartElastanceDistributedTimeDelay));
  if (src.HasSystemicResistanceDistributedTimeDelay())
    baro->set_allocated_systemicresistancedistributedtimedelay(PBProperty::Unload(*src.m_SystemicResistanceDistributedTimeDelay));
  if (src.HasVenousComplianceDistributedTimeDelay())
    baro->set_allocated_venouscompliancedistributedtimedelay(PBProperty::Unload(*src.m_VenousComplianceDistributedTimeDelay));
  if (src.HasNormalizedHeartRateIntercept())
    baro->set_allocated_normalizedheartrateintercept(PBProperty::Unload(*src.m_NormalizedHeartRateIntercept));
  if (src.HasNormalizedHeartRateSympatheticSlope())
    baro->set_allocated_normalizedheartratesympatheticslope(PBProperty::Unload(*src.m_NormalizedHeartRateSympatheticSlope));
  if (src.HasNormalizedHeartRateParasympatheticSlope())
    baro->set_allocated_normalizedheartrateparasympatheticslope(PBProperty::Unload(*src.m_NormalizedHeartRateParasympatheticSlope));
  if (src.HasNormalizedHeartElastanceIntercept())
    baro->set_allocated_normalizedheartelastanceintercept(PBProperty::Unload(*src.m_NormalizedHeartElastanceIntercept));
  if (src.HasNormalizedHeartElastanceSympatheticSlope())
    baro->set_allocated_normalizedheartelastancesympatheticslope(PBProperty::Unload(*src.m_NormalizedHeartElastanceSympatheticSlope));
  if (src.HasNormalizedResistanceIntercept())
    baro->set_allocated_normalizedresistanceintercept(PBProperty::Unload(*src.m_NormalizedResistanceIntercept));
  if (src.HasNormalizedResistanceSympatheticSlope())
    baro->set_allocated_normalizedresistancesympatheticslope(PBProperty::Unload(*src.m_NormalizedResistanceSympatheticSlope));
  if (src.HasNormalizedComplianceIntercept())
    baro->set_allocated_normalizedcomplianceintercept(PBProperty::Unload(*src.m_NormalizedComplianceIntercept));
  if (src.HasNormalizedComplianceParasympatheticSlope())
    baro->set_allocated_normalizedcomplianceparasympatheticslope(PBProperty::Unload(*src.m_NormalizedComplianceParasympatheticSlope));

  // Blood Chemistry
  ConfigurationData_BloodChemistryConfigurationData* bc = dst.mutable_bloodchemistryconfiguration();
  if (src.HasMeanCorpuscularHemoglobin())
    bc->set_allocated_meancorpuscularhemoglobin(PBProperty::Unload(*src.m_MeanCorpuscularHemoglobin));
  if (src.HasMeanCorpuscularVolume())
    bc->set_allocated_meancorpuscularvolume(PBProperty::Unload(*src.m_MeanCorpuscularVolume));
  if (src.HasStandardDiffusionDistance())
    bc->set_allocated_standarddiffusiondistance(PBProperty::Unload(*src.m_StandardDiffusionDistance));
  if (src.HasStandardOxygenDiffusionCoefficient())
    bc->set_allocated_standardoxygendiffusioncoefficient(PBProperty::Unload(*src.m_StandardOxygenDiffusionCoefficient));

  // Cardiovascular
  ConfigurationData_CardiovascularConfigurationData* cv = dst.mutable_cardiovascularconfiguration();
  if (src.HasLeftHeartElastanceMaximum())
    cv->set_allocated_leftheartelastancemaximum(PBProperty::Unload(*src.m_LeftHeartElastanceMaximum));
  if (src.HasLeftHeartElastanceMinimum())
    cv->set_allocated_leftheartelastanceminimum(PBProperty::Unload(*src.m_LeftHeartElastanceMinimum));
  if (src.HasMinimumBloodVolumeFraction())
    cv->set_allocated_minimumbloodvolumefraction(PBProperty::Unload(*src.m_MinimumBloodVolumeFraction));
  if (src.HasRightHeartElastanceMaximum())
    cv->set_allocated_rightheartelastancemaximum(PBProperty::Unload(*src.m_RightHeartElastanceMaximum));
  if (src.HasRightHeartElastanceMinimum())
    cv->set_allocated_rightheartelastanceminimum(PBProperty::Unload(*src.m_RightHeartElastanceMinimum));
  if (src.HasStandardPulmonaryCapillaryCoverage())
    cv->set_allocated_standardpulmonarycapillarycoverage(PBProperty::Unload(*src.m_StandardPulmonaryCapillaryCoverage));

  // Circuits
  ConfigurationData_CircuitConfigurationData* circuit = dst.mutable_circuitconfiguration();
  if (src.HasCardiovascularOpenResistance())
    circuit->set_allocated_cardiovascularopenresistance(PBProperty::Unload(*src.m_CardiovascularOpenResistance));
  if (src.HasDefaultClosedElectricResistance())
    circuit->set_allocated_defaultclosedelectricresistance(PBProperty::Unload(*src.m_DefaultClosedElectricResistance));
  if (src.HasDefaultClosedFlowResistance())
    circuit->set_allocated_defaultclosedflowresistance(PBProperty::Unload(*src.m_DefaultClosedFlowResistance));
  if (src.HasDefaultClosedHeatResistance())
    circuit->set_allocated_defaultclosedheatresistance(PBProperty::Unload(*src.m_DefaultClosedHeatResistance));
  if (src.HasDefaultOpenElectricResistance())
    circuit->set_allocated_defaultopenelectricresistance(PBProperty::Unload(*src.m_DefaultOpenElectricResistance));
  if (src.HasDefaultOpenFlowResistance())
    circuit->set_allocated_defaultopenflowresistance(PBProperty::Unload(*src.m_DefaultOpenFlowResistance));
  if (src.HasDefaultOpenHeatResistance())
    circuit->set_allocated_defaultopenheatresistance(PBProperty::Unload(*src.m_DefaultOpenHeatResistance));
  if (src.HasMachineClosedResistance())
    circuit->set_allocated_machineclosedresistance(PBProperty::Unload(*src.m_MachineClosedResistance));
  if (src.HasMachineOpenResistance())
    circuit->set_allocated_machineopenresistance(PBProperty::Unload(*src.m_MachineOpenResistance));
  if (src.HasRespiratoryClosedResistance())
    circuit->set_allocated_respiratoryclosedresistance(PBProperty::Unload(*src.m_RespiratoryClosedResistance));
  if (src.HasRespiratoryOpenResistance())
    circuit->set_allocated_respiratoryopenresistance(PBProperty::Unload(*src.m_RespiratoryOpenResistance));

  // Constants
  ConfigurationData_ConstantsConfigurationData* consts = dst.mutable_constantsconfiguration();
  if (src.HasOxygenMetabolicConstant())
    consts->set_allocated_oxygenmetabolicconstant(PBProperty::Unload(*src.m_OxygenMetabolicConstant));
  if (src.HasStefanBoltzmann())
    consts->set_allocated_stefanboltzmann(PBProperty::Unload(*src.m_StefanBoltzmann));
  if (src.HasUniversalGasConstant())
    consts->set_allocated_universalgasconstant(PBProperty::Unload(*src.m_UniversalGasConstant));

  // Drugs
  ConfigurationData_DrugsConfigurationData* drugs = dst.mutable_drugsconfiguration();
  drugs->set_pdmodel((cdm::eSwitch)src.m_PDEnabled);

  // Energy
  ConfigurationData_EnergyConfigurationData* energy = dst.mutable_energyconfiguration();
  if (src.HasBodySpecificHeat())
    energy->set_allocated_bodyspecificheat(PBProperty::Unload(*src.m_BodySpecificHeat));
  if (src.HasCarbonDioxideProductionFromOxygenConsumptionConstant())
    energy->set_allocated_carbondioxideproductionfromoxygenconsumptionconstant(PBProperty::Unload(*src.m_CarbonDioxideProductionFromOxygenConsumptionConstant));
  if (src.HasCoreTemperatureLow())
    energy->set_allocated_coretemperaturelow(PBProperty::Unload(*src.m_CoreTemperatureLow));
  if (src.HasCoreTemperatureHigh())
    energy->set_allocated_coretemperaturehigh(PBProperty::Unload(*src.m_CoreTemperatureHigh));
  if (src.HasDeltaCoreTemperatureLow())
    energy->set_allocated_deltacoretemperaturelow(PBProperty::Unload(*src.m_DeltaCoreTemperatureLow));
  if (src.HasEnergyPerATP())
    energy->set_allocated_energyperatp(PBProperty::Unload(*src.m_EnergyPerATP));
  if (src.HasSweatHeatTransfer())
    energy->set_allocated_sweatheattransfer(PBProperty::Unload(*src.m_SweatHeatTransfer));
  if (src.HasVaporSpecificHeat())
    energy->set_allocated_vaporspecificheat(PBProperty::Unload(*src.m_VaporSpecificHeat));
  if (src.HasVaporizationEnergy())
    energy->set_allocated_vaporizationenergy(PBProperty::Unload(*src.m_VaporizationEnergy));

  // Environment
  ConfigurationData_EnvironmentConfigurationData* env = dst.mutable_environmentconfiguration();
  if (src.HasAirDensity())
    env->set_allocated_airdensity(PBProperty::Unload(*src.m_AirDensity));
  if (src.HasAirSpecificHeat())
    env->set_allocated_airspecificheat(PBProperty::Unload(*src.m_AirSpecificHeat));
  if (src.HasMolarMassOfDryAir())
    env->set_allocated_molarmassofdryair(PBProperty::Unload(*src.m_MolarMassOfDryAir));
  if (src.HasMolarMassOfWaterVapor())
    env->set_allocated_molarmassofwatervapor(PBProperty::Unload(*src.m_MolarMassOfWaterVapor));
  if (src.HasInitialEnvironmentalConditions())
    env->set_allocated_initialconditions(PBEnvironment::Unload(*src.m_InitialEnvironmentalConditions));
  if (src.HasWaterDensity())
    env->set_allocated_waterdensity(PBProperty::Unload(*src.m_WaterDensity));

  // Gastrointestinal
  ConfigurationData_GastrointestinalConfigurationData* gi = dst.mutable_gastrointestinalconfiguration();
  if (src.HasCalciumAbsorptionFraction())
    gi->set_allocated_calciumabsorptionfraction(PBProperty::Unload(*src.m_CalciumAbsorptionFraction));
  if (src.HasCalciumDigestionRate())
    gi->set_allocated_calciumdigestionrate(PBProperty::Unload(*src.m_CalciumDigestionRate));
  if (src.HasCarbohydrateAbsorptionFraction())
    gi->set_allocated_carbohydrateabsorptionfraction(PBProperty::Unload(*src.m_CarbohydrateAbsorptionFraction));
  if (src.HasDefaultCarbohydrateDigestionRate())
    gi->set_allocated_defaultcarbohydratedigestionrate(PBProperty::Unload(*src.m_DefaultCarbohydrateDigestionRate));
  if (src.HasDefaultFatDigestionRate())
    gi->set_allocated_defaultfatdigestionrate(PBProperty::Unload(*src.m_DefaultFatDigestionRate));
  if (src.HasDefaultProteinDigestionRate())
    gi->set_allocated_defaultproteindigestionrate(PBProperty::Unload(*src.m_DefaultProteinDigestionRate));
  if (src.HasDefaultStomachContents())
    gi->set_allocated_initialstomachcontents(PBPatientNutrition::Unload(*src.m_DefaultStomachContents));
  if (src.HasFatAbsorptionFraction())
    gi->set_allocated_fatabsorptionfraction(PBProperty::Unload(*src.m_FatAbsorptionFraction));
  if (src.HasProteinToUreaFraction())
    gi->set_allocated_proteintoureafraction(PBProperty::Unload(*src.m_ProteinToUreaFraction));
  if (src.HasWaterDigestionRate())
    gi->set_allocated_waterdigestionrate(PBProperty::Unload(*src.m_WaterDigestionRate));

  // Nervous
  ConfigurationData_NervousConfigurationData* n = dst.mutable_nervousconfiguration();
  if (src.HasPupilDiameterBaseline())
    n->set_allocated_pupildiameterbaseline(PBProperty::Unload(*src.m_PupilDiameterBaseline));

  // Renal
  ConfigurationData_RenalConfigurationData* renal = dst.mutable_renalconfiguration();
  renal->set_enablerenal((cdm::eSwitch)src.m_RenalEnabled);
  if (src.HasPlasmaSodiumConcentrationSetPoint())
    renal->set_allocated_plasmasodiumconcentrationsetpoint(PBProperty::Unload(*src.m_PlasmaSodiumConcentrationSetPoint));
  if (src.HasLeftGlomerularFilteringSurfaceAreaBaseline())
    renal->set_allocated_leftglomerularfilteringsurfaceareabaseline(PBProperty::Unload(*src.m_LeftGlomerularFilteringSurfaceAreaBaseline));
  if (src.HasLeftGlomerularFluidPermeabilityBaseline())
    renal->set_allocated_leftglomerularfluidpermeabilitybaseline(PBProperty::Unload(*src.m_LeftGlomerularFluidPermeabilityBaseline));
  if (src.HasLeftTubularReabsorptionFilteringSurfaceAreaBaseline())
    renal->set_allocated_lefttubularreabsorptionfilteringsurfaceareabaseline(PBProperty::Unload(*src.m_LeftTubularReabsorptionFilteringSurfaceAreaBaseline));
  if (src.HasLeftTubularReabsorptionFluidPermeabilityBaseline())
    renal->set_allocated_lefttubularreabsorptionfluidpermeabilitybaseline(PBProperty::Unload(*src.m_LeftTubularReabsorptionFluidPermeabilityBaseline));

  if (src.HasMaximumAfferentResistance())
    renal->set_allocated_maximumafferentresistance(PBProperty::Unload(*src.m_MaximumAfferentResistance));
  if (src.HasMinimumAfferentResistance())
    renal->set_allocated_minimumafferentresistance(PBProperty::Unload(*src.m_MinimumAfferentResistance));

  if (src.HasRightGlomerularFilteringSurfaceAreaBaseline())
    renal->set_allocated_rightglomerularfilteringsurfaceareabaseline(PBProperty::Unload(*src.m_RightGlomerularFilteringSurfaceAreaBaseline));
  if (src.HasRightGlomerularFluidPermeabilityBaseline())
    renal->set_allocated_rightglomerularfluidpermeabilitybaseline(PBProperty::Unload(*src.m_RightGlomerularFluidPermeabilityBaseline));
  if (src.HasRightTubularReabsorptionFilteringSurfaceAreaBaseline())
    renal->set_allocated_righttubularreabsorptionfilteringsurfaceareabaseline(PBProperty::Unload(*src.m_RightTubularReabsorptionFilteringSurfaceAreaBaseline));
  if (src.HasRightTubularReabsorptionFluidPermeabilityBaseline())
    renal->set_allocated_righttubularreabsorptionfluidpermeabilitybaseline(PBProperty::Unload(*src.m_RightTubularReabsorptionFluidPermeabilityBaseline));

  // Respiratory
  ConfigurationData_RespiratoryConfigurationData* resp = dst.mutable_respiratoryconfiguration();
  if (src.HasCentralControllerCO2PressureSetPoint())
    resp->set_allocated_centralcontrollerco2pressuresetpoint(PBProperty::Unload(*src.m_CentralControllerCO2PressureSetPoint));
  if (src.HasCentralVentilatoryControllerGain())
    resp->set_allocated_centralventilatorycontrollergain(PBProperty::Unload(*src.m_CentralVentilatoryControllerGain));
  if (src.HasMinimumAllowableInpiratoryAndExpiratoryPeriod())
    resp->set_allocated_minimumallowableinpiratoryandexpiratoryperiod(PBProperty::Unload(*src.m_MinimumAllowableInpiratoryAndExpiratoryPeriod));
  if (src.HasMinimumAllowableTidalVolume())
    resp->set_allocated_minimumallowabletidalvolume(PBProperty::Unload(*src.m_MinimumAllowableTidalVolume));
  if (src.HasPeripheralControllerCO2PressureSetPoint())
    resp->set_allocated_peripheralcontrollerco2pressuresetpoint(PBProperty::Unload(*src.m_PeripheralControllerCO2PressureSetPoint));
  if (src.HasPeripheralVentilatoryControllerGain())
    resp->set_allocated_peripheralventilatorycontrollergain(PBProperty::Unload(*src.m_PeripheralVentilatoryControllerGain));
  if (src.HasPleuralComplianceSensitivity())
    resp->set_allocated_pleuralcompliancesensitivity(PBProperty::Unload(*src.m_PleuralComplianceSensitivity));
  if (src.HasPulmonaryVentilationRateMaximum())
    resp->set_allocated_pulmonaryventilationratemaximum(PBProperty::Unload(*src.m_PulmonaryVentilationRateMaximum));
  if (src.HasVentilationTidalVolumeIntercept())
    resp->set_allocated_ventilationtidalvolumeintercept(PBProperty::Unload(*src.m_VentilationTidalVolumeIntercept));
  if (src.HasVentilatoryOcclusionPressure())
    resp->set_allocated_ventilatoryocclusionpressure(PBProperty::Unload(*src.m_VentilatoryOcclusionPressure));

  // Tissue
  ConfigurationData_TissueConfigurationData* tissue = dst.mutable_tissueconfiguration();
  tissue->set_enabletissue((cdm::eSwitch)src.m_TissueEnabled);
}

bool PBPulseConfiguration::SerializeToString(const PulseConfiguration& src, std::string& output, SerializationFormat m)
{
  pulse::proto::ConfigurationData data;
  PBPulseConfiguration::Serialize(src, data);
  return PBUtils::SerializeToString(data, output, m, src.GetLogger());
}
bool PBPulseConfiguration::SerializeToFile(const PulseConfiguration& src, const std::string& filename, SerializationFormat m)
{
  pulse::proto::ConfigurationData data;
  PBPulseConfiguration::Serialize(src, data);
  std::string content;
  PBPulseConfiguration::SerializeToString(src, content, m);
  return WriteFile(content, filename, m);
}

bool PBPulseConfiguration::SerializeFromString(const std::string& src, PulseConfiguration& dst, SerializationFormat m)
{
  pulse::proto::ConfigurationData data;
  if (!PBUtils::SerializeFromString(src, data, m, dst.GetLogger()))
    return false;
  PBPulseConfiguration::Load(data, dst);
  return true;
}
bool PBPulseConfiguration::SerializeFromFile(const std::string& filename, PulseConfiguration& dst, SerializationFormat m)
{
  std::string content = ReadFile(filename, m);
  if (content.empty())
    return false;
  return PBPulseConfiguration::SerializeFromString(content, dst, m);
}