# *******************************************************************************
# OpenStudio(R), Copyright (c) 2008-2020, Alliance for Sustainable Energy, LLC.
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# (1) Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# (2) Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# (3) Neither the name of the copyright holder nor the names of any contributors
# may be used to endorse or promote products derived from this software without
# specific prior written permission from the respective party.
#
# (4) Other than as required in clauses (1) and (2), distributions in any form
# of modifications or other derivative works may not use the "OpenStudio"
# trademark, "OS", "os", or any other confusingly similar designation without
# specific prior written permission from Alliance for Sustainable Energy, LLC.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE
# UNITED STATES GOVERNMENT, OR THE UNITED STATES DEPARTMENT OF ENERGY, NOR ANY OF
# THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# *******************************************************************************

# see the URL below for information on how to write OpenStudio measures
# http://openstudio.nrel.gov/openstudio-measure-writing-guide

# start the measure
class AddZoneMixingObject < OpenStudio::Measure::EnergyPlusMeasure
  # human readable name
  def name
    return 'Add Zone Mixing Object'
  end

  # human readable description
  def description
    return 'This adds a zone mixing object with a few inputs exposed, including source zone. You can add multiple copies of this to the workflow as needed.'
  end

  # human readable description of modeling approach
  def modeler_description
    return 'Currently this is just setup for design level calculation method, but it could be extended as needed..'
  end

  # define the arguments that the user will input
  def arguments(workspace)
    args = OpenStudio::Measure::OSArgumentVector.new

    # the name of the zone to receive air
    zone_name = OpenStudio::Measure::OSArgument.makeStringArgument('zone_name', true)
    zone_name.setDisplayName('Zone with Exhaust')
    args << zone_name

    # the name of the schedule
    schedule_name = OpenStudio::Measure::OSArgument.makeStringArgument('schedule_name', true)
    schedule_name.setDisplayName('Schedule Name for Zone Mixing')
    args << schedule_name

    # design level for zone mixing
    design_level = OpenStudio::Measure::OSArgument.makeDoubleArgument('design_level', true)
    design_level.setDisplayName('Design Level for Zone Mixing')
    design_level.setUnits('cfm')
    args << design_level

    # the name of the zone to receive air
    source_zone_name = OpenStudio::Measure::OSArgument.makeStringArgument('source_zone_name', true)
    source_zone_name.setDisplayName('Source Zone for Zone Mixing')
    args << source_zone_name

    return args
  end

  # define what happens when the measure is run
  def run(workspace, runner, user_arguments)
    super(workspace, runner, user_arguments)

    # use the built-in error checking
    if !runner.validateUserArguments(arguments(workspace), user_arguments)
      return false
    end

    # assign the user inputs to variables
    zone_name = runner.getStringArgumentValue('zone_name', user_arguments)
    schedule_name = runner.getStringArgumentValue('schedule_name', user_arguments)
    design_level = runner.getDoubleArgumentValue('design_level', user_arguments)
    source_zone_name = runner.getStringArgumentValue('source_zone_name', user_arguments)

    # reporting initial condition of model
    zone_mixing_objects = workspace.getObjectsByType('ZoneMixing'.to_IddObjectType)
    runner.registerInitialCondition("The building started with #{zone_mixing_objects.size} zone mixing objects.")

    # get all thermal zones in the starting model
    zones = workspace.getObjectsByType('Zone'.to_IddObjectType)

    # validate input names and get zones
    zone_name_valid = false
    source_zone_name_valid = false
    zones.each do |zone|
      if zone_name == zone.getString(0).to_s
        zone_name_valid = true
      elsif source_zone_name == zone.getString(0).to_s
        source_zone_name_valid = true
      end
    end

    # error if didn't find zones
    if (zone_name_valid == false) || (source_zone_name_valid == false)
      runner.registerError('One or more of the expected zones could not be found..')
      return false
    end

    # TODO: - validate schedule name (multiple types to look at)

    # validate design level input
    if design_level < 0.0
      runner.registerError('Choose a non negative number for design level.')
      return false
    end
    # variables for zone mixing object
    zm_calc_method = 'Flow/Zone' # at some point in the future could add more options and inputs for this
    design_level_si = OpenStudio.convert(design_level, 'cfm', 'm^3/s').get

    # add a new zone mixing to the model
    zone_mixing_string = "
      ZoneMixing,
        #{zone_name} Zone Mixing,  !- Name
        #{zone_name},  !- Zone Name
        #{schedule_name},  !- Schedule Name
        #{zm_calc_method},  !- Design Flow Rate Calculation Method
        #{design_level_si},  !- Design Level
        ,  !- Volume Flow Rate per Area {m3/s/m2}
        ,  !- Volume Flow Rate Per Person {m3/s/person}
        ,  !- Air Changes per Hour {ACH}
        #{source_zone_name},  !- Source Zone Name
        0.0;  !- Delta Temperature
      "
    idfObject = OpenStudio::IdfObject.load(zone_mixing_string)
    object = idfObject.get
    wsObject = workspace.addObject(object)
    new_zone_mixing_object = wsObject.get

    # echo the new zone mixing objects name back to the user, using the index based getString method
    runner.registerInfo("A zone mixing object named '#{new_zone_mixing_object.getString(0)}' was added.")

    # report final condition of model
    zone_mixing_objects = workspace.getObjectsByType('ZoneMixing'.to_IddObjectType)
    runner.registerFinalCondition("The building finished with #{zone_mixing_objects.size} zone mixing objects.")

    return true
  end
end

# register the measure to be used by the application
AddZoneMixingObject.new.registerWithApplication