# RSMP site
# The site initializes the connection to the supervisor.
# Connections to supervisors are handles via supervisor proxies.

module RSMP
  class Site < Node
    include Components

    attr_reader :rsmp_versions, :site_settings, :logger, :proxies

    def initialize options={}
      super options
      initialize_components
      handle_site_settings options
      @proxies = []
      @sleep_condition = Async::Notification.new
      @proxies_condition = Async::Notification.new

      build_proxies
    end

    def site_id
      @site_settings['site_id']
    end

    def handle_site_settings options={}
      defaults = {
        'site_id' => 'RN+SI0001',
        'supervisors' => [
          { 'ip' => '127.0.0.1', 'port' => 12111 }
        ],
        'rsmp_versions' => 'all',
        'sxl' => 'tlc',
        'sxl_version' => '1.0.15',
        'intervals' => {
          'timer' => 0.1,
          'watchdog' => 1,
          'reconnect' => 0.1
        },
        'timeouts' => {
          'watchdog' => 2,
          'acknowledgement' => 2
        },
        'send_after_connect' => true,
        'components' => {
          'main' => {
            'C1' => {}
          }
        }
      }
      # only one main component can be defined, so replace the default if options define one
      if options.dig(:site_settings,'components','main')
        defaults['components']['main'] = options[:site_settings]['components']['main']
      end

      @site_settings = defaults.deep_merge options[:site_settings]
      check_sxl_version
      setup_components @site_settings['components']
    end

    def check_sxl_version
      sxl = @site_settings['sxl']
      version = @site_settings['sxl_version']
      RSMP::Schemer::find_schema! sxl, version, lenient: true
    end

    def run
      log "Starting site #{@site_settings["site_id"]}",
          level: :info,
          timestamp: @clock.now
      @proxies.each { |proxy| proxy.start }
      @proxies.each { |proxy| proxy.wait }
    end

    def build_proxies
      @site_settings["supervisors"].each do |supervisor_settings|
        @proxies << SupervisorProxy.new({
          site: self,
          task: @task,
          settings: @site_settings,
          ip: supervisor_settings['ip'],
          port: supervisor_settings['port'],
          logger: @logger,
          archive: @archive,
          collect: @collect
        })
      end
    end

    def aggregated_status_changed component, options={}
      @proxies.each do |proxy|
        proxy.send_aggregated_status component, options if proxy.ready?
      end
    end

    def alarm_state_to_hash alarm_state
      {
        'cId' => alarm_state.component_id,
        'aCId' => alarm_state.code,
        'aTs' => Clock.to_s(alarm_state.timestamp),
        'ack' => (alarm_state.acknowledged ? 'Acknowledged' : 'notAcknowledged'),
        'sS' => (alarm_state.suspended ? 'suspended' : 'notSuspended'),
        'aS' => (alarm_state.active ? 'Active' : 'inActive'),
        'cat' => alarm_state.category,
        'pri' => alarm_state.priority.to_s,
        'rvs' => alarm_state.rvs
      }
    end

    def alarm_suspended_or_resumed alarm_state
      alarm = AlarmIssue.new( alarm_state_to_hash(alarm_state).merge('aSp' => 'Suspend') )
      send_alarm alarm
    end

    def alarm_activated_or_deactivated alarm_state
      alarm = AlarmIssue.new( alarm_state_to_hash(alarm_state).merge('aSp' => 'Issue') )
      send_alarm alarm
    end

    def send_alarm alarm
      @proxies.each do |proxy|
        proxy.send_message alarm if proxy.ready?
      end
    end

    def connect_to_supervisor task, supervisor_settings
      proxy = build_proxy({
        site: self,
        task: @task,
        settings: @site_settings,
        ip: supervisor_settings['ip'],
        port: supervisor_settings['port'],
        logger: @logger,
        archive: @archive,
        collect: @collect
      })
      @proxies << proxy
      proxy.start
      @proxies_condition.signal
    end

    # stop
    def stop
      log "Stopping site #{@site_settings["site_id"]}", level: :info
      super
    end

    def wait_for_supervisor ip, timeout
      supervisor = find_supervisor ip
      return supervisor if supervisor
      wait_for_condition(@proxy_condition,timeout:timeout) { find_supervisor ip }
    rescue Async::TimeoutError
      raise RSMP::TimeoutError.new "Supervisor '#{ip}' did not connect within #{timeout}s"
    end

    def find_supervisor ip
      @proxies.each do |supervisor|
        return supervisor if ip == :any || supervisor.ip == ip
      end
      nil
    end

    def build_component id:, type:, settings:{}
      if type == 'main'
        Component.new id:id, node: self, grouped: true,
          ntsOId: settings['ntsOId'], xNId: settings['xNId']
      else
        Component.new id:id, node: self, grouped: false
      end
    end
  end
end