module Floorplanner
  class Opening3D < Geom::TriangleMesh

    TYPE_DOOR   = 1
    TYPE_WINDOW = 2

    attr_accessor(:position,:window)

    def initialize(baseline,thickness,opening)
      super()
      @position = baseline.snap(opening[:position])
      @type     = opening[:type]
      dir = baseline.direction
      angle  = Math.atan2(dir.y,dir.x)
      width  = opening[:size].x
      height = 0

      case @type
      when TYPE_DOOR
        @position.z = 0.01
        height = Floorplanner.config['openings']['door_height']
      else
        @position.z = Floorplanner.config['openings']['window_base']
        height = Floorplanner.config['openings']['window_height']
      end

      v1 = Geom::Vertex.new(-width/2,0,0)
      v2 = Geom::Vertex.new( width/2,0,0)
      o_base = Geom::Edge.new(v1,v2)
      
      # create opening side
      o_inner = o_base.offset(thickness/2.0,Wall3D::UP)
      o_outer = o_base.offset(-thickness/2.0,Wall3D::UP)

      @base = Geom::Polygon.new([
        o_inner.end_point, o_inner.start_point,
        o_outer.start_point  , o_outer.end_point
      ])

      # rotate in wall's direction
      @base.transform_vertices(Geom::Matrix3D.rotationZ(angle))
      # move to position
      @base.transform_vertices(Geom::Matrix3D.translation(@position.x,@position.y,@position.z))

      extrusion = @base.extrude(height,Wall3D::UP,nil,false)

      # delete sides
      extrusion.delete_at(0)
      extrusion.delete_at(1)

      # flip top cap
      extrusion.last.reverse
      
      @meshes << @base
      @meshes.concat(extrusion)

      # create glass
      if @type == TYPE_WINDOW
        g_inner = o_base.offset( 0.02, Wall3D::UP)
        g_outer = o_base.offset(-0.02, Wall3D::UP)

        glass_base = Geom::Polygon.new([
          g_inner.end_point, g_inner.start_point,
          g_outer.start_point  , g_outer.end_point
        ])

        # rotate in wall's direction
        glass_base.transform_vertices(Geom::Matrix3D.rotationZ(angle))
        # move to position
        glass_base.transform_vertices(Geom::Matrix3D.translation(@position.x,@position.y,@position.z))

        extrusion = glass_base.extrude(height,Wall3D::UP,nil,false)

        # flip base cap
        glass_base.reverse

        @window = Geom::TriangleMesh.new
        @window.meshes.concat extrusion
        @window << glass_base
        @window.update
      end
    end

    # drill hole to sides
    def drill(mesh,outer)
      side = outer ? mesh.meshes.first : mesh.meshes.last

      # opening start
      t1   = @meshes.first.vertices[outer ? 0 : 3].clone
      t1.z = side.vertices[0].z
      t1b  = @meshes[3].vertices[outer ? 0 : 3]

      b1   = @meshes.first.vertices[outer ? 0 : 3].clone
      b1.z = side.vertices[2].z
      b1t  = @meshes.first.vertices[outer ? 0 : 3]

      # opening end
      t2   = @meshes.first.vertices[outer ? 1 : 2].clone
      t2.z = side.vertices[0].z
      t2b  = @meshes[3].vertices[outer ? 1 : 2]

      b2   = @meshes.first.vertices[outer ? 1 : 2].clone
      b2.z = side.vertices[2].z
      b2t  = @meshes.first.vertices[outer ? 1 : 2]

      # old side vertices
      ot = side.vertices[1]
      ob = side.vertices[2]
      side.vertices[1] = outer ? t2 : t1
      side.vertices[2] = outer ? b2 : b1

      # polygon above opening
      op_top = Geom::Polygon.new
      if outer
        op_top.vertices.push(t2,t1,t1b,t2b)
      else
        op_top.vertices.push(t1,t2,t2b,t1b)
      end

      # polygon below opening
      op_bot = Geom::Polygon.new
      if outer
        op_bot.vertices.push(b2t,b1t,b1,b2)
      else
        op_bot.vertices.push(b1t,b2t,b2,b1)
      end

      rest = Geom::Polygon.new
      if outer
        rest.vertices.push(t1,ot,ob,b1)
      else
        rest.vertices.push(t2,ot,ob,b2)
      end
      
      mesh.meshes.push(op_top)
      mesh.meshes.push(op_bot)
      mesh.meshes.push(rest)
    end
  end
end