# encoding: utf-8
# This file is distributed under Ting Yun's license terms.


require 'ting_yun/agent'
require 'zlib'
require 'ting_yun/ting_yun_service/http'
require 'ting_yun/support/collector'
require 'ting_yun/support/serialize/encodes'
require 'ting_yun/support/timer_lib'
require 'ting_yun/support/exception'
require 'ting_yun/support/serialize/json_marshaller'
require 'ting_yun/ting_yun_service/upload_service'
require 'ting_yun/version'

module TingYun
  class TingYunService
    include Http
    include UploadService

    CONNECTION_ERRORS = [Timeout::Error, EOFError, SystemCallError, SocketError].freeze

    PROTOCOL_VERSION = 1


    attr_accessor :request_timeout,
                  :appSessionKey,
                  :data_version,
                  :metric_id_cache,
                  :applicationId,
                  :ssl_cert_store,
                  :shared_tcp_connection,
                  :quantile_cache


    def initialize(license_key=nil)

      @license_key = license_key || TingYun::Agent.config[:'license_key']
      @request_timeout = TingYun::Agent.config[:timeout]
      @data_version = "1.4"
      @marshaller =TingYun::Support::Serialize::JsonMarshaller.new
      @metric_id_cache = {}
      @quantile_cache = {}
    end

    def connect(settings={})
      if host = get_redirect_host
        @collector = TingYun::Support.collector_from_host(host)
      end
      response = invoke_remote(:initAgentApp, [settings])
      TingYun::Agent.logger.info("initAgentApp response: #{response}") if TingYun::Agent.config[:'nbs.audit_mode']
      @applicationId = response['applicationId']
      @appSessionKey = response['appSessionKey']
      response
    end

    def get_redirect_host
      @collector=TingYun::Support.collector
      invoke_remote(:getRedirectHost)
    end

    def force_restart
      @applicationId = nil
      @appSessionKey = nil
      @metric_id_cache = {}
      @quantile_cache = {}
      close_shared_connection
    end


    # send a message via post to the actual server. This attempts
    # to automatically compress the data via zlib if it is large
    # enough to be worth compressing, and handles any errors the
    # server may return

    # private

    def invoke_remote(method, payload=[], options = {})

      data = nil
      payload = payload[0]  if method == :initAgentApp
      begin
        data = @marshaller.dump(payload, options)
      rescue StandardError, SystemStackError => e
        handle_serialization_error(method, e)
      end
      # serialize_finish_time = Time.now
      uri = remote_method_uri(method)
      full_uri = "#{@collector}#{uri}"

      if audit_mode?
        TingYun::Agent.logger.info("the prepare data: #{data} to url: #{full_uri}")
      else
        TingYun::Agent.logger.info("prepare to send data")
      end

      data, encoding = compress_request_if_needed(data)

      response = send_request(:data      => data,
                              :uri       => uri,
                              :encoding  => encoding,
                              :collector => @collector)

      if audit_mode?
        TingYun::Agent.logger.info("the return data: #{response.body}")
      else
        TingYun::Agent.logger.info("the send-process end")
      end
      @marshaller.load(decompress_response(response))
    ensure
      # take the initiative to GC
      payload = nil
      data = nil
    end

    def audit_mode?
      TingYun::Agent.config[:'nbs.audit_mode']
    end

    def handle_serialization_error(method, e)
      msg = "Failed to serialize #{method} data using #{@marshaller.class.to_s}: #{e.inspect}"
      error = TingYun::Support::Exception::SerializationError.new(msg)
      error.set_backtrace(e.backtrace)
      raise error
    end

  end
end