TEST_F(ModelFixture, Construction_NetArea_SubSurface) {
  Model model;

  ThermalZone thermalZone(model);

  Space space(model);
  space.setThermalZone(thermalZone);

  Construction construction1(model);
  Construction construction2(model);
  EXPECT_DOUBLE_EQ(0.0, construction1.getNetArea());
  EXPECT_DOUBLE_EQ(0.0, construction2.getNetArea());

  Point3dVector points;
  points.push_back(Point3d(0, 0, 10));
  points.push_back(Point3d(0, 0, 0));
  points.push_back(Point3d(0, 10, 0));
  points.push_back(Point3d(0, 10, 10));

  Surface surface(points, model);
  surface.setConstruction(construction1);
  surface.setSpace(space);
  EXPECT_EQ("Wall", surface.surfaceType());
  EXPECT_DOUBLE_EQ(100.0, surface.netArea());
  EXPECT_DOUBLE_EQ(100.0, construction1.getNetArea());
  EXPECT_DOUBLE_EQ(0.0, construction2.getNetArea());
  EXPECT_DOUBLE_EQ(0, surface.windowToWallRatio());

  points.clear();
  points.push_back(Point3d(0, 0, 2));
  points.push_back(Point3d(0, 0, 1));
  points.push_back(Point3d(0, 1, 1));
  points.push_back(Point3d(0, 1, 2));

  SubSurface subSurface(points, model);
  subSurface.setConstruction(construction2);
  subSurface.setSurface(surface);
  EXPECT_EQ("FixedWindow", subSurface.subSurfaceType());
  EXPECT_DOUBLE_EQ(1.0, subSurface.netArea());
  EXPECT_DOUBLE_EQ(99.0, construction1.getNetArea());
  EXPECT_DOUBLE_EQ(1.0, construction2.getNetArea());
  EXPECT_DOUBLE_EQ(1.0/100.0, surface.windowToWallRatio());

  EXPECT_TRUE(subSurface.setMultiplier(4));
  EXPECT_DOUBLE_EQ(1.0, subSurface.netArea());
  EXPECT_DOUBLE_EQ(96.0, construction1.getNetArea());
  EXPECT_DOUBLE_EQ(4.0, construction2.getNetArea());
  EXPECT_DOUBLE_EQ(4.0/100.0, surface.windowToWallRatio());

  EXPECT_TRUE(thermalZone.setMultiplier(4));
  EXPECT_DOUBLE_EQ(1.0, subSurface.netArea());
  EXPECT_DOUBLE_EQ(4*96.0, construction1.getNetArea());
  EXPECT_DOUBLE_EQ(4*4.0, construction2.getNetArea());
  EXPECT_DOUBLE_EQ(4.0/100.0, surface.windowToWallRatio());
}
TEST_F(RulesetFixture, ModelRuleset) {

  openstudio::path wallComponentPath = toPath("./modelruleset_wall_construction.osc");
  openstudio::path roofComponentPath = toPath("./modelruleset_roof_construction.osc");

  {
    Model model;
    StandardOpaqueMaterial material(model);

    Construction wallConstruction(model);
    wallConstruction.insertLayer(0,material);
    wallConstruction.setName("ModelRulesetWallConstruction");
    wallConstruction.createComponent().save(wallComponentPath, true);

    Construction roofConstruction(model);
    roofConstruction.insertLayer(0,material);
    roofConstruction.setName("ModelRulesetRoofConstruction");
    roofConstruction.createComponent().save(roofComponentPath, true);
  }

  for (unsigned i = 0; i < 100; ++i){
    QDomDocument document;

    {
      ModelObjectFilterType typeFilter(IddObjectType::OS_Surface);
      ModelObjectFilterStringAttribute isWallFilter("surfaceType", RulesetStringPredicate::IEquals, "Wall");
      ModelObjectActionSetAttribute setWallName("name", "New Wall");
      ModelObjectActionSetRelationship setWallConstruction("construction", wallComponentPath);

      ModelObjectFilterStringAttribute isRoofFilter("surfaceType", RulesetStringPredicate::IEquals, "RoofCeiling");
      ModelObjectActionSetAttribute setRoofName("name", "New Roof");
      ModelObjectActionSetRelationship setRoofConstruction("construction", roofComponentPath);

      ModelRule wallRule("WallRule");
      wallRule.add(typeFilter);
      wallRule.add(isWallFilter);
      wallRule.add(setWallName);
      wallRule.add(setWallConstruction);

      ModelRule roofRule("RoofRule");
      roofRule.add(typeFilter);
      roofRule.add(isRoofFilter);
      roofRule.add(setRoofName);
      roofRule.add(setRoofConstruction);

      ModelRuleset ruleset("ruleset");
      ruleset.add(wallRule);
      ruleset.add(roofRule);
      
      ASSERT_EQ(2u, ruleset.rules().size());
      EXPECT_EQ("WallRule", ruleset.rules()[0].name());
      EXPECT_EQ("RoofRule", ruleset.rules()[1].name());

      document = ruleset.toXml();
    }

    EXPECT_TRUE(Ruleset::factoryFromXml(document.documentElement()));
  
    // deserialize from xml
    ModelRuleset ruleset(document.documentElement());
    ASSERT_EQ(2u, ruleset.rules().size()) << "Failure on iteration " << i << std::endl << document << std::endl << ruleset;
    EXPECT_EQ("WallRule", ruleset.rules()[0].name());
    EXPECT_EQ("RoofRule", ruleset.rules()[1].name());

    ASSERT_EQ(2u, ruleset.rules()[0].filters().size()) << "Failure on iteration " << i << std::endl << document << std::endl << ruleset.rules()[0];
    EXPECT_TRUE(ruleset.rules()[0].filters()[0].optionalCast<ModelObjectFilterType>());
    EXPECT_TRUE(ruleset.rules()[0].filters()[1].optionalCast<ModelObjectFilterStringAttribute>());
    ASSERT_EQ(2u, ruleset.rules()[0].actions().size());
    EXPECT_TRUE(ruleset.rules()[0].actions()[0].optionalCast<ModelObjectActionSetAttribute>());
    EXPECT_TRUE(ruleset.rules()[0].actions()[1].optionalCast<ModelObjectActionSetRelationship>());

    ASSERT_EQ(2u, ruleset.rules()[1].filters().size()) << "Failure on iteration " << i << std::endl << document << std::endl << ruleset.rules()[1];
    EXPECT_TRUE(ruleset.rules()[1].filters()[0].optionalCast<ModelObjectFilterType>());
    EXPECT_TRUE(ruleset.rules()[1].filters()[1].optionalCast<ModelObjectFilterStringAttribute>());
    ASSERT_EQ(2u, ruleset.rules()[1].actions().size());
    EXPECT_TRUE(ruleset.rules()[1].actions()[0].optionalCast<ModelObjectActionSetAttribute>());
    EXPECT_TRUE(ruleset.rules()[1].actions()[1].optionalCast<ModelObjectActionSetRelationship>());

    // make up a model
    Model model;

    Construction construction(model);
    construction.setName("Construction");
    ASSERT_TRUE(construction.name());
    EXPECT_EQ("Construction", construction.name().get());

    Space space(model);
    space.setName("Space");
    ASSERT_TRUE(space.name());
    EXPECT_EQ("Space", space.name().get());

    Point3dVector points;
    points.push_back(Point3d(0,1,1));
    points.push_back(Point3d(0,0,1));
    points.push_back(Point3d(1,0,1));
    points.push_back(Point3d(1,1,1));

    Surface roof(points, model); 
    EXPECT_TRUE(roof.setAttribute("name", "Roof"));
    roof.setName("Roof");
    roof.setSpace(space);
    roof.setSurfaceType("RoofCeiling");
    roof.setConstruction(construction);

    ASSERT_TRUE(roof.name());
    EXPECT_EQ("Roof", roof.name().get());
    ASSERT_TRUE(roof.construction());
    EXPECT_EQ(construction.handle(), roof.construction()->handle());

    points.clear();
    points.push_back(Point3d(0,0,1));
    points.push_back(Point3d(0,0,0));
    points.push_back(Point3d(0,1,0));
    points.push_back(Point3d(0,1,1));

    Surface wall(points, model);
    EXPECT_TRUE(wall.setAttribute("name", "Wall"));
    wall.setName("Wall");
    wall.setSpace(space);
    wall.setSurfaceType("Wall");
    wall.setConstruction(construction);

    ASSERT_TRUE(wall.name());
    EXPECT_EQ("Wall", wall.name().get());
    ASSERT_TRUE(wall.construction());
    EXPECT_EQ(construction.handle(), wall.construction()->handle());

    points.clear();
    points.push_back(Point3d(0,0.25,0.75));
    points.push_back(Point3d(0,0.25,0.25));
    points.push_back(Point3d(0,0.75,0.25));
    points.push_back(Point3d(0,0.75,0.75));

    SubSurface window(points, model); 
    window.setName("Window");
    window.setSurface(wall);
    window.setSubSurfaceType("FixedWindow");
    window.setConstruction(construction);

    ASSERT_TRUE(window.name());
    EXPECT_EQ("Window", window.name().get());
    ASSERT_TRUE(window.construction());
    EXPECT_EQ(construction.handle(), window.construction()->handle());

    points.clear();
    points.push_back(Point3d(0,1,0));
    points.push_back(Point3d(1,1,0));
    points.push_back(Point3d(1,0,0));
    points.push_back(Point3d(0,0,0));

    Surface floor(points, model); 
    floor.setName("Floor");
    floor.setSpace(space);
    floor.setSurfaceType("Floor");
    floor.setConstruction(construction);

    ASSERT_TRUE(floor.name());
    EXPECT_EQ("Floor", floor.name().get());
    ASSERT_TRUE(floor.construction());
    EXPECT_EQ(construction.handle(), floor.construction()->handle());

    // apply rule set
    EXPECT_TRUE(ruleset.apply(model));

    ASSERT_TRUE(construction.name());
    EXPECT_EQ("Construction", construction.name().get());

    ASSERT_TRUE(space.name());
    EXPECT_EQ("Space", space.name().get());

    ASSERT_TRUE(roof.name());
    EXPECT_EQ("New Roof", roof.name().get());
    ASSERT_TRUE(roof.construction());
    EXPECT_NE(construction.handle(), roof.construction()->handle());
    ASSERT_TRUE(roof.construction()->name());
    EXPECT_EQ("ModelRulesetRoofConstruction", roof.construction()->name().get());

    ASSERT_TRUE(wall.name());
    EXPECT_EQ("New Wall", wall.name().get());
    ASSERT_TRUE(wall.construction());
    EXPECT_NE(construction.handle(), wall.construction()->handle());
    ASSERT_TRUE(wall.construction()->name());
    EXPECT_EQ("ModelRulesetWallConstruction", wall.construction()->name().get());

    ASSERT_TRUE(window.name());
    EXPECT_EQ("Window", window.name().get());
    ASSERT_TRUE(window.construction());
    EXPECT_EQ(construction.handle(), window.construction()->handle());

    ASSERT_TRUE(floor.name());
    EXPECT_EQ("Floor", floor.name().get());
    ASSERT_TRUE(floor.construction());
    EXPECT_EQ(construction.handle(), floor.construction()->handle());
  }
}
TEST_F(ModelFixture, Construction_NetArea_InteriorWall) {
  Model model;

  Construction construction1(model);
  Construction construction2(model);
  EXPECT_DOUBLE_EQ(0.0, construction1.getNetArea());
  EXPECT_DOUBLE_EQ(0.0, construction2.getNetArea());

  // add costs
  boost::optional<LifeCycleCost> cost1 = LifeCycleCost::createLifeCycleCost("Brick", construction1, 3.0, "CostPerArea", "Construction");
  ASSERT_TRUE(cost1);
  boost::optional<LifeCycleCost> cost2 = LifeCycleCost::createLifeCycleCost("Glass", construction2, 5.0, "CostPerArea", "Construction");
  ASSERT_TRUE(cost2);
  EXPECT_DOUBLE_EQ(0, cost1->totalCost());
  EXPECT_DOUBLE_EQ(0, cost2->totalCost());

  Building building = model.getUniqueModelObject<Building>();
  DefaultSurfaceConstructions defaultExteriorSurfaceConstructions(model);
  DefaultSurfaceConstructions defaultInteriorSurfaceConstructions(model);
  DefaultSubSurfaceConstructions defaultExteriorSubSurfaceConstructions(model);
  DefaultSubSurfaceConstructions defaultInteriorSubSurfaceConstructions(model);
  DefaultConstructionSet defaultConstructionSet(model);
  defaultConstructionSet.setDefaultExteriorSurfaceConstructions(defaultExteriorSurfaceConstructions);
  defaultConstructionSet.setDefaultInteriorSurfaceConstructions(defaultInteriorSurfaceConstructions);
  defaultConstructionSet.setDefaultExteriorSubSurfaceConstructions(defaultExteriorSubSurfaceConstructions);
  defaultConstructionSet.setDefaultInteriorSubSurfaceConstructions(defaultInteriorSubSurfaceConstructions);
  building.setDefaultConstructionSet(defaultConstructionSet);

  Space space(model);

  Point3dVector points;
  points.push_back(Point3d(0, 0, 1));
  points.push_back(Point3d(0, 0, 0));
  points.push_back(Point3d(0, 1, 0));
  points.push_back(Point3d(0, 1, 1));

  Surface surface1(points, model);
  surface1.setSpace(space);
  EXPECT_EQ("Wall", surface1.surfaceType());
  EXPECT_EQ("Outdoors", surface1.outsideBoundaryCondition());
  EXPECT_DOUBLE_EQ(1.0, surface1.netArea());

  points.clear();
  points.push_back(Point3d(0, 1, 1));
  points.push_back(Point3d(0, 1, 0));
  points.push_back(Point3d(0, 0, 0));
  points.push_back(Point3d(0, 0, 1));

  Surface surface2(points, model);
  surface2.setSpace(space);
  EXPECT_EQ("Wall", surface2.surfaceType());
  EXPECT_EQ("Outdoors", surface2.outsideBoundaryCondition());
  EXPECT_DOUBLE_EQ(1.0, surface2.netArea());

  EXPECT_DOUBLE_EQ(0.0, construction1.getNetArea());
  EXPECT_DOUBLE_EQ(0.0, construction2.getNetArea());
  EXPECT_DOUBLE_EQ(0, cost1->totalCost());
  EXPECT_DOUBLE_EQ(0, cost2->totalCost());

  defaultExteriorSurfaceConstructions.setWallConstruction(construction1);
  ASSERT_TRUE(surface1.construction());
  ASSERT_TRUE(surface2.construction());
  EXPECT_EQ(surface1.construction()->handle(), construction1.handle());
  EXPECT_EQ(surface2.construction()->handle(), construction1.handle());
  EXPECT_DOUBLE_EQ(2.0, construction1.getNetArea());
  EXPECT_DOUBLE_EQ(0.0, construction2.getNetArea());
  EXPECT_DOUBLE_EQ(6.0, cost1->totalCost());
  EXPECT_DOUBLE_EQ(0, cost2->totalCost());

  surface1.setConstruction(construction1);
  surface2.setConstruction(construction2);
  ASSERT_TRUE(surface1.construction());
  ASSERT_TRUE(surface2.construction());
  EXPECT_EQ(surface1.construction()->handle(), construction1.handle());
  EXPECT_EQ(surface2.construction()->handle(), construction2.handle());
  EXPECT_DOUBLE_EQ(1.0, construction1.getNetArea());
  EXPECT_DOUBLE_EQ(1.0, construction2.getNetArea());
  EXPECT_DOUBLE_EQ(3.0, cost1->totalCost());
  EXPECT_DOUBLE_EQ(5.0, cost2->totalCost());

  surface1.setAdjacentSurface(surface2);
  ASSERT_TRUE(surface1.construction());
  ASSERT_TRUE(surface2.construction());
  EXPECT_EQ(surface1.construction()->handle(), construction1.handle());
  EXPECT_EQ(surface2.construction()->handle(), construction2.handle());
  EXPECT_DOUBLE_EQ(1.0, construction1.getNetArea());
  EXPECT_DOUBLE_EQ(1.0, construction2.getNetArea());
  EXPECT_DOUBLE_EQ(3.0, cost1->totalCost());
  EXPECT_DOUBLE_EQ(5.0, cost2->totalCost());

  surface2.setConstruction(construction1);
  ASSERT_TRUE(surface1.construction());
  ASSERT_TRUE(surface2.construction());
  EXPECT_EQ(surface1.construction()->handle(), construction1.handle());
  EXPECT_EQ(surface2.construction()->handle(), construction1.handle());
  EXPECT_DOUBLE_EQ(1.0, construction1.getNetArea());
  EXPECT_DOUBLE_EQ(0.0, construction2.getNetArea());
  EXPECT_DOUBLE_EQ(3.0, cost1->totalCost());
  EXPECT_DOUBLE_EQ(0.0, cost2->totalCost());
}
TEST_F(ModelFixture, DefaultConstructionSet_InteriorSubSurfaces)
{
  Model model;

  Point3dVector points;
  points.push_back(Point3d(0,1,0));
  points.push_back(Point3d(0,0,0));
  points.push_back(Point3d(1,0,0));
  Space space(model);
  Surface surface(points, model);
  surface.setSpace(space);
  EXPECT_FALSE(surface.setOutsideBoundaryCondition("Surface"));

  points.clear();
  points.push_back(Point3d(1,0,0));
  points.push_back(Point3d(0,0,0));
  points.push_back(Point3d(0,1,0));
  Space space2(model);
  Surface surface2(points, model);
  surface2.setSpace(space2);
  EXPECT_TRUE(surface.setAdjacentSurface(surface2));
  EXPECT_EQ("Surface", surface.outsideBoundaryCondition());
  EXPECT_EQ("Surface", surface2.outsideBoundaryCondition());
  ASSERT_TRUE(surface.adjacentSurface());
  ASSERT_TRUE(surface2.adjacentSurface());
  EXPECT_EQ(surface2.handle(), surface.adjacentSurface()->handle());
  EXPECT_EQ(surface.handle(), surface2.adjacentSurface()->handle());

  SubSurface subSurface(points, model);
  EXPECT_TRUE(subSurface.setSurface(surface));

  DefaultConstructionSet defaultConstructionSet(model);
  DefaultSubSurfaceConstructions defaultSubSurfaceConstructions(model);
  Construction construction(model);

  EXPECT_FALSE(defaultConstructionSet.defaultInteriorSubSurfaceConstructions());
  EXPECT_TRUE(defaultConstructionSet.setDefaultInteriorSubSurfaceConstructions(defaultSubSurfaceConstructions));
  ASSERT_TRUE(defaultConstructionSet.defaultInteriorSubSurfaceConstructions());
  EXPECT_EQ(defaultSubSurfaceConstructions.handle(), defaultConstructionSet.defaultInteriorSubSurfaceConstructions()->handle());

  EXPECT_TRUE(subSurface.setSubSurfaceType("FixedWindow"));
  EXPECT_FALSE(defaultConstructionSet.getDefaultConstruction(subSurface));
  EXPECT_TRUE(defaultSubSurfaceConstructions.setFixedWindowConstruction(construction));
  ASSERT_TRUE(defaultConstructionSet.getDefaultConstruction(subSurface));
  EXPECT_EQ(construction.handle(), defaultConstructionSet.getDefaultConstruction(subSurface)->handle());
  defaultSubSurfaceConstructions.resetFixedWindowConstruction();
  EXPECT_FALSE(defaultConstructionSet.getDefaultConstruction(subSurface));

  EXPECT_TRUE(subSurface.setSubSurfaceType("Door"));
  EXPECT_FALSE(defaultConstructionSet.getDefaultConstruction(subSurface));
  EXPECT_TRUE(defaultSubSurfaceConstructions.setDoorConstruction(construction));
  ASSERT_TRUE(defaultConstructionSet.getDefaultConstruction(subSurface));
  EXPECT_EQ(construction.handle(), defaultConstructionSet.getDefaultConstruction(subSurface)->handle());
  defaultSubSurfaceConstructions.resetDoorConstruction();
  EXPECT_FALSE(defaultConstructionSet.getDefaultConstruction(subSurface));

  EXPECT_TRUE(subSurface.setSubSurfaceType("Skylight"));
  EXPECT_FALSE(defaultConstructionSet.getDefaultConstruction(subSurface));
  EXPECT_TRUE(defaultSubSurfaceConstructions.setSkylightConstruction(construction));
  ASSERT_TRUE(defaultConstructionSet.getDefaultConstruction(subSurface));
  EXPECT_EQ(construction.handle(), defaultConstructionSet.getDefaultConstruction(subSurface)->handle());
  defaultSubSurfaceConstructions.resetSkylightConstruction();
  EXPECT_FALSE(defaultConstructionSet.getDefaultConstruction(subSurface));
}