OptionalModelObject ReverseTranslator::translateScheduleCompact( const WorkspaceObject & workspaceObject )
{
  if( workspaceObject.iddObject().type() != IddObjectType::Schedule_Compact )
  {
    LOG(Error, "WorkspaceObject is not IddObjectType: Schedule:Compact");
    return boost::none;
  }

  ScheduleCompact scheduleCompact(m_model);

  OptionalWorkspaceObject target = workspaceObject.getTarget(Schedule_CompactFields::ScheduleTypeLimitsName);
  if (target){
    OptionalModelObject scheduleTypeLimits = translateAndMapWorkspaceObject(*target);
    if (scheduleTypeLimits){
      scheduleCompact.setPointer(OS_Schedule_CompactFields::ScheduleTypeLimitsName, scheduleTypeLimits->handle());
    }
  }

  if (OptionalString os = workspaceObject.name()) {
    scheduleCompact.setName(*os);
  }

  for (const IdfExtensibleGroup& eg : workspaceObject.extensibleGroups()) {
    scheduleCompact.pushExtensibleGroup(eg.fields());
  }

  return scheduleCompact;
}
OptionalModelObject ReverseTranslator::translateScheduleDayHourly(const WorkspaceObject & workspaceObject){
  if (workspaceObject.iddObject().type() != IddObjectType::Schedule_Day_Hourly){
    LOG(Error, "WorkspaceObject is not IddObjectType: Schedule:Day:Hourly");
    return boost::none;
  }

  // create the schedule
  ScheduleDay scheduleDay(m_model);

  OptionalString s = workspaceObject.name();
  if (s){
    scheduleDay.setName(*s);
  }

  OptionalWorkspaceObject target = workspaceObject.getTarget(Schedule_Day_HourlyFields::ScheduleTypeLimitsName);
  if (target){
    OptionalModelObject scheduleTypeLimits = translateAndMapWorkspaceObject(*target);
    if (scheduleTypeLimits){
      scheduleDay.setPointer(OS_Schedule_DayFields::ScheduleTypeLimitsName, scheduleTypeLimits->handle());
    }
  }

  for(unsigned i = 0; i < 24; ++i){
    OptionalDouble d = workspaceObject.getDouble(Schedule_Day_HourlyFields::Hour1 + i, true);
    if (d){
      scheduleDay.addValue(openstudio::Time(0,i+1,0,0), *d);
    }
  }

  return scheduleDay;
}
OptionalModelObject ReverseTranslator::translateScheduleConstant( const WorkspaceObject & workspaceObject )
{
  if( workspaceObject.iddObject().type() != IddObjectType::Schedule_Constant )
  {
    LOG(Error, "WorkspaceObject is not IddObjectType: Schedule:Constant");
    return boost::none;
  }

  ScheduleConstant scheduleConstant(m_model);

  boost::optional<WorkspaceObject> target = workspaceObject.getTarget(Schedule_ConstantFields::ScheduleTypeLimitsName);
  if (target){
    OptionalModelObject scheduleTypeLimits = translateAndMapWorkspaceObject(*target);
    if (scheduleTypeLimits) {
      scheduleConstant.setPointer(OS_Schedule_ConstantFields::ScheduleTypeLimitsName, scheduleTypeLimits->handle());
    }
  }
  if (OptionalDouble val = workspaceObject.getDouble(Schedule_ConstantFields::HourlyValue)) {
    scheduleConstant.setValue(*val);
  }

  return scheduleConstant;
}
OptionalModelObject ReverseTranslator::translateScheduleDayInterval(const WorkspaceObject & workspaceObject){
  if (workspaceObject.iddObject().type() != IddObjectType::Schedule_Day_Interval){
    LOG(Error, "WorkspaceObject is not IddObjectType: Schedule:Day:Interval");
    return boost::none;
  }

  // create the schedule
  ScheduleDay scheduleDay(m_model);

  OptionalString s = workspaceObject.name();
  if (s){
    scheduleDay.setName(*s);
  }

  OptionalWorkspaceObject target = workspaceObject.getTarget(Schedule_Day_IntervalFields::ScheduleTypeLimitsName);
  if (target){
    OptionalModelObject scheduleTypeLimits = translateAndMapWorkspaceObject(*target);
    if (scheduleTypeLimits){
      scheduleDay.setPointer(OS_Schedule_DayFields::ScheduleTypeLimitsName, scheduleTypeLimits->handle());
    }
  }

  s = workspaceObject.getString(2,true);
  if (s){
    if (openstudio::istringEqual(*s,"yes")){
      scheduleDay.setInterpolatetoTimestep(true);
    }
    else if (openstudio::istringEqual(*s,"yes")){
      scheduleDay.setInterpolatetoTimestep(false);
    }
  }

  //get extensible groups
  std::vector<IdfExtensibleGroup> extensibleGroups = workspaceObject.extensibleGroups();
  //loop over extensible groups
  boost::regex timeRegex("(\\d?\\d:\\d\\d)");
  boost::smatch m;
  unsigned n = extensibleGroups.size();
  for (unsigned i = 0; i < n; ++i){
    //read in extensible groups
    boost::optional<std::string> timeString = extensibleGroups[i].getString(Schedule_Day_IntervalExtensibleFields::Time);
    boost::optional<double> valueUntilTime = extensibleGroups[i].getDouble(Schedule_Day_IntervalExtensibleFields::ValueUntilTime);
    if (timeString && valueUntilTime) {
      // Time string may be prefixed with "Until: ". Extract time in HH:MM format.
      if (boost::regex_search(*timeString,m,timeRegex)) {
        timeString = std::string(m[1].first,m[1].second);
      }
      try {
        openstudio::Time time(*timeString);
        scheduleDay.addValue(time,*valueUntilTime);
      }
      catch (std::exception& e) {
        LOG(Warn,"Could not add value (" << *timeString << ", " << *valueUntilTime
            << ") to ScheduleDay being created from " << workspaceObject.briefDescription()
            << ", because " << e.what() << ".");
      }
    }
    else {
      LOG(Warn,"Encountered extensible group with incomplete or improperly formatted data in "
          << workspaceObject.briefDescription() << ". Therefore, a corresponding value is not "
          << "being added to the ScheduleDay object under construction.");
    }
  }

  return scheduleDay;
}
OptionalModelObject ReverseTranslator::translateZoneHVACIdealLoadsAirSystem( const WorkspaceObject & workspaceObject )
{
  if( workspaceObject.iddObject().type() != IddObjectType::ZoneHVAC_IdealLoadsAirSystem ) {
    LOG(Error, "WorkspaceObject is not IddObjectType: ZoneHVAC:IdealLoadsAirSystem");
     return boost::none;
  }

  ZoneHVACIdealLoadsAirSystem zoneHVACIdealLoadsAirSystem(m_model);
  
  // name
  boost::optional<std::string> s = workspaceObject.name();
  if(s){
    zoneHVACIdealLoadsAirSystem.setName(*s);
  }
  
  // availability schedule
  OptionalWorkspaceObject target = workspaceObject.getTarget(ZoneHVAC_IdealLoadsAirSystemFields::AvailabilityScheduleName);
  if (target){
    OptionalModelObject availabilitySchedule = translateAndMapWorkspaceObject(*target);
    if (availabilitySchedule){
      zoneHVACIdealLoadsAirSystem.setPointer(OS_ZoneHVAC_IdealLoadsAirSystemFields::AvailabilityScheduleName, availabilitySchedule->handle());
    }
  }

  // skip inlet and outlet node names - that should be done by the zone HVAC object translator

  // maximum heating supply air temperature
  OptionalDouble d = workspaceObject.getDouble(ZoneHVAC_IdealLoadsAirSystemFields::MaximumHeatingSupplyAirTemperature);
  if (d){
    zoneHVACIdealLoadsAirSystem.setMaximumHeatingSupplyAirTemperature(*d);
  }

  // minimum cooling supply air temperature
  d = workspaceObject.getDouble(ZoneHVAC_IdealLoadsAirSystemFields::MinimumCoolingSupplyAirTemperature);
  if (d){
    zoneHVACIdealLoadsAirSystem.setMinimumCoolingSupplyAirTemperature(*d);
  }

  // maximum heating supply air humidity ratio
  d = workspaceObject.getDouble(ZoneHVAC_IdealLoadsAirSystemFields::MaximumHeatingSupplyAirHumidityRatio);
  if (d){
    zoneHVACIdealLoadsAirSystem.setMaximumHeatingSupplyAirHumidityRatio(*d);
  }

  // minimum cooling supply air humidity ratio
  d = workspaceObject.getDouble(ZoneHVAC_IdealLoadsAirSystemFields::MinimumCoolingSupplyAirHumidityRatio);
  if (d){
    zoneHVACIdealLoadsAirSystem.setMinimumCoolingSupplyAirHumidityRatio(*d);
  }

  // heating limit
  s = workspaceObject.getString(ZoneHVAC_IdealLoadsAirSystemFields::HeatingLimit);
  if (s){
    zoneHVACIdealLoadsAirSystem.setHeatingLimit(*s);
  }

  // maximum heating air flow rate
  d = workspaceObject.getDouble(ZoneHVAC_IdealLoadsAirSystemFields::MaximumHeatingAirFlowRate);
  if (d){
    zoneHVACIdealLoadsAirSystem.setMaximumHeatingAirFlowRate(*d);
  }

  // maximum sensible heating capacity
  d = workspaceObject.getDouble(ZoneHVAC_IdealLoadsAirSystemFields::MaximumSensibleHeatingCapacity);
  if (d){
    zoneHVACIdealLoadsAirSystem.setMaximumSensibleHeatingCapacity(*d);
  }

  // cooling limit
  s = workspaceObject.getString(ZoneHVAC_IdealLoadsAirSystemFields::CoolingLimit);
  if (s){
    zoneHVACIdealLoadsAirSystem.setCoolingLimit(*s);
  }

  // maximum cooling airflow rate
  d = workspaceObject.getDouble(ZoneHVAC_IdealLoadsAirSystemFields::MaximumCoolingAirFlowRate);
  if (d){
    zoneHVACIdealLoadsAirSystem.setMaximumCoolingAirFlowRate(*d);
  }

  // maximum total cooling capacity
  d = workspaceObject.getDouble(ZoneHVAC_IdealLoadsAirSystemFields::MaximumTotalCoolingCapacity);
  if (d){
    zoneHVACIdealLoadsAirSystem.setMaximumTotalCoolingCapacity(*d);
  }

  // heating availability schedule
  target = workspaceObject.getTarget(ZoneHVAC_IdealLoadsAirSystemFields::HeatingAvailabilityScheduleName);
  if (target){
    OptionalModelObject heatingAvailabilitySchedule = translateAndMapWorkspaceObject(*target);
    if (heatingAvailabilitySchedule){
      zoneHVACIdealLoadsAirSystem.setPointer(OS_ZoneHVAC_IdealLoadsAirSystemFields::HeatingAvailabilityScheduleName, heatingAvailabilitySchedule->handle());
    }
  }

  // cooling availability schedule
  target = workspaceObject.getTarget(ZoneHVAC_IdealLoadsAirSystemFields::CoolingAvailabilityScheduleName);
  if (target){
    OptionalModelObject coolingAvailabilitySchedule = translateAndMapWorkspaceObject(*target);
    if (coolingAvailabilitySchedule){
      zoneHVACIdealLoadsAirSystem.setPointer(OS_ZoneHVAC_IdealLoadsAirSystemFields::CoolingAvailabilityScheduleName, coolingAvailabilitySchedule->handle());
    }
  }

  // dehumidification control type
  s = workspaceObject.getString(ZoneHVAC_IdealLoadsAirSystemFields::DehumidificationControlType);
  if (s){
    zoneHVACIdealLoadsAirSystem.setDehumidificationControlType(*s);
  }

  // cooling sensible heat ratio
  d = workspaceObject.getDouble(ZoneHVAC_IdealLoadsAirSystemFields::CoolingSensibleHeatRatio);
  if (d){
    zoneHVACIdealLoadsAirSystem.setCoolingSensibleHeatRatio(*d);
  }

  // humidification control type
  s = workspaceObject.getString(ZoneHVAC_IdealLoadsAirSystemFields::HumidificationControlType);
  if (s){
    zoneHVACIdealLoadsAirSystem.setHumidificationControlType(*s);
  }

  // design specification outdoor air object name
  target = workspaceObject.getTarget(ZoneHVAC_IdealLoadsAirSystemFields::DesignSpecificationOutdoorAirObjectName);
  if (target){
    OptionalModelObject designSpecificationOutdoorAir = translateAndMapWorkspaceObject(*target);
    if (designSpecificationOutdoorAir){
      zoneHVACIdealLoadsAirSystem.setPointer(OS_ZoneHVAC_IdealLoadsAirSystemFields::DesignSpecificationOutdoorAirObjectName, designSpecificationOutdoorAir->handle());
    }
  }

  // demand controlled ventilation type
  s = workspaceObject.getString(ZoneHVAC_IdealLoadsAirSystemFields::DemandControlledVentilationType);
  if (s){
    zoneHVACIdealLoadsAirSystem.setDemandControlledVentilationType(*s);
  }

  // outdoor air economizer type
  s = workspaceObject.getString(ZoneHVAC_IdealLoadsAirSystemFields::OutdoorAirEconomizerType);
  if (s){
    zoneHVACIdealLoadsAirSystem.setOutdoorAirEconomizerType(*s);
  }

  // heat recovery type
  s = workspaceObject.getString(ZoneHVAC_IdealLoadsAirSystemFields::HeatRecoveryType);
  if (s){
    zoneHVACIdealLoadsAirSystem.setHeatRecoveryType(*s);
  }

  // sensible heat recovery effectiveness
  d = workspaceObject.getDouble(ZoneHVAC_IdealLoadsAirSystemFields::SensibleHeatRecoveryEffectiveness);
  if (d){
    zoneHVACIdealLoadsAirSystem.setSensibleHeatRecoveryEffectiveness(*d);
  }

  // latent heat recovery effectiveness
  d = workspaceObject.getDouble(ZoneHVAC_IdealLoadsAirSystemFields::LatentHeatRecoveryEffectiveness);
  if (d){
    zoneHVACIdealLoadsAirSystem.setLatentHeatRecoveryEffectiveness(*d);
  }

  return zoneHVACIdealLoadsAirSystem;
}