require_relative '../MiqDisk'
require 'ostruct'

module AzureDiskCommon
  # The maximum read length that supports MD5 return.
  MAX_READ_LEN  = 1024 * 1024 * 4
  SECTOR_LENGTH = 512

  def d_init_common(d_info)
    @blockSize = SECTOR_LENGTH
    if d_info.blob_uri
      d_init_blob_disk(d_info)
    else
      d_init_managed_disk(d_info)
    end
    $log.debug("#{@class}: open(#{@disk_path})")
    @t0 = Time.now.to_i
    @reads = 0
    @bytes = 0
    @split_reads = 0
  end

  def d_init_blob_disk(d_info)
    @blob_uri         = d_info.blob_uri
    @storage_acct_svc = d_info.storage_acct_svc
    @my_class         = "AzureBlobDisk"
    uri_info          = @storage_acct_svc.parse_uri(@blob_uri)
    @container        = uri_info[:container]
    @blob             = uri_info[:blob]
    @acct_name        = uri_info[:account_name]
    @snapshot         = uri_info[:snapshot]
    @storage_acct     = @storage_acct_svc.accounts_by_name[@acct_name]
    @disk_path        = @blob_uri
    raise "AzureBlob: Storage account #{@acct_name} not found." unless @storage_acct
  end

  def d_init_managed_disk(d_info)
    @disk_name        = d_info.disk_name
    @storage_disk_svc = d_info.storage_disk_svc
    @resource_group   = d_info.resource_group
    @my_class         = "AzureManagedDisk"
    @disk_path        = @disk_name
  end

  def d_close_common
    @managed_disk&.close
    return nil unless $log.debug?
    t1 = Time.now.to_i
    $log.debug("#{@my_class}: close(#{@disk_path})")
    $log.debug("#{@my_class}: (#{@disk_path}) time:  #{t1 - @t0}")
    $log.debug("#{@my_class}: (#{@disk_path}) reads: #{@reads}, split_reads: #{@split_reads}")
    $log.debug("#{@my_class}: (#{@disk_path}) bytes: #{@bytes}")
    nil
  end

  def d_read_common(pos, len)
    return blob_read(pos, len) unless len > MAX_READ_LEN

    @split_reads += 1
    ret = ""
    blocks, rem = len.divmod(MAX_READ_LEN)

    blocks.times do
      ret << blob_read(pos, MAX_READ_LEN)
    end
    ret << blob_read(pos, rem) if rem > 0

    ret
  end

  def blob_properties
    @blob_properties ||= begin
      options = @snapshot ? {:date => @snapshot} : {}
      @storage_acct.blob_properties(@container, @blob, key, options)
    end
  end

  def blob_read(start_byte, length)
    $log.debug("#{@my_class}#blob_read(#{start_byte}, #{length})")
    options = {
      :start_byte => start_byte,
      :length     => length
    }
    if @storage_acct
      options[:date] = @snapshot if @snapshot
      ret = @storage_acct.get_blob_raw(@container, @blob, key, options)
    else
      ret = managed_disk.read(options)
    end

    @reads += 1
    @bytes += ret.body.length

    ret.body
  end

  def key
    @key ||= @storage_acct_svc.list_account_keys(@storage_acct.name, @storage_acct.resource_group).fetch('key1')
  end

  def managed_disk
    @managed_disk ||= @storage_disk_svc.open(@disk_name, @resource_group)
  end
end
