require 'gcrypto'
require_relative 'secretkey'
require_relative 'error'
require_relative 'io_utils'
require_relative 'provider'
require_relative 'global'

module GcryptoJce
  module KDF
    module HKDF
      
      #def extract(opts = { })
      #  
      #  salt = opts[:salt]
      #  if salt.nil?
      #    salt = Java::byte[hmac.mac_length].new
      #  end       
      # 
      #  secKey = javax.crypto.spec.SecretKeySpec.new(salt, "AES")
      #  
      #  hmacSpec = opts[:hmacEng] || 'HmacSHA256'
      #  hmac = javax.crypto.Mac.getInstance(hmacSpec)
      #  hmac.init(secKey)

      #  ikm = opts[:ikm]
      #  if ikm.nil?
      #    raise GcryptoJce::Error, "Input Key Material (IKM) cannot be nil. It can be any random value or message itself"
      #  end
      #  is = IoUtils.load_input(ikm)
      #  
      #  os = java.io.ByteArrayOutputStream.new

      #  begin
      #    bufConf = opts[:int_buf] || { }
      #    total = 0
      #    IoUtils.read_chunk(is, bufConf) do |buf, from, len|
      #      os.write(hmac.update(buf, from, len))
      #      total += len
      #      GcryptoJce::GConf.instance.glog.debug "Processed #{NumberHelper.number_to_human_size(total)}"
      #    end

      #    os.write(hmac.doFinal)
      #  rescue Exception
      #  ensure
      #    begin
      #      is.close
      #      os.flush
      #      os.close
      #    rescue Exception
      #    end
      #  end

      #  
      #end
      # end extract
      #

      # allow key to be expended to arbitary length according to RFC5869
      def expand(opts = {})

        key = opts[:key]
        if key.nil?
          raise GcryptoJce::Error, "Key is not given to HKDF for expansion operation"
        else
          key = IoUtils.ensure_java_bytes(key)
        end
        
        kcType = opts[:key_type] || :aes
        case kcType
        when :aes, "AES", "aes"
          secKey = javax.crypto.spec.SecretKeySpec.new(key, "AES")
        else
          raise GcryptoJce::Error, "Unknown key type '#{kcType}'"
        end

        hmacSpec = opts[:hmacEng] || 'HmacSHA256'
        hmac = javax.crypto.Mac.getInstance(hmacSpec)
        hmac.init(secKey)
       
        ctxInfo = opts[:contextInfo]
        if ctxInfo.nil?
          ctxInfoBytes = "".to_java.getBytes
        else
          ctxInfoBytes = IoUtils.ensure_java_bytes(ctxInfo)
        end
        
        baseBuf = java.io.ByteArrayOutputStream.new
        baseBuf.write(key)
        baseBuf.write(ctxInfoBytes)
        baseBufBin = baseBuf.toByteArray
        baseBufLen = baseBufBin.length
          
        GcryptoJce::GConf.instance.glog.debug "baseBufBin : #{baseBufBin}"
        
        baseBlock = Java::byte[baseBufLen+1].new
        java.lang.System.arraycopy(baseBufBin,0,baseBlock,0,baseBufLen)
        GcryptoJce::GConf.instance.glog.debug "baseBlock : #{baseBlock}"

        # always in bits
        reqKeyLen = opts[:outKeyLen]
        loopCnt = ((reqKeyLen*1.0) / (hmac.mac_length*8*1.0)).ceil
        GcryptoJce::GConf.instance.glog.debug "loopCnt : #{loopCnt}"
        
        exOut = java.io.ByteArrayOutputStream.new
        
        cnt = 0
        while (cnt < loopCnt)  do
          baseBlock[baseBlock.length-1] = cnt
          #GcryptoJce::GConf.instance.glog.debug "baseBlock (#{cnt}) : #{baseBlock[baseBlock.length-1]}"
          hash = hmac.doFinal(baseBlock)
          exOut.write(hash)
          cnt += 1
        end
        
        expanded = exOut.toByteArray
        ret = Java::byte[reqKeyLen/8].new
        java.lang.System.arraycopy(expanded,0,ret,0,ret.length)
        
        ret
        
      end
      # end expand
      #
  
    end
    # end HKDF
    #
 
    class HKDFEngine
      extend HKDF
    end

  end
  # end KDF
  #
end
# end GcryptoJce