boost::optional<IdfObject> ForwardTranslator::translateScheduleYear( ScheduleYear & modelObject )
{
  IdfObject scheduleYear = createRegisterAndNameIdfObject(openstudio::IddObjectType::Schedule_Year,
                                                          modelObject);

  std::vector<ScheduleWeek> scheduleWeeks = modelObject.scheduleWeeks();
  std::vector<openstudio::Date> dates = modelObject.dates();
  
  unsigned N = scheduleWeeks.size();

  if( N != dates.size() )
  {
    LOG(Error,"Could not translate " << modelObject.briefDescription() << ", because the number of week schedules does not match the number of dates.");

    return boost::none;
  }

  boost::optional<ScheduleTypeLimits> scheduleTypeLimits = modelObject.scheduleTypeLimits();
  if (scheduleTypeLimits){
    boost::optional<IdfObject> idfScheduleTypeLimits = translateAndMapModelObject(*scheduleTypeLimits);
    if (idfScheduleTypeLimits){
      scheduleYear.setString(Schedule_YearFields::ScheduleTypeLimitsName, idfScheduleTypeLimits->name().get());
    }
  }

  openstudio::Date startDate(MonthOfYear::Jan, 1);

  scheduleYear.clearExtensibleGroups();

  for (unsigned i = 0; i < N; ++i){
    IdfExtensibleGroup group = scheduleYear.pushExtensibleGroup();

    boost::optional<IdfObject> idfScheduleWeek = translateAndMapModelObject(scheduleWeeks[i]);
    if (idfScheduleWeek){
      group.setString(Schedule_YearExtensibleFields::Schedule_WeekName, idfScheduleWeek->name().get());
    }

    group.setInt(Schedule_YearExtensibleFields::StartMonth, startDate.monthOfYear().value());
    group.setUnsigned(Schedule_YearExtensibleFields::StartDay, startDate.dayOfMonth());
    group.setInt(Schedule_YearExtensibleFields::EndMonth, dates[i].monthOfYear().value());
    group.setUnsigned(Schedule_YearExtensibleFields::EndDay, dates[i].dayOfMonth());

    startDate = dates[i] + openstudio::Time(1,0,0);
  }

  return scheduleYear;
}
boost::optional<IdfObject> ForwardTranslator::translateCentralHeatPumpSystem( CentralHeatPumpSystem& modelObject )
{
  OptionalString s;
  OptionalDouble d;
  OptionalModelObject temp;

  auto const& modules = modelObject.getImpl<model::detail::CentralHeatPumpSystem_Impl>()->modules();

  // If the CentralHeatPumpSystem doesn't have at least one CentralHeatPumpSystemModule, then it shouldn't be translated
  if (modules.empty()) {
    LOG(Warn, "CentralHeatPumpSystem " << modelObject.name().get() << " has no CentralHeatPumpSystemModules, it will not be translated");
    return boost::none;
  }

  // Name
  IdfObject idfObject = createRegisterAndNameIdfObject(openstudio::IddObjectType::CentralHeatPumpSystem, modelObject);

  // ControlMethod
  if( (s = modelObject.controlMethod()) ) {
    idfObject.setString(CentralHeatPumpSystemFields::ControlMethod,s.get());
  }

  // AncillaryPower
  if( (d = modelObject.ancillaryPower()) ) {
    idfObject.setDouble(CentralHeatPumpSystemFields::AncillaryPower,d.get());
  }

  // AncillaryOperationScheduleName
  {
   if( auto schedule = modelObject.ancillaryOperationSchedule() ) {
     if( auto _schedule = translateAndMapModelObject(schedule.get()) ) {
       idfObject.setString(CentralHeatPumpSystemFields::AncillaryOperationScheduleName,_schedule->name().get());
     }
   }
  }

  // supply = Cooling Loop, demand=Source Loop, tertiary = Heating Loop

  // CoolingLoopInletNodeName
  if( auto mo = modelObject.supplyInletModelObject() ) {
    if( auto node = mo->optionalCast<Node>() ) {
      idfObject.setString(CentralHeatPumpSystemFields::CoolingLoopInletNodeName,node->name().get());
    }
  }

  // CoolingLoopOutletNodeName
  if( auto mo = modelObject.supplyOutletModelObject() ) {
    if( auto node = mo->optionalCast<Node>() ) {
      idfObject.setString(CentralHeatPumpSystemFields::CoolingLoopOutletNodeName,node->name().get());
    }
  }

  // SourceLoopInletNodeName
  if( auto mo = modelObject.demandInletModelObject() ) {
    if( auto node = mo->optionalCast<Node>() ) {
      idfObject.setString(CentralHeatPumpSystemFields::SourceLoopInletNodeName,node->name().get());
    }
  }

  // SourceLoopOutletNodeName
  if( auto mo = modelObject.demandOutletModelObject() ) {
    if( auto node = mo->optionalCast<Node>() ) {
      idfObject.setString(CentralHeatPumpSystemFields::SourceLoopOutletNodeName,node->name().get());
    }
  }


  // HeatingLoopInletNodeName
  if ( auto mo = modelObject.tertiaryInletModelObject() ) {
    if ( auto node = mo->optionalCast<Node>() ) {
      idfObject.setString(CentralHeatPumpSystemFields::HeatingLoopInletNodeName, node->name().get());
    }
  }
  // HeatingLoopOutletNodeName
  if ( auto mo = modelObject.tertiaryOutletModelObject() ) {
    if ( auto node = mo->optionalCast<Node>() ) {
      idfObject.setString(CentralHeatPumpSystemFields::HeatingLoopOutletNodeName, node->name().get());
    }
  }

  // ChillerHeaterModulesPerformanceComponentObjectType1
  // ChillerHeaterModulesPerformanceComponentName1
  // ChillerHeaterModulesControlScheduleName1
  // NumberofChillerHeaterModules1

  for ( auto const& module : modules ) {
    IdfExtensibleGroup group = idfObject.pushExtensibleGroup();

    auto performanceComponent = module.chillerHeaterModulesPerformanceComponent();
    if( auto _performanceComponent = translateAndMapModelObject(performanceComponent) ) {
      group.setString(CentralHeatPumpSystemExtensibleFields::ChillerHeaterModulesPerformanceComponentObjectType, _performanceComponent->iddObject().name());
      group.setString(CentralHeatPumpSystemExtensibleFields::ChillerHeaterModulesPerformanceComponentName, _performanceComponent->name().get());
    }
    {
      auto schedule = module.chillerHeaterModulesControlSchedule();
      if( auto _schedule = translateAndMapModelObject(schedule) ) {
        group.setString(CentralHeatPumpSystemExtensibleFields::ChillerHeaterModulesControlScheduleName,_schedule->name().get());
     }
    }
    group.setInt(CentralHeatPumpSystemExtensibleFields::NumberofChillerHeaterModules, module.numberofChillerHeaterModules() );
  }

  return idfObject;
}