# *******************************************************************************
# OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC.
# See also https://openstudio.net/license
# *******************************************************************************

require 'rubygems'
require 'json'
require 'erb'

require_relative 'resources/va3c'

# start the measure
class ViewModel < OpenStudio::Measure::ModelMeasure
  # define the name that a user will see
  def name
    return 'ViewModel'
  end

  # human readable description
  def description
    return 'Visualize an OpenStudio model in a web based viewer'
  end

  # human readable description of modeling approach
  def modeler_description
    return 'Converts the OpenStudio model to vA3C JSON format and renders using Three.js'
  end

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

    if Gem::Version.new(OpenStudio.openStudioVersion) > Gem::Version.new('3.6.1')
      use_geometry_diagnostics = OpenStudio::Measure::OSArgument.makeBoolArgument('use_geometry_diagnostics', true)
      use_geometry_diagnostics.setDisplayName("Enable Geometry Diagnostics")
      use_geometry_diagnostics.setDescription("Enables checks for Surface/Space Convexity. The ThreeJS export is slightly slower.")
      use_geometry_diagnostics.setDefaultValue(false)
      args << use_geometry_diagnostics
    end

    return args
  end

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

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

    if Gem::Version.new(OpenStudio.openStudioVersion) > Gem::Version.new('3.6.1')
      use_geometry_diagnostics = runner.getBoolArgumentValue('use_geometry_diagnostics', user_arguments)
    end

    # convert the model to vA3C JSON format
    json = nil
    model_clone = model.clone(true).to_Model
    begin
      # try to use new implementation
      ft = OpenStudio::Model::ThreeJSForwardTranslator.new
      if Gem::Version.new(OpenStudio.openStudioVersion) > Gem::Version.new('3.6.1')
        ft.setIncludeGeometryDiagnostics(use_geometry_diagnostics)
      end
      three_scene = ft.modelToThreeJS(model_clone, true)
      json = JSON.parse(three_scene.toJSON(false), symbolize_names: true)
      runner.registerInfo('Used new ThreeScene translator.')
    rescue NameError, StandardError
      # use old Ruby implementation
      runner.registerInfo('Using Ruby VA3C translator.')
      json = VA3C.convert_model(model_clone)
    end

    # write json file
    json_out_path = './report.json'
    File.open(json_out_path, 'w') do |file|
      file << JSON.generate(json, object_nl: "\n", array_nl: '', indent: '  ', space: '', space_before: '')
      # file << JSON::generate(json, {:object_nl=>"", :array_nl=>"", :indent=>"", :space=>"", :space_before=>""})
      # make sure data is written to the disk one way or the other
      begin
        file.fsync
      rescue StandardError
        file.flush
      end
    end

    # read in template
    html_in_path = "#{File.dirname(__FILE__)}/resources/report.html.in"
    if File.exist?(html_in_path)
      html_in_path = html_in_path
    else
      html_in_path = "#{File.dirname(__FILE__)}/report.html.in"
    end
    html_in = ''
    File.open(html_in_path, 'r') do |file|
      html_in = file.read
    end

    # configure template with variable values
    os_data = JSON.generate(json, object_nl: '', array_nl: '', indent: '', space: '', space_before: '')
    title = 'View Model'

    # Embed js
    js_in_path = "#{File.dirname(__FILE__)}/resources/js"
    if !File.directory?(js_in_path)
      js_in_path = "#{File.dirname(__FILE__)}/js"
    end
    three_js = File.read(File.join(js_in_path, 'three.r98.min.js'))
    three_orbitcontrol_js = File.read(File.join(js_in_path, 'three.orbitcontrols.js'))
    dat_gui_js = File.read(File.join(js_in_path, 'dat.gui.0.7.9.min.js'))
    tweenlite_js = File.read(File.join(js_in_path, 'TweenLite.2.1.3.min.js'))

    renderer = ERB.new(html_in)
    html_out = renderer.result(binding)

    # write html file
    html_out_path = './report.html'
    File.open(html_out_path, 'w') do |file|
      file << html_out

      # make sure data is written to the disk one way or the other
      begin
        file.fsync
      rescue StandardError
        file.flush
      end
    end

    html_out_path = File.absolute_path(html_out_path)

    # reporting final condition
    runner.registerFinalCondition("Report written to <a href='file:///#{html_out_path}'>report.html</a>.")

    runner.registerAsNotApplicable('No changes made to the model.')

    return true
  end # end the run method
end # end the measure

# this allows the measure to be use by the application
ViewModel.new.registerWithApplication