# *********************************************************************
# *  Copyright (c) 2008-2015, Natural Resources Canada
# *  All rights reserved.
# *
# *  This library is free software; you can redistribute it and/or
# *  modify it under the terms of the GNU Lesser General Public
# *  License as published by the Free Software Foundation; either
# *  version 2.1 of the License, or (at your option) any later version.
# *
# *  This library is distributed in the hope that it will be useful,
# *  but WITHOUT ANY WARRANTY; without even the implied warranty of
# *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# *  Lesser General Public License for more details.
# *
# *  You should have received a copy of the GNU Lesser General Public
# *  License along with this library; if not, write to the Free Software
# *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
# **********************************************************************/



require "singleton"


module BTAP
  module EQuest
    # Author::    Phylroy Lopez  (mailto:plopez@nrcan.gc.ca)
    # Copyright:: Copyright (c) NRCan
    # License::   GNU Public Licence
    #This class contains encapsulates the generic interface for the DOE2.x command
    #set. It stores the u type, commands, and keyword pairs for each command. It also
    #stores the parent and child command relationships w.r.t. the building envelope
    #and the hvac systems. I have attempted to make the underlying storage of data
    #private so, if required, we could move to a database solution in the future
    #if required for web development..

    class DOECommand

      # Contains the user specified name
      attr_accessor :utype
      #Contains the u-value
      attr_accessor :uvalue
      # Contains the DOE-2 command name.
      attr_accessor :commandName
      # Contains the Keyword Pairs.
      attr_accessor :keywordPairs
      # Lists all ancestors in increasing order.
      attr_accessor :parents
      # An Array of all the children of this command.
      attr_accessor :children
      # The command type.
      attr_accessor :commandType
      # Flag to see if this component is exempt.
      attr_accessor :exempt
      # Comments. To be added to the command.
      attr_accessor :comments
      # A list of all the non_utype_commands.
      attr_accessor :non_utype_commands
      # A list of all the one line commands (no keyword pairs)
      attr_accessor :one_line_commands
      # Pointer to the building obj.
      attr_accessor :building




      def remove()
        #unlink children
        self.children.each {|item| item.remove}
        #unlink from parent.
        self.get_parents[0].children.delete(self)
        #remove from command array.
        @building.commands.delete(self)
        return self
      end

      def set_parent(parent)
        @parents.clear
        parent.get_parents().each {|parent| @parents << parent}
        @parents << parent
      end

      #This method will return the value of the keyword pair if available.
      #Example:
      #If you object has this data in it...
      #
      #"EL1 West Perim Spc (G.W4)" = SPACE
      #SHAPE            = POLYGON
      #ZONE-TYPE        = CONDITIONED
      #PEOPLE-SCHEDULE  = "EL1 Bldg Occup Sch"
      #LIGHTING-SCHEDUL = ( "EL1 Bldg InsLt Sch" )
      #EQUIP-SCHEDULE   = ( "EL1 Bldg Misc Sch" )
      #
      #
      #then calling
      #
      #get_keyword_value("ZONE-TYPE")
      #
      #will return the string
      #
      #"CONDITIONED".
      #
      #if the keyword does not exist, it will return a nil object.
      # Returns the value associated with the keyword.
      def get_keyword_value(string)
        return_string = String.new()
        found = false
        @keywordPairs.each do |pair|
          if pair[0] == string
            found = true
            return_string = pair[1]
          end
        end
        if found == false
          raise "Error: In the command #{@utype}:#{@command_name} Attempted to get a Keyword pair #{string} present in the command\n Is this keyword missing? \n#{output}"
        end
        return return_string
      end

      # Sets the keyword value.
      def set_keyword_value(keyword, value)
        found = false
        unless @keywordPairs.empty?
          @keywordPairs.each do |pair|
            if pair[0] == keyword
              pair[1] = value
              found = true
            end
          end
          if (found == false)
            @keywordPairs.push([keyword,value])
          end
        else
          #First in the array...
          add_keyword_pair(keyword,value)
        end
      end

      # Removes the keyword pair.
      def remove_keyword_pair(string)
        return_string = String.new()
        @keywordPairs.each do |pair|
          if pair[0] == string
            @keywordPairs.delete(pair)
          end
        end
        return return_string
      end

      def initialize()
        @utype = String.new()
        @commandName= String.new()
        @keywordPairs=Array.new()
        @parents = Array.new()
        @children = Array.new()
        @commandType = String.new()
        @exempt = false
        #HVAC Hierarchry
        @comments =Array.new()
        @hvacLevel = Array.new()
        @hvacLevel[0] =["SYSTEM"]
        @hvacLevel[1] =["ZONE"]
        #Envelope Hierachy
        @envelopeLevel = Array.new()
        @envelopeLevel[0] = ["FLOOR"]
        @envelopeLevel[1] = ["SPACE"]

        @envelopeLevel[2] = [
          "EXTERIOR-WALL",
          "INTERIOR-WALL",
          "UNDERGROUND-WALL",
          "ROOF"
        ]

        @envelopeLevel[3] = [
          "WINDOW",
          "DOOR"]

        @non_utype_commands = Array.new()
        @non_utype_commands = [
          "TITLE",
          "SITE-PARAMETERS",
          "BUILD-PARAMETER",
          "LOADS_REPORT",
          "SYSTEMS-REPORT",
          "MASTERS-METERS",
          "ECONOMICS-REPORT",
          "PLANT-REPORT",
          "LOADS-REPORT",
          "COMPLIANCE"
        ]
        @one_line_commands = Array.new()
        @one_line_commands = ["INPUT","RUN-PERIOD","DIAGNOSTIC","ABORT", "END", "COMPUTE", "STOP", "PROJECT-DATA"]
      end

      # Determines the DOE scope, either envelope or hvac (Window, Wall, Space Floor) or (System->Plant) 
      # Hierarchy) this is required to determine parent/child relationships in the building. 
      def doe_scope
        scope = "none"
        @envelopeLevel.each_index do |index|
          @envelopeLevel[index].each do |name|
            if (@commandName == name )
              scope = "envelope"
            end
          end
        end

        @hvacLevel.each_index do |index|
          @hvacLevel[index].each do |name|
            if (@commandName == name )
              scope = "hvac"
            end
          end
        end
        return scope
      end
      # Determines the DOE scope depth (Window, Wall, Space Floor) or (System->Plant) Hierarchy)
      def depth
        level = 0
        scopelist=[]
        if (doe_scope == "hvac")
          scopelist = @hvacLevel
        else
          scopelist = @envelopeLevel
        end
        scopelist.each_index do |index|
          scopelist[index].each do |name|
            if (@commandName == name )
              level = index
            end
          end
        end
        return level
      end

      #Outputs the command in DOE 2.2 format.
      def output
        return basic_output()
      end

      #Outputs the command in DOE 2.2 format.
      def basic_output()
        temp_string = String.new()

        if (@utype != "")
          temp_string = temp_string + "#{@utype} = "
        end
        temp_string = temp_string + @commandName
        temp_string = temp_string + "\n"
        @keywordPairs.each {|array| temp_string = temp_string +  "\t#{array[0]} = #{array[1]}\n" }
        temp_string = temp_string + "..\n"

        temp_string = temp_string + "$Parents\n"
        @parents.each do |array|
          temp_string = temp_string +  "$\t#{array.utype} = #{array.commandName}\n"
        end
        temp_string = temp_string + "..\n"

        temp_string = temp_string + "$Children\n"
        @children.each {|array| temp_string = temp_string +  "$\t#{array.utype} = #{array.commandName}\n" }
        temp_string = temp_string + "..\n"

      end

      # Creates the command informantion based on DOE 2.2 syntax.
      def get_command_from_string(command_string)
        #Split the command based on the equal '=' sign.
        remove = ""
        keyword=""
        value=""

        if (command_string != "")
          #Get command and u-value
          if ( command_string.match(/(^\s*(\".*?\")\s*\=\s*(\S+)\s*)/) )
            @commandName=$3.strip
            @utype = $2.strip
            remove = Regexp.escape($1)

          else
            # if no u-value, get just the command.
            command_string.match(/(^\s*(\S*)\s)/ )
            remove = Regexp.escape($1)
            @commandName=$2.strip
          end
          #Remove command from string.

          command_string.sub!(/#{remove}/,"")
          command_string.strip!


          #Loop throught the keyword values.
          while ( command_string.length > 0 )
            #DOEMaterial, or SCHEDULES
            if ( command_string.match(/(^\s*(MATERIAL|DAY-SCHEDULES|WEEK-SCHEDULES)\s*(\=?)\s*(.*)\s*)/))
              #puts "Bracket"
              keyword = $2.strip
              value = $4.strip
              remove = Regexp.escape($1)
              #Stars
            elsif ( command_string.match(/(^\s*(\S*)\s*(\=?)\s*(\*.*?\*)\s*)/))
              #puts "Bracket"
              keyword = $2.strip
              value = $4.strip
              remove = Regexp.escape($1)

              #Brackets
            elsif ( command_string.match(/(^\s*(\S*)\s*(\=?)\s*(\(.*?\))\s*)/))
              #puts "Bracket"
              keyword = $2.strip
              value = $4.strip
              remove = Regexp.escape($1)
              #Quotes
            elsif ( command_string.match(/(^\s*(\S*)\s*(\=?)\s*(".*?")\s*)/) )
              #puts "Quotes"
              keyword = $2
              value = $4.strip
              remove = Regexp.escape($1)
              #single command
            elsif command_string.match(/(^\s*(\S*)\s*(\=?)\s*(\S+)\s*)/)
              #puts "Other"
              keyword = $2
              value = $4.strip
              remove = Regexp.escape($1)
            end
            #puts "DOE22::DOECommand: #{command_string}"
            #puts "K = #{keyword} V = #{value}\n"
            if (keyword != "")
              set_keyword_value(keyword,value)
            end
            command_string.sub!(/#{remove}/,"")
          end
          #puts "Keyword"
          #puts keywordPairs
        end
      end

      #Returns an array of the commands parents.
      def get_parents
        return @parents
      end

      #Returns an array of the commands children.
      def get_children
        return children
      end

      # Gets name.
      def get_name()
        return @utype
      end

      # Check if keyword exists.
      def check_keyword?(keyword)
        @keywordPairs.each do |pair|
          if pair[0] == keyword
            return true
          end
        end
        return false
      end

      # Gets the parent of command...if any.
      def get_parent(keyword)

        get_parents().each do |findcommand|

          if ( findcommand.commandName == keyword)
            return findcommand
          end
        end
        return nil

      end

      #Gets children of command, if any.
      def get_children_of_command(keyword)
        array = Array.new()
        children.each do |findcommand|
          if ( findcommand.commandName == keyword)
            array.push(findcommand)
          end
        end
        return array
      end

      def name()
        return utype
      end

      private
      def add_keyword_pair(keyword,pair)
        array = [keyword,pair]
        keywordPairs.push(array)
      end
    end
    class DOEZone < BTAP::EQuest::DOECommand
      attr_accessor :space
      # a vector of spaces used when the declaration of space is "combined"
      attr_accessor :space_uses
      # a lighting object which stores the lighting characteristics of each zone
      attr_accessor :lighting
      #defines the thermal mass characteristics of the zone.
      #could be a string object or a user defined object
      attr_accessor :thermal_mass
      # stores a constant floating value of the amount of air leakage,
      #accoriding to rule #4.3.5.9.
      attr_accessor :air_leakage
      # this will be a vector consisting of heat transfer objects,
      # which contains a pointer to the adjacent thermal block and a pointer
      # to the wall in between them
      attr_accessor :heat_transfers
      def initialize
        super()
      end

      def output

        temp_string = basic_output()
        if (@space == nil)
          temp_string = temp_string + "$ No space found to match zone!\n"
        else
          temp_string = temp_string + "$Space\n"
          temp_string = temp_string +  "$\t#{@space.utype} = #{@space.commandName}\n"
        end
        return temp_string
      end

      # This method finds all the exterior surfaces, ie. Exterior Wall and Roof
      # Output => surfaces as an Array of commands
      def get_exterior_surfaces()
        surfaces = Array.new()
        @space.get_children().each do |child|

          if child.commandName == "EXTERIOR-WALL" ||
              child.commandName == "ROOF"
            surfaces.push(child)
          end
        end
        return surfaces
      end

      # This method returns all the children of the space
      def get_children()
        return @space.get_children()
      end



      # This method returns the area of the space
      def get_area()
        @space.get_area()
      end


     

      def convert_to_openstudio(model,runner = nil)
        if self.space.get_shape() == "NO-SHAPE"
          BTAP::runner_register("Info", "Thermal Zone contains a NO-SHAPE space named. OS does not support no shape spaces.  Thermal Zone will not be created.",runner)
        else
          os_zone = OpenStudio::Model::ThermalZone.new(model)
          os_zone.setAttribute("name", self.name)
          #set space to thermal zone
          OpenStudio::Model::getSpaceByName(model,self.space.name).get.setThermalZone(os_zone)
          BTAP::runner_register("Info", "\tThermalZone: " + self.name + " created",runner)
        end
      end
    end
    

    class DOESurface < DOECommand
      attr_accessor :construction
      attr_accessor :polygon

      def initialize
        super()
        @polygon = nil
      end

      def get_azimuth()
        #puts OpenStudio::radToDeg( OpenStudio::getAngle(OpenStudio::Vector3d.new(0.0, 0.0, 0.0), OpenStudio::Vector3d.new(1.0, 0.0, 0.0) ) )
        if check_keyword?("LOCATION")
          case get_keyword_value("LOCATION")
          when /SPACE-\s*V\s*(.*)/
            index = $1.strip.to_i - 1
            point0 = self.get_parent("SPACE").polygon.point_list[index]
            point1 = self.get_parent("SPACE").polygon.point_list[index + 1] ? get_parent("SPACE").polygon.point_list[index + 1] : get_parent("SPACE").polygon.point_list[0]
            edge = point1-point0

            sign = OpenStudio::Vector3d.new(1.0, 0.0, 0.0).dot(( edge )) > 0 ? 1 :-1
            angle = OpenStudio::radToDeg( sign * OpenStudio::getAngle(OpenStudio::Vector3d.new(1.0, 0.0, 0.0), ( point1 - point0 ) ) )

            #since get angle only get acute angles we need to get sign and completment for reflex angle
            angle = angle + 180 if edge.y < 0
            return angle
          when "FRONT"
            return  OpenStudio::radToDeg( OpenStudio::getAngle(OpenStudio::Vector3d.new(0.0, 1.0, 0.0), ( get_parent("SPACE").polygon.point_list[1] - get_parent("SPACE").polygon.point_list[0] ) ) )
          when "RIGHT"
            return OpenStudio::radToDeg( OpenStudio::getAngle(OpenStudio::Vector3d.new(0.0, 1.0, 0.0), ( get_parent("SPACE").polygon.point_list[2] - get_parent("SPACE").polygon.point_list[1] ) ) )
          when "BACK"
            return OpenStudio::radToDeg( OpenStudio::getAngle(OpenStudio::Vector3d.new(0.0, 1.0, 0.0), ( get_parent("SPACE").polygon.point_list[3] - get_parent("SPACE").polygon.point_list[2] ) ) )
          when "LEFT"
            return OpenStudio::radToDeg( OpenStudio::getAngle(OpenStudio::Vector3d.new(0.0, 1.0, 0.0), ( get_parent("SPACE").polygon.point_list[0] - get_parent("SPACE").polygon.point_list[3] ) ) )
          end
        end
        return self.check_keyword?("AZIMUTH")? self.get_keyword_value("AZIMUTH").to_f : 0.0
      end

      def get_tilt()
        #puts OpenStudio::radToDeg( OpenStudio::getAngle(OpenStudio::Vector3d.new(0.0, 0.0, 0.0), OpenStudio::Vector3d.new(1.0, 0.0, 0.0) ) )
        if check_keyword?("LOCATION")
          case get_keyword_value("LOCATION")
          when "FRONT","BACK","LEFT","RIGHT",/SPACE-\s*V\s*(.*)/
            return  90.0
          when "TOP"
            return 0.0
          when "BOTTOM"
            return 180.0
          end
        end
        return self.check_keyword?("TILT")? self.get_keyword_value("TILT").to_f : 0.0
      end




      def get_origin()
        space_xref = self.check_keyword?("X")? self.get_keyword_value("X").to_f : 0.0
        space_yref = self.check_keyword?("Y")? self.get_keyword_value("Y").to_f : 0.0
        space_zref = self.check_keyword?("Z")? self.get_keyword_value("Z").to_f : 0.0
        return OpenStudio::Vector3d.new(space_xref,space_yref,space_zref)
      end
      
      def get_sub_surface_origin()
        height = ""
        BTAP::runner_register("Info", "geting origin",runner)
        origin = ""
        if self.check_keyword?("X") and self.check_keyword?("Y") and self.check_keyword?("Z")
          BTAP::runner_register("Info", "XYZ definition",runner)
          space_xref = self.get_keyword_value("X").to_f
          space_yref = self.get_keyword_value("Y").to_f
          space_zref = self.get_keyword_value("Z").to_f
          return OpenStudio::Vector3d.new(space_xref,space_yref,space_zref)
        end
        BTAP::runner_register("Info", get_name(),runner)
        array = Array.new()
        origin = ""
        floor = get_parent("FLOOR")
        space = get_parent("SPACE")
        case space.get_keyword_value("ZONE-TYPE")
        when "PLENUM"
          height = floor.get_keyword_value("FLOOR-HEIGHT").to_f  - floor.get_keyword_value("SPACE-HEIGHT").to_f
        when "CONDITIONED","UNCONDITIONED"
          height =  space.check_keyword?("HEIGHT") ? space.get_keyword_value("HEIGHT").to_f : floor.get_keyword_value("SPACE-HEIGHT").to_f

        end
        BTAP::runner_register("Info", "Space is #{space.get_shape}",runner)
        case space.get_shape
        when "BOX"
          BTAP::runner_register("Info", "Box Space Detected....",runner)
          #get height, width and depth of box.
          height = space.check_keyword?("HEIGHT").to_f ? space.check_keyword?("HEIGHT") : height
          width = space.get_keyword_value("WIDTH").to_f
          depth = space.get_keyword_value("DEPTH").to_f

          case get_keyword_value("LOCATION")
          when "TOP"
            BTAP::runner_register("Info", "Top of Box....",runner)
            #counter clockwise
            origin = OpenStudio::Point3d.new(0.0,0.0,height)

          when "BOTTOM"
            BTAP::runner_register("Info", "Bottom of Box....",runner)
            #counter clockwise
            origin = OpenStudio::Point3d.new( 0.0, 0.0, 0.0 )
          when "FRONT"
            BTAP::runner_register("Info", "Front of Box....",runner)
            #counter clockwise
            origin = OpenStudio::Point3d.new( 0.0, 0.0, 0.0 )
          when "RIGHT"
            BTAP::runner_register("Info", "Right of Box....",runner)
            #counter clockwise
            origin = OpenStudio::Point3d.new(width, 0.0, 0.0)
          when "BACK"
            BTAP::runner_register("Info", "Back of Box....",runner)
            #counter clockwise
            origin = OpenStudio::Point3d.new(width,depth,0.0)
          when "LEFT"
            BTAP::runner_register("Info", "Left of Box....",runner)
            #counter clockwise
            origin = OpenStudio::Point3d.new(0.0,depth,0.0)

          end

        when "POLYGON"
          #puts "Polygon Space definition detected..."
          if check_keyword?("LOCATION")
            #puts "LOCATION surface definition detected..."
            case get_keyword_value("LOCATION")
            when "BOTTOM"
              origin = OpenStudio::Vector3d.new(0.0,0.0, 0.0 )
            when "TOP"
              #puts "TOP surface definition detected..."
              #need to move floor polygon up to space height for top. Using Transformation.translation matrix for this.
                
              origin = OpenStudio::Vector3d.new(0.0,0.0, height ) #to-do!!!!!!!!!!!
            when /SPACE-\s*V\s*(.*)/
              #puts "SPACE-V#{$1} surface definition detected..."
              index = $1.strip.to_i - 1
              point0 = space.polygon.point_list[index]
              #counter clockwise
              origin = OpenStudio::Point3d.new( point0.x, point0.y, 0.0)

            end
          else
            #puts "CATCH-ALL for surface definition.."
            #nasty. The height is NOT defined if the height is the same as the space height...so gotta get it from it's parent space. 
            space_height =  space.check_keyword?("HEIGHT") ? space.get_keyword_value("HEIGHT").to_f : floor.get_keyword_value("SPACE-HEIGHT").to_f
            height = self.check_keyword?("HEIGHT") ? self.get_keyword_value("HEIGHT").to_f : space_height
            width =  self.get_keyword_value("WIDTH").to_f
            #origin
            origin = OpenStudio::Point3d.new(width,0.0,0.0)
          end
        when "NO-SHAPE"
          raise("Using SHAPE = NO-SHAPE deifnition for space is not supported by open Studio")
        end
        
        origin =  OpenStudio::Vector3d.new(origin.x,origin.y,origin.z)
        #puts "Surface origin vector is #{origin}"
        return origin
      end
      


      def get_transformation_matrix
        #Rotate points around z (azimuth) and x (Tilt)
        translation = OpenStudio::createTranslation(self.get_origin) 
        e_a = OpenStudio::EulerAngles.new(	OpenStudio::degToRad( self.get_tilt ), 0.0, OpenStudio::degToRad( 180.0 - self.get_azimuth  ) )
        rotations = OpenStudio::Transformation::rotation(e_a)
        return  translation * rotations
      end

      def get_3d_polygon()
        array = Array.new()
        origin = ""
        floor = get_parent("FLOOR")
        space = get_parent("SPACE")
        case space.get_keyword_value("ZONE-TYPE")
        when "PLENUM"
          height = floor.get_keyword_value("FLOOR-HEIGHT").to_f  - floor.get_keyword_value("SPACE-HEIGHT").to_f
        when "CONDITIONED","UNCONDITIONED"
          height =  space.check_keyword?("HEIGHT") ? space.get_keyword_value("HEIGHT").to_f : floor.get_keyword_value("SPACE-HEIGHT").to_f
        end

        #if the surface has been given a polygon. Then use it.
        if check_keyword?("POLYGON")
          #          puts "Polygon Surface Detected...Doing a local transform.."
          #          
          #          puts "Point List"
          #          puts self.polygon.point_list
          #          puts "Origin"
          #          puts self.get_origin
          #          puts "azimuth"
          #          puts self.get_azimuth
          #          puts "tilt"
          #          puts self.get_tilt
          

          
          #all other methods below create points relative to the space. This method however, need to be transformed.
          array = self.polygon.point_list


          #if surfaces are defined by shape of space.
        else
          case space.get_shape
          when "BOX"
            BTAP::runner_register("Info", "Box Space Detected....",runner)
            #get height, width and depth of box.
            height = space.check_keyword?("HEIGHT").to_f ? space.check_keyword?("HEIGHT") : height
            width = space.get_keyword_value("WIDTH").to_f
            depth = space.get_keyword_value("DEPTH").to_f

            case get_keyword_value("LOCATION")
            when "TOP"
              #puts "Top of Box...."
              #counter clockwise
              origin = OpenStudio::Point3d.new(0.0,0.0,height)
              p2 = OpenStudio::Point3d.new(width,0.0,height)
              p3 = OpenStudio::Point3d.new(width,depth,height)
              p4 = OpenStudio::Point3d.new(0.0,depth,height)
              array =  [origin,p2,p3,p4]
            when "BOTTOM"
              #puts "Bottom of Box...."
              #counter clockwise
              origin = OpenStudio::Point3d.new( 0.0, 0.0, 0.0 )
              p2 = OpenStudio::Point3d.new( 0.0, depth, 0.0)
              p3 = OpenStudio::Point3d.new( width, depth, 0.0)
              p4 = OpenStudio::Point3d.new( width,0.0 ,0.0 )
              array =  [origin,p2,p3,p4]
            when "FRONT"
              #puts "Front of Box...."
              #counter clockwise
              origin = OpenStudio::Point3d.new( 0.0, 0.0, 0.0 )
              p2 = OpenStudio::Point3d.new( width,0.0 ,0.0 )
              p3 = OpenStudio::Point3d.new( width, 0.0, height)
              p4 = OpenStudio::Point3d.new( 0.0, 0.0, height)
              array =  [origin,p2,p3,p4]
            when "RIGHT"
              #puts "Right of Box...."
              #counter clockwise
              origin = OpenStudio::Point3d.new(width, 0.0, 0.0)
              p2 = OpenStudio::Point3d.new(width,depth, 0.0)
              p3 = OpenStudio::Point3d.new(width,depth,height)
              p4 = OpenStudio::Point3d.new(width,0.0,height)
              array =  [origin,p2,p3,p4]
            when "BACK"
              #puts "Back of Box...."
              #counter clockwise
              origin = OpenStudio::Point3d.new(width,depth,0.0)
              p2 = OpenStudio::Point3d.new(0.0,depth,0.0)
              p3 = OpenStudio::Point3d.new(0.0,depth,height)
              p4 = OpenStudio::Point3d.new(width,depth,height)
              array =  [origin,p2,p3,p4]
            when "LEFT"
              #puts "Left of Box...."
              #counter clockwise
              origin = OpenStudio::Point3d.new(0.0,depth,0.0)
              p2 = OpenStudio::Point3d.new( 0.0, 0.0, 0.0 )
              p3 = OpenStudio::Point3d.new(0.0, 0.0,height)
              p4 = OpenStudio::Point3d.new(0.0,depth,height)
              array =  [origin,p2,p3,p4]
            end

          when "POLYGON"
            #puts "Polygon Space definition detected..."
            if check_keyword?("LOCATION")
              #puts "LOCATION surface definition detected..."
              case get_keyword_value("LOCATION")
              when "BOTTOM"
                #puts "BOTTOM surface definition detected..."
                #reverse array
                array = space.polygon.point_list.dup
                first = array.pop
                array.insert(0,first).reverse!
              when "TOP"
                #puts "TOP surface definition detected..."
                #need to move floor polygon up to space height for top. Using Transformation.translation matrix for this.
                array = OpenStudio::createTranslation(OpenStudio::Vector3d.new(0.0,0.0, height )) * space.polygon.point_list
              when /SPACE-\s*V\s*(.*)/
                #puts "SPACE-V#{$1} surface definition detected..."
                index = $1.strip.to_i - 1
                point0 = space.polygon.point_list[index]
                point1 = space.polygon.point_list[index + 1] ? space.polygon.point_list[index + 1] : space.polygon.point_list[0]
                #counter clockwise
                origin = OpenStudio::Point3d.new( point0.x, point0.y, 0.0)
                p2 = OpenStudio::Point3d.new(     point1.x, point1.y, 0.0)
                p3 = OpenStudio::Point3d.new(     point1.x, point1.y, height )
                p4 = OpenStudio::Point3d.new(     point0.x, point0.y, height )
                array =  [origin,p2,p3,p4]
              end
            else
              #puts "CATCH-ALL for surface definition.."
              #nasty. The height is NOT defined if the height is the same as the space height...so gotta get it from it's parent space. 
              space_height =  space.check_keyword?("HEIGHT") ? space.get_keyword_value("HEIGHT").to_f : floor.get_keyword_value("SPACE-HEIGHT").to_f
              height = self.check_keyword?("HEIGHT") ? self.get_keyword_value("HEIGHT").to_f : space_height
              width =  self.get_keyword_value("WIDTH").to_f
              #counter clockwise
              origin = OpenStudio::Point3d.new(width,0.0,0.0)
              p2 = OpenStudio::Point3d.new( 0.0,0.0,0.0 )
              p3 = OpenStudio::Point3d.new(0.0,0.0,height)
              p4 = OpenStudio::Point3d.new(width,0.0,height)
              array = [p4, p3, p2, origin]
  

              
            end
          when "NO-SHAPE"
            raise("Using SHAPE = NO-SHAPE deifnition for space is not supported...yet")
          end
        end
        #        if self.check_keyword?("AZIMUTH") or self.check_keyword?("TILT")
        #          puts "Did a transform"
        #          return get_transformation_matrix * array
        #        else
        #          return array
        #        end
        return array
      end


      def get_windows()
        return self.get_children_of_command("WINDOW")
      end

      def get_doors()
        return self.get_children_of_command("DOOR")
      end



      # This method finds all the commands within the building that are "Construction"
      # and if the utype matches, it gets the construction
      def determine_user_defined_construction()
        constructions = @building.find_all_commands("CONSTRUCTION")
        constructions.each do |construction|
          if ( construction.utype == get_keyword_value("CONSTRUCTION") )
            @construction = construction
          end
        end
        return @construction
      end

      #This method will try to convert a DOE inp file to an openstudio file.. 
      def convert_to_openstudio(model,runner = nil)
        #Get 3d polygon of surface and tranform the points based on space origin and the floor origin since they each may use their own co-ordinate base system.
        total_transform = ""
        if self.check_keyword?("AZIMUTH") or self.check_keyword?("TILT")
          total_transform =  get_parent("FLOOR").get_transformation_matrix() * get_parent("SPACE").get_transformation_matrix() * get_transformation_matrix()
        else
          total_transform =  get_parent("FLOOR").get_transformation_matrix() * get_parent("SPACE").get_transformation_matrix()
        end
        surface_points = total_transform * self.get_3d_polygon()
        #Add the surface to the new openstudio model.
        
        os_surface = OpenStudio::Model::Surface.new(surface_points, model)
        #set the name of the surface. 
        os_surface.setAttribute("name", self.name)
        case self.commandName
          #Set the surface boundary condition if it is a ground surface.
        
        when "UNDERGROUND-WALL"
          BTAP::Geometry::Surfaces::set_surfaces_boundary_condition(model,os_surface, "Ground") 
        when "EXTERIOR-WALL","ROOF"
          #this is needed since the surface constructor defaults to a Ground boundary and Floor Surface type 
          #when a horizontal surface is initialized. 
          if os_surface.outsideBoundaryCondition == "Ground" and os_surface.surfaceType == "Floor" 
            os_surface.setSurfaceType("RoofCeiling") 
          end
          BTAP::Geometry::Surfaces::set_surfaces_boundary_condition(model,os_surface, "Outdoors")
        when "INTERIOR-WALL"
          BTAP::Geometry::Surfaces::set_surfaces_boundary_condition(model,os_surface, "Surface")
        end
        
        #Add to parent space that was already created. 
        os_surface.setSpace(OpenStudio::Model::getSpaceByName( model,get_parent("SPACE").name).get )
        #output to console for debugging. 
        BTAP::runner_register("Info", "\tSurface: " + self.name + " created",runner)
        #check if we need to create a mirror surface in another space.
        if self.check_keyword?("NEXT-TO")
          #reverse the points.
          new_array = surface_points.dup
          first = new_array.pop
          new_array.insert(0,first).reverse!
          #...then add the reverse surface to the model and assign the name with a mirror suffix. 
          os_surface_mirror = OpenStudio::Model::Surface.new(new_array, model)
          os_surface_mirror.setAttribute("name", self.name + "-mirror" )
          #Assign the mirror surface to the parent space that is NEXT-TO
          os_surface_mirror.setSpace(OpenStudio::Model::getSpaceByName(model,get_keyword_value("NEXT-TO")).get)
          #output to console for debugging. 
          BTAP::runner_register("Info", "\tSurface: " + self.name + "-mirror"  + " created",runner)
        end #if statement
        
        #Some switches for debugging. 
        convert_sub_surfaces = true
        convert_sub_surfaces_as_surfaces = false
        
        #
        if convert_sub_surfaces
          #convert subsurfaces
          self.get_children().each do |child|
            #Get height and width of subsurface
            height = child.get_keyword_value("HEIGHT").to_f
            width = child.get_keyword_value("WIDTH").to_f
            
          
            #Sum the origin of the surface and the translation of the window
            x = os_surface.vertices.first().x + ( child.check_keyword?("X")?  child.get_keyword_value("X").to_f : 0.0 )
            y = os_surface.vertices.first().y + ( child.check_keyword?("Y")?  child.get_keyword_value("Y").to_f : 0.0 )
            z = os_surface.vertices.first().z
          
            #counter clockwise
            origin = OpenStudio::Point3d.new( x, y , z )
            p2 = OpenStudio::Point3d.new(x + width , y, z )
            p3 = OpenStudio::Point3d.new(x + width , y + height , z )
            p4 = OpenStudio::Point3d.new(x, y + height, z )
            polygon =  [origin,p2,p3,p4]

            #get floot and space rotations
            space_azi = 360.0 - get_parent("SPACE").get_azimuth()
            floor_azi = 360.0 - get_parent("FLOOR").get_azimuth()

          
            tilt_trans = OpenStudio::Transformation::rotation(os_surface.vertices.first(), OpenStudio::Vector3d.new(1.0,0.0,0.0), OpenStudio::degToRad( self.get_tilt ))
            azi_trans = OpenStudio::Transformation::rotation(os_surface.vertices.first(), OpenStudio::Vector3d.new(0.0,0.0,1.0), OpenStudio::degToRad( 360.0 - self.get_azimuth + space_azi + floor_azi  ))
            surface_points =  azi_trans  * tilt_trans * polygon
            if convert_sub_surfaces_as_surfaces
              #Debug subsurface
              os_sub_surface = OpenStudio::Model::Surface.new(surface_points, model)
              #set the name of the surface. 
              os_sub_surface.setAttribute("name", child.name)
              #Add to parent space that was already created. 
              os_sub_surface.setSpace(OpenStudio::Model::getSpaceByName( model,self.get_parent("SPACE").name).get )
            else
              #Add the subsurface to the new openstudio model. 
              os_sub_surface = OpenStudio::Model::SubSurface.new(surface_points, model)
              #set the name of the surface. 
              os_sub_surface.setAttribute("name", child.name )
              #Add to parent space that was already created. 
              os_sub_surface.setSurface(os_surface)
              #output to console for debugging. 
              BTAP::runner_register("Info", "\tSubSurface: " + child.name + " created",runner)
              case child.commandName
              when "WINDOW"
                #By default it is a window. 
              when "DOOR"
                os_sub_surface.setSubSurfaceType( "Door" )
              end #end case.
              
              # Add overhang for subsurface if required. Note this only supports overhangs of width the same as the window.  
              if child.check_keyword?("OVERHANG-D") == true
                offset = 0.0
                offset = child.get_keyword_value("OVERHANG-O").to_f if child.check_keyword?("OVERHANG-O")
                depth = 0.0
                depth = child.get_keyword_value("OVERHANG-D").to_f 
                os_sub_surface.addOverhang(	depth , offset )
              end
              	
            end
          end
        end
      end

    end
    
    #This class allows to manipulate a subsurface (window/door) in inherits from surface. 
    class DOESubSurface < DOESurface

      def initialize
        #run the parent class initialization. 
        super()
      end

      # This method returns the area of the window
      def get_area()
        unless check_keyword?("HEIGHT")  and check_keyword?("WIDTH")
          raise "Error: In the command #{@utype}:#{@command_name} the area could not be evaluated. Either the HEIGHT or WIDTH is invalid.\n #{output}"
        end
        return get_keyword_value("WIDTH").to_f * get_keyword_value("HEIGHT").to_f
      end

      #Return the widow polygon with an origin of zero
      def get_3d_polygon()
        height = get_keyword_value("HEIGHT").to_f
        width = get_keyword_value("WIDTH").to_f
        x = self.check_keyword?("X")?  self.get_keyword_value("X").to_f : 0.0
        y = self.check_keyword?("Y")?  self.get_keyword_value("Y").to_f : 0.0
        #counter clockwise
        origin = OpenStudio::Point3d.new( x, y , 0.0 )
        p2 = OpenStudio::Point3d.new(x + width , y,0.0 )
        p3 = OpenStudio::Point3d.new(x + width , y + height , 0.0 )
        p4 = OpenStudio::Point3d.new(x, y + height,0.0 )
        return [origin,p2,p3,p4]
      end

      #Returns the origin relative to the parent surface. 
      def get_origin()
        origin = get_parent_surface().get_sub_surface_origin()
        return origin
      end

      #Gets azimuth, based on parent surface. 
      def get_azimuth()
        get_parent_surface().get_azimuth()
      end

      #gets tilt based on parent surface. 
      def get_tilt()
        get_parent_surface().get_tilt()
      end

      #return the parent surface of the subsurface. 
      def get_parent_surface()
        get_parents().each do |findcommand|
          [
            "EXTERIOR-WALL",
            "INTERIOR-WALL",
            "UNDERGROUND-WALL",
            "ROOF"
          ].each do |type|

            if ( findcommand.commandName == type)
              return findcommand
            end
          end
        end
        raise("#no parent surface defined!")
      end

      #returns the translation matrix reletive to its parent ( the surface ) 
      def get_transformation_matrix
        return  self.get_rotation_matrix() * self.get_translation_matrix()
      end
      
      def get_rotation_matrix
        #Rotate points around z (azimuth) and x (Tilt)
        e_a = OpenStudio::EulerAngles.new(	OpenStudio::degToRad( self.get_tilt ), 0.0, OpenStudio::degToRad( 0.0  ) )
        rotations = OpenStudio::Transformation::rotation(e_a)
        return  rotations 
      end
      
      def get_translation_matrix
        #Rotate points around z (azimuth) and x (Tilt)
        translation = OpenStudio::createTranslation(self.get_origin) 
        return  translation 
      end
      
      
      
      

      # this will translate the subsurface to the openstudio model. 
      def convert_to_openstudio(model)        
      end
    end
    
    #an attempt to organize the BDLlibs...don't think it works well at all. 
    class DOEBDLlib

      attr_accessor :db, :materials

      include Singleton




      # stores the name of the individual materials

      attr_accessor :commandList
      # stores the name of the individual layers


      def initialize
        @commandList = Array.new()
        @db = Sequel.sqlite
        @db.create_table :materials do # Create a new table
          primary_key :id, :integer, :auto_increment => true
          column :command_name, :text
          column :name, :text
          column :type, :text
          column :thickness, :float
          column :conductivity, :float
          column :resistance, :float
          column :density, :float
          column :spec_heat, :float
        end
        @materials = @db[:materials] # Create a dataset

        @db.create_table :layers do # Create a new table
          primary_key :id, :integer, :auto_increment => true
          column :command_name, :text
          column :name, :text
          column :material, :text
          column :inside_film_res, :float
        end
        @layers = @db[:layers] # Create a dataset


        store_material()
      end



      def find_material(utype)
        posts =  @materials.filter(:name => utype)
        record = posts.first()
        #Create the new command object.
        command = DOE2::DOECommand.new()
        #Insert the collected information into the object.
        command.commandName = "MATERIAL"
        command.utype = record[:name]
        command.set_keyword_value("TYPE", record[:type])
        command.set_keyword_value("THICKNESS", record[:thickness])
        command.set_keyword_value("CONDUCTIVITY", record[:conductivity])
        command.set_keyword_value("DENSITY", record[:density])
        command.set_keyword_value("SPECIFIC HEAT", record[:spec_heat])

        return command
      end


      def find_layer(utype)
        posts =  @layers.filter(:name => utype)
        record = posts.first()
        #Create the new command object.
        command = DOE2::DOECommand.new()
        #Insert the collected information into the object.
        command.commandName = "LAYERS"
        command.utype = record[:name]
        command.set_keyword_value("MATERIAL", record[:material])
        command.set_keyword_value("THICKNESS", record[:thickness])
        command.set_keyword_value("CONDUCTIVITY", record[:conductivity])
        command.set_keyword_value("DENSITY", record[:density])
        command.set_keyword_value("SPECIFIC HEAT", record[:spec_heat])

        return command
      end





      # stores the material information using keywordPairs into the command structure
      # accessed using the find_command method
      private
      def store_material

        begin
          f = File.open("../Resources/DOE2_2/bdllib.dat")
        rescue
          f = File.open("Resources/DOE2_2/bdllib.dat")
        end

        lines = f.readlines
        # Iterating through the file.
        lines.each_index do |i|
          command_string = ""
          # If we find a material.
          if lines[i].match(/\$LIBRARY-ENTRY\s(.{32})MAT .*/)
            #Get the name strips the white space.
            name = ("\""+$1.strip + "\"")

            #Is this the last line?
            command_string = get_data(command_string, i, lines)
            #Extract data for material type PROPERTIES.
            if (match = command_string.match(/^\s*TYPE\s*=\s*(\S*)\s*TH\s*=\s*(\S*)\s*COND\s*=\s*(\S*)\s*DENS\s*=\s*(\S*)\s*S-H\s*=\s*(\S*)\s*$/) )
              #Create the new command object.
              command = DOE2::DOECommand.new()
              #Insert the collected information into the object.
              command.commandName = "MATERIAL"
              command.utype = name
              command.set_keyword_value("TYPE", $1.strip)
              command.set_keyword_value("THICKNESS", $2.strip.to_f.to_s)
              command.set_keyword_value("CONDUCTIVITY", $3.strip.to_f.to_s)
              command.set_keyword_value("DENSITY", $4.strip.to_f.to_s)
              command.set_keyword_value("SPECIFIC HEAT", $5.strip.to_f.to_s)
              #Push the object into the array for storage.
              @commandList.push(command)
              @materials << {:name => name,
                :command_name => 'MATERIAL',
                :type =>  $1.strip,
                :thickness =>  $2.strip.to_f.to_s,
                :conductivity =>  $3.strip.to_f.to_s,
                :density =>  $4.strip.to_f.to_s,
                :spec_heat =>  $5.strip.to_f.to_s}



              #Extract data for material type RESISTANCE.
            elsif (match = command_string.match(/^\s*TYPE\s*=\s*(\S*)\s*RES\s*=\s*(\S*)\s*$/) )
              command = DOE2::DOECommand.new()
              command.commandName = "MATERIAL"
              command.utype = name
              command.set_keyword_value("TYPE", $1.strip)
              command.set_keyword_value("RESISTANCE", $2.strip.to_f.to_s)
              #Push the object into the array for storage.
              @materials << {:name => name,
                :command_name => 'MATERIAL',
                :type =>  $1.strip,
                :resistance =>  $2.strip.to_f.to_s}

              @commandList.push(command)
            else
              raise("data not extracted")
            end
          end

          if lines[i].match(/\$LIBRARY-ENTRY\s(.{32})LA .*/)
            #Get the name
            name = ("\""+$1.strip + "\"")
            #Is this the last line?
            command_string = get_data(command_string, i, lines)
            #Extract data into the command.
            if (match = command_string.match(/^\s*MAT\s*=\s*(.*?)\s*I-F-R\s*=\s*(\S*)\s*$/) )
              command = DOE2::DOECommand.new()
              command.commandName = "LAYERS"
              command.utype = name
              command.set_keyword_value("MATERIAL",$1)
              #Push the object into the array for storage.
              @layers << {:name => name,
                :command_name => 'LAYER',
                :material =>  $1.strip,
                :inside_film_res =>  $2.strip.to_f.to_s}
              @commandList.push(command)
            else
              raise("data not extracted")
            end
          end
        end
      end

      private
      # This method will get all the
      def get_data(command_string, i, lines)
        #Do this while this is NOT the last line of data.
        while (! lines[i].match(/^(.*?)\.\.\s*(.{6})?\s*?(\d*)?/) )
          #Grab all the data in between.
          if ( lines[i].match(/^\$.*$/) )
          elsif ( myarray = lines[i].match(/^(.*?)\s*(.{6})?\s*?(\d*)?\s*$/) )
            command_string = command_string + $1.strip
          end
          #Increment counter.
          i = i + 1
        end
        #Get the last line
        lines[i].match(/^(.*?)\.\.\s*(.{6})?\s*?(\d*)?/)
        command_string = command_string + $1.strip
        if command_string == ""
          raise("error")
        end
        i  = i + 1
        command_string
      end
    end
    
    #class that 
    class DOEExteriorWall < DOESurface

      def initialize
        #call the parent class. 
        super()
      end

      # This method finds the area of the exterior wall
      def get_area()
        OpenStudio::getArea(self.get_3d_polygon())
      end

      #This method finds the floor parent
      def get_floor()
        get_parent("FLOOR")
      end

      #This method finds the space parent command
      def get_space()
        get_parent("SPACE")
      end

      #This method gets the construction command
      def get_construction_name()
        get_keyword_value("CONSTRUCTION")
      end

      #This method returns the window area
      def get_window_area()
        get_children_area("WINDOW")
      end

      #This method returns the door area
      def get_door_area()
        get_children_area("DOOR")
      end

      # This method returns the difference between the wall area and the window
      # and door
      def get_opaque_area()
        get_area.to_f - get_window_area().to_f - get_door_area().to_f
      end

      # This method returns the fraction of the wall dominated by the window
      def get_fwr()
        get_window_area().to_f / get_area.to_f
      end

      # This method returns the area of the children classes based on the given
      # commandname.
      # Input => A command_name as a String
      # Output => Total area as a float
      def get_children_area(scommand_name)
        area = 0.0
        @children.each do |child|

          if child.commandName == scommand_name
            area = child.get_area() + area
          end
        end
        return area
      end

      # This method checks if the construction only has a defined U-value
      def just_u_value?()
        @construction.check_keyword?("U-VALUE")
      end


    end
    
    

    

    
    #The interface for the roof command.. same as parent. 
    class DOERoof < DOECommand
      def initialize
        super()
      end

      # This method finds the area of the roof
      def get_area

        # Finds the floor and space parents and assigns them to @floor and @space
        # variables to be used later
        parent = get_parents
        parent.each do |findcommand|
          if ( findcommand.commandName == "FLOOR" )
            @floor = findcommand
          end
          if ( findcommand.commandName == "SPACE")
            @space = findcommand
          end
        end

        # Get the keyword value for location
        begin
          location = get_keyword_value("LOCATION")
        rescue
        end

        # Get the keyword value for polygon
        begin
          polygon_id = get_keyword_value("POLYGON")
        rescue
        end

        # if the polygon_id keyword value was nil and the location value was nil, then
        # the height and width are directly defined within the "roof" command


        if  ( location == "BOTTOM" || location == "TOP") && (@space.get_shape != "BOX")
          return @space.polygon.get_area

        elsif ( location == nil  && polygon_id == nil )
          height = get_keyword_value("HEIGHT")
          width = get_keyword_value("WIDTH")
          height = height.to_f
          width = width.to_f
          return height * width
        elsif ( location == nil && polygon_id != nil)
          return @space.polygon.get_area


          # if the location was defined as "SPACE...", it is immediately followed by a
          # vertex, upon which lies the width of the roof
        elsif location.match(/SPACE.*/)
          location = location.sub( /^(.{6})/, "")
          width = @space.polygon.get_length(location)
          height = @floor.get_space_height
          return width * height
          # if the shape was a box, the width and height would be taken from the
          # "SPACE" object
        elsif ( @space.get_shape == "BOX" )
          width = @space.get_width
          height = @space.get_height
          return width * height
        else
          raise "The area could not be evaluated"
        end
      end

      #returns tilt of roof surface. 
      def get_tilt()
        if check_keyword?("TILT") then return get_keyword_value("TILT").to_f
        else
          if check_keyword?("LOCATION")
            location = get_keyword_value("LOCATION")
            case location
            when "TOP"
              return 0.0
            when "BOTTOM"
              return 180.0
            when "LEFT", "RIGHT", "BACK", "FRONT"
              return 90.0
            end
          end
          # If it is a polygon or not defined, set to DOE default = 0.0
          return 0
        end
      end

      # This method returns the Azimuth value as a FLOAT if it exists
      # It first checks if the azimuth keyword value is present within the roof
      # command itself. If it does not find this, then it checks for the location
      # keyword and assigns the correct azimuth depending on the azimuth of the parent
      # space. However, if the shape of the parent space is defined as a polygon, then it
      # searches for the location of the roof and uses the polygon's get-azimuth for the vertex
      # to return the azimuth of the roof

      #NOTE: The FRONT is defined as 0, going clockwise, ie. RIGHT = 90 degrees

      #OUTPUT: Azimuth between the parent SPACE and the ROOF
      def get_azimuth()
        space = get_parent("SPACE")
        if check_keyword?("AZIMUTH") then return get_keyword_value("AZIMUTH").to_f
        else
          if check_keyword?("LOCATION")
            location = get_keyword_value("LOCATION")

            case location
            when "TOP"
              raise "Exception: Azimuth does not exist"
            when "BOTTOM"
              raise "Exception: Azimuth does not exist"
            when "FRONT"
              return 0.0 + space.get_azimuth
            when "RIGHT"
              return 90.0 + space.get_azimuth
            when "BACK"
              return 180.0 + space.get_azimuth
            when "LEFT"
              return 270.0 + space.get_azimuth
            end
          end
          if space.get_keyword_value("SHAPE") == "POLYGON"
            space_vertex = get_keyword_value("LOCATION")
            space_vertex.match(/SPACE-(.*)/)
            vertex = $1.strip
            return space.polygon.get_azimuth(vertex)
          end

        end
      end

      # This method returns the Azimuth value as a FLOAT if it exists
      # It first checks if the azimuth keyword value is present within the roof
      # command itself. If it does not find this, then it checks for the location
      # keyword and assigns the correct azimuth depending on the azimuth of the parent
      # space. However, if the shape of the parent space is defined as a polygon, then it
      # searches for the location of the roof and uses the polygon's get-azimuth for the vertex
      # and adding it on to the overall azimuth to get the Absolute Azimuth from True North

      #NOTE: The FRONT is defined as 0, going clockwise, ie. RIGHT = 90 degrees

      #OUTPUT: Azimuth between ROOF and TRUE NORTH
      def get_absolute_azimuth
        space = get_parent("SPACE")
        if check_keyword?("AZIMUTH")
          azimuth = get_keyword_value("AZIMUTH").to_f
          space_azimuth = space.get_absolute_azimuth
          return azimuth + space_azimuth
        else
          if check_keyword?("LOCATION")
            location = get_keyword_value("LOCATION")
            case location
            when "TOP"
              raise "Exception: Azimuth does not exist"
            when "BOTTOM"
              raise "Exception: Azimuth does not exist"
            when "FRONT"
              return 0.0 + space.get_absolute_azimuth
            when "RIGHT"
              return 90.0 + space.get_absolute_azimuth
            when "BACK"
              return 180.0 + space.get_absolute_azimuth
            when "LEFT"
              return 270.0 + space.get_absolute_azimuth
            end
          end
          if space.get_keyword_value("SHAPE") == "POLYGON"
            space_vertex = get_keyword_value("LOCATION")
            space_vertex.match(/SPACE-(.*)/)
            vertex = $1.strip
            return space.polygon.get_azimuth(vertex) + space.get_absolute_azimuth
          end
        end
      end
    end
    #Interface for the DOESpace Command. 
    class DOESpace < DOECommand
      attr_accessor :polygon
      attr_accessor :zone
      def initialize

        super()
      end

      #this outputs the command to a string. 
      def output
        temp_string = basic_output()
        if @polygon != nil
          temp_string = temp_string + "$Polygon\n"
          temp_string = temp_string +  "$\t#{@polygon.utype} = #{@polygon.commandName}\n"
        end
        if @zone != nil
          temp_string = temp_string + "$Zone\n"
          temp_string = temp_string +  "$\t#{@zone.utype} = #{@zone.commandName}\n"
        end
        return temp_string
      end

      # This method finds the area of the space
      def get_area

        # get the keyword value of shape
        shape = get_keyword_value("SHAPE")

        # if the shape value is nil, or it is defined as "NO-SHAPE", the get_area value
        # would be defined, and would represent the get_area of the space
        if ( shape == nil || shape == "NO-SHAPE")
          area = get_keyword_value("AREA")
          area = area.to_f
          return area

          # if the shape value is "BOX", the height and width key values are given,
          # and the get_area would be defined as their product
        elsif ( shape == "BOX" )
          height = get_keyword_value("HEIGHT")
          width = get_keyword_value("WIDTH")
          height = height.to_f
          width = width.to_f
          return height * width

          # if the shape value is defined as a polygon , the get_area of the polygon would
          # represent the get_area of the space
        elsif ( shape == "POLYGON")
          return @polygon.get_area
        else
          raise "Error: The area could not be evaluated. Please check inputs\n "

        end
      end

      # This method finds the volume of the space
      def get_volume

        # get the keyword value of "SHAPE"
        shape = get_keyword_value("SHAPE")

        # if the shape value returns nil, or is defined as "NO-SHAPE", the volume is
        # given directly
        if ( shape == nil || shape == "NO-SHAPE")
          volume = get_keyword_value("VOLUME")
          volume = volume.to_f
          return volume

          # if the shape is defined as a "BOX", the values for height, width, and
          # depth are given, from which you can get the volume
        elsif ( shape == "BOX" )
          height = get_keyword_value("HEIGHT")
          width = get_keyword_value("WIDTH")
          depth = get_keyword_value("DEPTH")
          height = height.to_f
          width = width.to_f
          depth = depth.to_f
          return height * width * depth

          # if the shape is defined as a "POLYGON", the get_area is defined as the area
          # of the polygon, and the height is given by the value of "HEIGHT"
        elsif ( shape == "POLYGON")
          height = getKeywordvalue("HEIGHT")
          temp = get_keyword_value("POLYGON")
          height = height.to_f
          @polygon.utype = temp
          return @polygon.get_area * height
        else
          raise "Error: The volume could not be evaluated. Please check inputs\n "

        end

      end

      def get_height()
        if check_keyword?("HEIGHT") then return get_keyword_value("HEIGHT").to_f end
        return get_floor.get_keyword_value("SPACE-HEIGHT").to_f
      end

      def get_width
        width = get_keyword_value("WIDTH")
        width = width.to_f
        return width
      end

      def get_depth
        depth = get_keyword_value("DEPTH")
        depth = depth.to_f
        return depth
      end

      def get_shape
        return "NO-SHAPE" unless check_keyword?("SHAPE")
        return get_keyword_value("SHAPE")
      end

      def get_floor
        get_parent("FLOOR")
      end


      def get_origin()
        space_origin = nil
        if check_keyword?("LOCATION") and ( not self.check_keyword?("X") or not self.check_keyword?("Y") or not self.check_keyword?("Z") )
          zero = OpenStudio::Point3d.new( 0.0, 0.0, 0.0 )
          case get_keyword_value("LOCATION")
          when /FLOOR-\s*V\s*(.*)/
            index = $1.strip.to_i - 1
            surf_vector =  get_parent("FLOOR").polygon.point_list[index] - zero
          when "FRONT"
            surf_vector =  get_parent("FLOOR").polygon.point_list[0] - zero
          when "RIGHT"
            surf_vector =  get_parent("FLOOR").polygon.point_list[1] - zero
          when "BACK"
            surf_vector =  get_parent("FLOOR").polygon.point_list[2] - zero
          when "LEFT"
            surf_vector =  get_parent("FLOOR").polygon.point_list[3] - zero
          end
          space_xref = self.check_keyword?("X")? self.get_keyword_value("X").to_f : 0.0
          space_yref = self.check_keyword?("Y")? self.get_keyword_value("Y").to_f : 0.0
          space_zref = self.check_keyword?("Z")? self.get_keyword_value("Z").to_f : 0.0
          space_origin = OpenStudio::Vector3d.new(space_xref,space_yref,space_zref)
          space_origin = surf_vector + space_origin
        else
          space_xref = self.check_keyword?("X")? self.get_keyword_value("X").to_f : 0.0
          space_yref = self.check_keyword?("Y")? self.get_keyword_value("Y").to_f : 0.0
          space_zref = self.check_keyword?("Z")? self.get_keyword_value("Z").to_f : 0.0
          space_origin = OpenStudio::Vector3d.new(space_xref,space_yref,space_zref)
        end
        return space_origin
      end

      def get_azimuth()
        angle = 0.0
        #puts OpenStudio::radToDeg( OpenStudio::getAngle(OpenStudio::Vector3d.new(0.0, 0.0, 0.0), OpenStudio::Vector3d.new(1.0, 0.0, 0.0) ) )
        if check_keyword?("LOCATION") and not check_keyword?("AZIMUTH")
          case get_keyword_value("LOCATION")
          when /FLOOR-\s*V\s*(.*)/
            index = $1.strip.to_i - 1
            point0 = self.get_parent("FLOOR").polygon.point_list[index]
            point1 = self.get_parent("FLOOR").polygon.point_list[index + 1] ? get_parent("FLOOR").polygon.point_list[index + 1] : get_parent("FLOOR").polygon.point_list[0]
            edge = point1-point0


            sign = 1.0# OpenStudio::Vector3d.new(1.0, 0.0, 0.0).dot(( edge )) > 0 ? 1 :-1
            angle = OpenStudio::radToDeg( sign * OpenStudio::getAngle(OpenStudio::Vector3d.new(1.0, 0.0, 0.0), ( point1 - point0 ) ) )

            #since get angle only get acute angles we need to get sign and completment for reflex angle
            if edge.y > 0.0
              angle = -1.0 * angle 
            end

          when "FRONT"
            angle = OpenStudio::radToDeg( OpenStudio::getAngle(OpenStudio::Vector3d.new(0.0, 1.0, 0.0), ( get_parent("FLOOR").polygon.point_list[1] - get_parent("FLOOR").polygon.point_list[0] ) ) )
          when "RIGHT"
            angle = OpenStudio::radToDeg( OpenStudio::getAngle(OpenStudio::Vector3d.new(0.0, 1.0, 0.0), ( get_parent("FLOOR").polygon.point_list[2] - get_parent("FLOOR").polygon.point_list[1] ) ) )
          when "BACK"
            angle = OpenStudio::radToDeg( OpenStudio::getAngle(OpenStudio::Vector3d.new(0.0, 1.0, 0.0), ( get_parent("FLOOR").polygon.point_list[3] - get_parent("FLOOR").polygon.point_list[2] ) ) )
          when "LEFT"
            angle = OpenStudio::radToDeg( OpenStudio::getAngle(OpenStudio::Vector3d.new(0.0, 1.0, 0.0), ( get_parent("FLOOR").polygon.point_list[0] - get_parent("FLOOR").polygon.point_list[3] ) ) )
          end
        else
          angle =  self.check_keyword?("AZIMUTH")? self.get_keyword_value("AZIMUTH").to_f : 0.0
        end
        return angle
      end


      def get_transformation_matrix()
        #This will transform the space vertices to normal space co-ordinates using Sketchup/OS convention
        return OpenStudio::createTranslation(self.get_origin) * OpenStudio::Transformation::rotation(OpenStudio::Vector3d.new(0.0, 0.0, 1.0), OpenStudio::degToRad(360.0 - self.get_azimuth()))
      end
      
      def get_rotation_matrix()
        return OpenStudio::Transformation::rotation(OpenStudio::Vector3d.new(0.0, 0.0, 1.0), OpenStudio::degToRad(360.0 - self.get_azimuth()))
      end

      def convert_to_openstudio(model,runner = nil)
        if self.get_keyword_value("SHAPE") == "NO-SHAPE"
          BTAP::runner_register("Info", "OpenStudio does not support NO-SHAPE SPACE definitions currently. Not importing the space #{self.name}.",runner)
        else
          os_space = OpenStudio::Model::Space.new(model)
          os_space.setAttribute("name", self.name)
          #set floor
          os_space.setBuildingStory(OpenStudio::Model::getBuildingStoryByName(model,self.get_parent("FLOOR").name).get)
          BTAP::runner_register("Info", "\tSpace: " + self.name + " created",runner)
          #puts "\t\t Azimuth:#{self.get_azimuth}"
          #puts "\t\t Azimuth:#{self.get_origin}"
        end
      end

    end
    class DOEFloor < DOESurface
      attr_accessor :polygon
      # a string object which defines the type of roof (e.g. attic)
      attr_accessor :type
      # The absorptance of the exterior surface of the floor
      # (see rule #4.3.5.3.(6)
      attr_accessor :absorptance
      # thermal insulation of floors
      attr_accessor :thermal_insulation

      def initialize
        super()
      end

      #This method returns the floor area
      def get_area

        # get the keyword for the shape of the floor
        case get_keyword_value("SHAPE")

          # if the keyword value is "BOX", the width and depth values are defined
        when "BOX"
          return get_keyword_value("WIDTH").to_f * get_keyword_value("DEPTH").to_f

          # if the keyword value is "POLYGON", the get_area is defined as the area of the
          # given polygon
        when "POLYGON"
          return @polygon.get_area

          # if the keyword value of the floor is "No-SHAPE", the get_area is given as the
          # get_area keyword value
        when "NO-SHAPE"
          return get_keyword_value("AREA").to_f
        else
          raise "Error: The area could not be evaluated. Please check inputs\n "
        end
      end

      # This method returns the volume of the floor space
      def get_volume
        return get_floor_height.to_f * get_area.to_f
      end

      # gets the height of the floor
      def get_height
        return get_keyword_value("FLOOR-HEIGHT").to_f
      end

      # gets the space height
      def get_space_height
        return get_keyword_value("SPACE-HEIGHT").to_f
      end

      def get_origin()
        space_xref = self.check_keyword?("X")? self.get_keyword_value("X").to_f : 0.0
        space_yref = self.check_keyword?("Y")? self.get_keyword_value("Y").to_f : 0.0
        space_zref = self.check_keyword?("Z")? self.get_keyword_value("Z").to_f : 0.0
        return OpenStudio::Vector3d.new(space_xref,space_yref,space_zref)
      end

      def get_azimuth()
        return self.check_keyword?("AZIMUTH")? self.get_keyword_value("AZIMUTH").to_f : 0.0
      end

      def get_transformation_matrix()
        return OpenStudio::createTranslation(self.get_origin) * OpenStudio::Transformation::rotation(OpenStudio::Vector3d.new(0.0, 0.0, 1.0), OpenStudio::degToRad(360.0 - self.get_azimuth()))
      end
      
      def get_rotation_matrix()
        return OpenStudio::Transformation::rotation(OpenStudio::Vector3d.new(0.0, 0.0, 1.0), OpenStudio::degToRad(360.0 - self.get_azimuth()))
      end

      def convert_to_openstudio(model,runner = nil)
        floor = OpenStudio::Model::BuildingStory.new(model)
        floor.setAttribute("name", self.name)
        BTAP::runner_register("Info", "\tBuildingStory: " + self.name + " created",runner)
      end

    end
    #This class makes it easier to deal with DOE Polygons.
    class DOEPolygon < DOECommand

      attr_accessor :point_list

      #The constructor.
      def initialize
        super()
        @point_list = Array.new()
        #Convert Keywork Pairs to points.

      end

      def create_point_list()

        #Convert Keywork Pairs to points.
        @point_list.clear
        @keywordPairs.each do |array|

          array[1].match(/\(\s*(\-?\d*\.?\d*)\s*\,\s*(\-?\d*\.?\d*)\s*\)/)
          #puts array[1]

          point = OpenStudio::Point3d.new($1.to_f,$2.to_f,0.0)
          @point_list.push(point)
        end
        #      @point_list.each do |p|
        #        puts p.x.to_s + " " + p.y.to_s + " " + p.z.to_s + " "
        #      end
      end

      # This method returns the area of the polygon.
      def get_area
        openstudio::getArea(@points_list)
      end


      # This method must determine the length of the given point to the next point
      # in the polygon list. If the point is the last point, then it will be the
      # distance from the last point to the first.
      # point_name is the string named keyword in the keyword pair list.
      # Example:
      # "DOEPolygon 2" = POLYGON
      #   V1               = ( 0, 0 )
      #   V2               = ( 0, 1 )
      #   V3               = ( 2, 1 )
      #   V4               = ( 2 ,0 )
      # get_length(3) should return "2"
      # get_length(2) should return "1"

      def get_length(point_index)
        if @points_list.size < pointindex + 2
          return OpenStudio::getDistance(@point_list[0],@point_list.last)
        else
          return OpenStudio::getDistance(@point_list[point_index],@point_list[point_index + 1] )
        end
      end


      def get_azimuth(point_index)
        if @points_list.size < pointindex + 2
          return OpenStudio::radToDeg(OpenStudio::getAngle(@point_list.last - @point_list[0] , openstudio::Vector3d( 1.0, 0.0, 0.0)))
        else
          return OpenStudio::radToDeg(OpenStudio::getAngle(@point_list[point_index + 1] - @point_list[point_index] , openstudio::Vector3d( 1.0, 0.0, 0.0)))
        end
      end

    end
    class DOELayer < DOECommand
      # type of material (see rule #4.3.5.2.(3))
      attr_accessor :material
      # the thickness of the material (see rule #4.3.5.2.(3))
      attr_accessor :thickness
      def initialize
        super()
      end
    end
    class DOEMaterial < DOECommand
      # characteristics of the materials
      attr_accessor :density
      attr_accessor :specific_heat
      attr_accessor :thermal_conductivity
      def initialize
        super()
      end
    end
    class DOEConstruction < DOECommand

      def initialize
        super()
      end

      def get_materials()
        bdllib = DOE2::DOEBDLlib.instance
        materials = Array.new

        case self.get_keyword_value("TYPE")
        when "LAYERS"
          # finds the command associated with the layers keyword
          layers_command = building.find_command_with_utype( self.get_keyword_value("LAYERS") )

          #if Layres command cannot be found in the inp file... find it in the bdl database.
          layers_command = bdllib.find_layer(self.get_keyword_value("LAYERS")) unless layers_command.length == 1

          # if there ends up to be more than one command with the layers keyword
          # raise an exception
          raise "Layers was defined more than once " + self.get_keyword_value("LAYERS").to_s if layers_command.length > 1

          # get all the materials, separate it by the quotation marks and push it
          # onto the materials array
          layers_command[0].get_keyword_value("MATERIAL").scan(/(\".*?\")/).each do |material|
            material_command = ""

            #Try to find material in doe model.
            material_command_array = building.find_command_with_utype(material.to_s.strip)

            # if there ends up to be more than one, raise an exception
            raise "Material was defined more than once #{material}" if material_command_array.length > 1

            # if the material cannot be found within the model, find it within the doe2 database
            material_command = bdllib.find_material(material) if material_command_array.length < 1

            #If material was found then set it.
            material_command = material_command_array[0] if material_command_array.length == 1

            materials.push(material_command)
          end
          return materials
        when "U-VALUE"
          return nil
        end
      end

      # This method finds the u-value of the given construction
      # Output => total conductivity as a float
      def get_u_value()
        total_conductivity = 0.0
        case self.get_keyword_value("TYPE")
        when "LAYERS"
          self.get_materials().each do |material_command|
            case material_command.get_keyword_value("TYPE")
            when  "RESISTANCE"
              conductivity = 1 / material_command.get_keyword_value("RESISTANCE").to_f
            when "PROPERTIES"
              conductivity = material_command.get_keyword_value("CONDUCTIVITY").to_f
            else
              raise "Error in material properties"
            end
            total_conductivity = total_conductivity + conductivity
          end
          return total_conductivity
        when "U-VALUE"
          return self.get_keyword_value("U-VALUE").to_f
        end
      end


    end
    class DOECommandFactory
      def initialize

      end

      def DOECommandFactory.command_factory(command_string, building)
        
        command = ""
        command_name = ""
        if (command_string != "")
          #Get command and u-value
          if ( command_string.match(/(^\s*(\".*?\")\s*\=\s*(\S+)\s*)/) )
            command_name=$3.strip
          else
            # if no u-value, get just the command.
            command_string.match(/(^\s*(\S*)\s)/ )
            @command_name=$2.strip
            
          end
        end
        case command_name
        when  "ZONE" then
          command = DOEZone.new()
        when  "FLOOR" then
          command = DOEFloor.new()
        when  "SPACE" then
          command = DOESpace.new()
        when  "EXTERIOR-WALL" then
          command = DOEExteriorWall.new()
        when  "INTERIOR-WALL" then
          command = DOESurface.new()
        when  "UNDERGROUND-WALL" then
          command = DOESurface.new()
        when  "ROOF" then
          command = DOERoof.new()
        when "WINDOW" then
          command = DOESubSurface.new()
        when "DOOR" then
          command = DOESubSurface.new()
        when "POLYGON" then
          command = DOEPolygon.new()
        when "LAYER" then
          command = DOELayer.new()
        when "MATERIAL" then
          command = DOEMaterial.new()
        when "CONSTRUCTION" then
          command = DOEConstruction.new()
        else
          command = DOECommand.new()
        end

        command.get_command_from_string(command_string)
        command.building = building
        return command
      end
    end
    
    # This is the main interface dealing with DOE inp files. You can load, save
    # manipulate doe files with this interface at a command level. 
    class DOEBuilding

      #An array to contain all the DOE
      attr_accessor  :commands
      #An array to contain the current parent when reading in the input files.
      attr_accessor  :parents


      # This method makes a deep copy of the building object.
      def clone
        return Marshal::load(Marshal.dump(self))
      end

      # The Constructor.
      def initialize

        @commands=[]
        @parents=[]
        @commandList = Array.new()

      end

      # This method will find all Commands given the command name string.
      # Example
      # def find_all_Command("ZONE")  will return an array of all the ZONE commands
      # used in the building.
      def find_all_commands (sCOMMAND)
        array = Array.new()
        @commands.each do |command|
          if (command.commandName == sCOMMAND)
            array.push(command)
          end
        end
        return array
      end

      # This method will find all Commands given the command name string.
      # Example
      # def find_all_Command("Default Construction")  will return an array of all
      # the commands with "Default Construction" as the u-type used in the building.
      def find_command_with_utype (utype)
        array = Array.new()
        @commands.each do |command|
          if (command.utype == utype)
            array.push(command)
          end
        end
        return array
      end


      # Same as find_all_commands except you can use regular expressions.
      def find_all_regex(sCOMMAND)
        array = Array.new()
        search =/#{sCOMMAND}/
        @commands.each do |command|
          if (command.commandName.match(search) )
            array.push(command)
          end

        end
        return array
      end

      # Find a matching keyword value pair in from an array of commands.
      # Example:
      # find_keyword_value(building.commands, "TYPE", "CONDITIONED")  will return
      # all the commands that have the
      # TYPE = CONDITIONED"
      # Keyword pair.
      def search_by_keyword_value( keyword, value)
        returnarray = Array.new()
        @commands.each do |command|
          if ( command.keywordPairs[keyword] == value )
            returnarray.push(command)
          end
        end
        return returnarray
      end


      # Will read an input file into memory and store all the commands into the
      # commands array.
      # param filename
      # param runner
      def load_inp(filename,runner = nil)
        BTAP::runner_register("Info", "loading file:" + filename, runner)
        #Open the file.
        #puts filename
        iter = 0


        File.exist?(filename)
        f = File.open(filename, "r")




        #Read the file into an array, line by line.
        lines = f.readlines
        #Set up the temp string.
        command_string =""

        lines.each do|line|
          iter = iter.next
          #line.forced_encoding("US-ASCII")
          #Ignore comments (To do!...strip from file as well as in-line comments.
          if (!line.match(/\$.*/) )

            if (myarray = line.match(/(.*?)\.\./) )
              #Add the last part of the command to the newline...may be blank."
              command_string = command_string + myarray[1]
              #Determine correct command class to create, then populates it."
              command = DOECommandFactory.command_factory(command_string, self)
              #Push the command into the command array."
              @commands.push(command)
              command_string = ""
            else
              myarray = line.match(/(.*)/)
              command_string = command_string + myarray[1]
            end
          end
        end
        
        organize_data()
        BTAP::runner_register("Info","INP model contains:", runner)
        #report number of things read in. 
        ["SPACE","ZONE","EXTERIOR-WALL","ROOF","INTERIOR-WALL","UNDERGROUND-WALL","WINDOW","DOOR","MATERIAL","CONSTRUCTION"].each do |item|
          items = self.find_all_commands(item)
          message = "\t#{item} = #{items.size}"
          BTAP::runner_register("Info",message, runner)
        end
        BTAP::runner_register("Info", "\tFinished Loading File:" + filename,runner)
      end



      # This will right a clean output file, meaning no comments. Good for doing
      # diffs
      def save_inp(string)
        array = @commands
        w = File.open(string, 'w')
        array.each { |command| w.print command.output }
        w.close
      end



      

      #This routine organizes the hierarchy of the space <-> zones and the polygon
      # associations that are not formally identified by the sequential relationship
      # like the floor, walls, windows. It would seem that zones and spaces are 1 to
      # one relationships.  So each zone will have a reference to its space and vice versa.
      # If there is a polygon command in the space or floor definition, a reference to the
      # polygon class will be set.
      def organize_data()
        # set_envelope_hierarchy
        # This method determines the current parents of the current command.
        def determine_current_parents(new_command)
          if @last_command.nil?
            @last_command = new_command
          end
          #Check to see if scope (HVAC versus Envelope) has changed or the parent depth is undefined "0"
          if (!@parents.empty? and (new_command.doe_scope != @parents.last.doe_scope or new_command.depth == 0 ))
            @parents.clear
          end
          #no change in parent.
          if ( (new_command.depth  == @last_command.depth))
            #no change
            @last_command = new_command
            #puts "#{new_command.commandName}"
          end
          #Parent depth added
          if ( new_command.depth  > @last_command.depth)
            @parents.push(@last_command)
            #puts "Added parent#{@last_command.commandName}"
            @last_command = new_command
          end
          #parent depth removed.
          if ( new_command.depth  < @last_command.depth)
            parent = @parents.pop
            #puts "Removed parent #{parent}"
            @last_command = new_command
          end
          array = Array.new(@parents)
          return array
        end


        @commands.each do |command|
          if command.doe_scope() == "envelope"
            #Sets parents of command.
            parents = determine_current_parents(command)
            if (!parents.empty?)
              command.parents = parents
            end
            #inserts current command into the parent's children.
            if (!command.parents.empty?)
              command.parents.last.children.push(command)
            end
          end
        end
        # Associating the polygons with the FLoor and spaces.
        polygons =  find_all_commands("POLYGON")
        spaces = find_all_commands("SPACE")
        floors = find_all_commands("FLOOR")
        zones = find_all_commands("ZONE")
        ext_walls = find_all_commands("EXTERIOR-WALL")
        roof = find_all_commands("ROOF")
        door = find_all_commands("DOOR")
        int_walls = find_all_commands("INTERIOR-WALL")
        underground_walls = find_all_commands("UNDERGROUND-WALL")
        underground_floors = find_all_commands("UNDERGROUND-FLOOR")
        constructions =find_all_commands("CONSTRUCTION")
        surface_lists = [ ext_walls, roof, door, int_walls, underground_walls, underground_floors]


        #Organize surface data.
        surface_lists.each do |surfaces|
          surfaces.each do |surface|
            #Assign constructions to surface objects
            constructions.each do |construction|
              if ( construction.utype == surface.get_keyword_value("CONSTRUCTION") )
                surface.construction = construction
              end
            end
            #Find Polygons associated with surface.
            polygons.each do |polygon|
              if ( surface.check_keyword?("POLYGON") and polygon.utype == surface.get_keyword_value("POLYGON")  )
                surface.polygon = polygon
              end
            end
          end
        end



        #Organize polygon data for space and floors.
        polygons.each do |polygon|
          #set up point list in polygon objects
          polygon.create_point_list()
          #Find Polygons associated with  floor and and reference to floor.
          floors.each do |floor|
            if ( polygon.utype == floor.get_keyword_value("POLYGON") )
              floor.polygon = polygon
            end
          end
          #Find Polygons for space and add reference to the space.
          spaces.each do |space|
            if space.check_keyword?("POLYGON")
              if ( polygon.utype == space.get_keyword_value("POLYGON") )
                space.polygon = polygon
              end
            end
          end
        end



        #    Find spaces that belong to the zone.
        zones.each do |zone|
          spaces.each do |space|
            if ( space.utype ==  zone.get_keyword_value("SPACE") )
              space.zone = zone
              zone.space = space
            end
          end
        end
      end



      def get_building_transformation_matrix()
        build_params = self.find_all_commands("BUILD-PARAMETERS")[0]
        building_xref = build_params.check_keyword?("X-REF")? build_params.get_keyword?("X-REF") : 0.0
        building_yref = build_params.check_keyword?("Y-REF")? build_params.get_keyword?("Y-REF") : 0.0
        building_origin = OpenStudio::Vector3d.new(building_xref,building_yref,0.0)
        building_azimuth = build_params.check_keyword?("AZIMUTH")? build_params.get_keyword?("AZIMUTH") : 0.0
        return  OpenStudio::Transformation::rotation(OpenStudio::Vector3d(0.0, 0.0, 1.0), openstudio::degToRad(building_azimuth)) * OpenStudio::Transformation::translation(building_origin)
      end




      #this method will convert a DOE inp file to the OSM file.. This will return
      # and openstudio model object. 
      def create_openstudio_model_new(runner = nil)
        beginning_time = Time.now

        end_time = Time.now
        BTAP::runner_register("Info", "Time elapsed #{(end_time - beginning_time)*1000} milliseconds",runner)
        model = OpenStudio::Model::Model.new()
        #add All Materials
        #    find_all_commands( "Materials" ).each do |doe_material|
        #    end
        #
        #    find_all_commands( "Constructions" ).each do |doe_cons|
        #    end

        #this block will create OS story objects in the OS model. 
        BTAP::runner_register("Info", "Exporting DOE FLOORS to OS",runner)
        find_all_commands("FLOOR").each do |doe_floor|
          doe_floor.convert_to_openstudio(model)
        end
        BTAP::runner_register("Info", OpenStudio::Model::getBuildingStorys(model).size.to_s + " floors created",runner)

        #this block will create OS space objects in the OS model. 
        BTAP::runner_register("Info", "Exporting DOE SPACES to OS",runner)
        find_all_commands("SPACE").each do |doe_space|
          doe_space.convert_to_openstudio(model)
        end
        BTAP::runner_register("Info", OpenStudio::Model::getSpaces(model).size.to_s + " spaces created",runner)
        
        #this block will create OS space objects in the OS model. 
        BTAP::runner_register("Info", "Exporting DOE ZONES to OS",runner)
        find_all_commands("ZONE").each do |doe_zone|
          doe_zone.convert_to_openstudio(model)
        end
        BTAP::runner_register("Info", OpenStudio::Model::getThermalZones(model).size.to_s + " zones created",runner)
        
        #this block will create OS surface objects in the OS model.
        BTAP::runner_register("Info", "Exporting DOE Surfaces to OS",runner)
        all_surfaces = Array.new()
        @commands.each do |command|
          case command.commandName
          when "EXTERIOR-WALL","INTERIOR-WALL","UNDERGROUND-WALL","ROOF"
            all_surfaces.push(command)
          end
        end
        all_surfaces.each do |doe_surface|
          doe_surface.convert_to_openstudio(model)
        end
        BTAP::runner_register("Info", OpenStudio::Model::getSurfaces(model).size.to_s + " surfaces created",runner)
        BTAP::runner_register("Info", OpenStudio::Model::getSubSurfaces(model).size.to_s + " sub_surfaces created",runner)
        BTAP::runner_register("Info", "Setting Boundary Conditions for surfaces",runner)
        BTAP::Geometry::match_surfaces(model)
        
        x_scale = y_scale = z_scale = 0.3048
        BTAP::runner_register("Info", "scaling model from feet to meters",runner)
        model.getPlanarSurfaces.each do |surface|
          new_vertices = OpenStudio::Point3dVector.new
          surface.vertices.each do |vertex|
            new_vertices << OpenStudio::Point3d.new(vertex.x * x_scale, vertex.y * y_scale, vertex.z * z_scale)
          end    
          surface.setVertices(new_vertices)
        end
 
        model.getPlanarSurfaceGroups.each do |surface_group|
          transformation = surface_group.transformation
          translation = transformation.translation
          euler_angles = transformation.eulerAngles
          new_translation = OpenStudio::Vector3d.new(translation.x * x_scale, translation.y * y_scale, translation.z * z_scale)
          #TODO these might be in the wrong order
          new_transformation = OpenStudio::createRotation(euler_angles) * OpenStudio::createTranslation(new_translation) 
          surface_group.setTransformation(new_transformation)
        end
        BTAP::runner_register("Info", "DOE2.2 -> OS Geometry Conversion Complete",runner)
        BTAP::runner_register("Info", "Summary of Conversion",runner)
        BTAP::runner_register("Info", OpenStudio::Model::getBuildingStorys(model).size.to_s + " floors created",runner)
        BTAP::runner_register("Info", OpenStudio::Model::getSpaces(model).size.to_s + " spaces created",runner)
        BTAP::runner_register("Info", OpenStudio::Model::getThermalZones(model).size.to_s + " thermal zones created",runner)
        BTAP::runner_register("Info", OpenStudio::Model::getSurfaces(model).size.to_s + " surfaces created",runner)
        BTAP::runner_register("Info", OpenStudio::Model::getSubSurfaces(model).size.to_s + " sub_surfaces created",runner)
        BTAP::runner_register("Info", "No Contruction were converted.",runner)
        BTAP::runner_register("Info", "No Materials were converted",runner)
        BTAP::runner_register("Info", "No HVAC components were converted",runner)
        BTAP::runner_register("Info", "No Environment or Simulation setting were converted.",runner)

        end_time = Time.now
        BTAP::runner_register("Info", "Time elapsed #{(end_time - beginning_time)} seconds",runner)
        return model
      end





      def get_materials()
        BTAP::runner_register("Info", "Spaces",runner)
        find_all_commands("SPACE").each do |space|
          BTAP::runner_register("Info", space.get_azimuth(),runner)
        end
        BTAP::runner_register("Info", "Materials",runner)
        find_all_commands("MATERIAL").each do |materials|
          BTAP::runner_register("Info", materials.get_name(),runner)
        end
        BTAP::runner_register("Info", "Layers",runner)
        find_all_commands("LAYERS").each do |materials|
          BTAP::runner_register("Info", materials.get_name(),runner)
        end
        BTAP::runner_register("Info", "Constructions",runner)
        find_all_commands("CONSTRUCTION").each do |materials|
          BTAP::runner_register("Info", materials.get_name(),runner)
        end

      end


    end
    # This class will manage all the layer information of the Reference components.
    class LayerManager
      include Singleton
      class Layer

        attr_accessor :name
        attr_accessor :thickness
        attr_accessor :conductivity
        attr_accessor :density
        attr_accessor :specific_heat
        attr_accessor :air_space
        attr_accessor :resistance
        def initialize
          @air_space = false
        end

        def set( thickness, conductivity, density, specific_heat)
          @thickness, @conductivity, @density, @specific_heat =  thickness, conductivity, density, specific_heat
          @airspace = false
        end

        def set_air_space(thickness, resistance)
          @thickness, @resistance = thickness, resistance
          @air_space = true
        end

        def output
          string = "Airspace = #{@air_space}\nThickness = #{@thickness}\nConductivity = #{@conductivity}\nResistance = #{@resistance}\nDensity = #{@density}\nSpecificHeat = #{@specific_heat}\n"
        end
      end
      # Array of all the layers
      attr_accessor :layers
      def initialize
        @layers = Array.new()
      end

      #Add a layer. If the layer already exists. It will return the exi
      def add_layer(new_layer)
        #first determine if the layer already exists.
        @layers.each do  |current_layer|
          if new_layer == current_layer
            return current_layer
          end
        end
        @layers.push(new_layer)
        return @layers.last()
      end

      private

      def clear()
        @layers.clear()
      end
    end
    #This class manages all of the constructions that are used in the simulation. It
    #should remove any constructions that are doubly defined in the project.
    class ConstructionManager
      # An array containing all the constructions.
      attr_accessor :constructions

      # The layer manager all the constructions.
      attr_accessor :layer_manager
      class Construction

        #The unique name for the construction.
        attr_accessor :name
        #The array which contains the material layers of the construction.
        attr_accessor :layers

        def initialize
          #Set up the array for the layers.
          @layers = Array.new()
        end

        #Adds a layer object to the construction.
        # Must pass a Layer object as an arg.
        def add_layer_object( object )
          layers.push( object )
        end

        #Adds a layer based on the physical properties list.
        #All units are based on the simulators input.
        def add_layer(thickness, conductivity, density, specific_heat)
          layer = Layer.new()
          # Make sure all the values are > 0.
          layer.set(thickness, conductivity, density, specific_heat)
          @layers.push(layer)
        end

        # Adds an airspace to the construction based on the thickness and Resistances.
        #All units are based on the simulators input.
        def add_air_space(thickness, resistance )
          layer = Layer.new()
          layer.set_air_space(thickness, resistance)
          @layers.push(layer)
        end

        def output()
          soutput = ""
          @layers.each do|layer|
            soutput = soutput + layer.output() + "\n"
          end
          soutput
        end
      end


      def initialize
        @constructions = Array.new()
        @layer_manager = LayerManager.instance()
      end


      #Adds a new construction to the construction array.
      #Arg must be a construction object.
      def add_construction(new_construction)
        #first determine if the layer already exists.
        @constructions.each do  |current_construction|
          if new_construction == current_construction
            return current_construction
          end
        end
        new_construction.layers.each do |new_layer|
          #If the new layer already exists...use the old one instead.
          # it is the layerManager's job to decide this.
          new_layer = @layer_manager.add_layer(new_layer)
        end
        @constructions.push(new_construction)
        return @constructions.last()
      end

      def clear()
        @constructions.clear()
        @layer_manager.clear()
      end

    end
  end
end