require 'pkernel'
require 'pkernel_jce'

require_relative 'error'
require_relative 'io_utils'
require_relative 'provider'
require_relative 'global'

module GcryptoJce
  module SecretKey
    
    def generate(opts = { })
      
      skType = opts[:type]
      if skType.nil?
        raise GcryptoJce::Error, "Secret key type is not given for generation"
      else
        skType = to_algo_string(skType)
      end

      prov = GcryptoJce::Provider.handle_options(opts, nil)
      
      if prov.nil?
        GcryptoJce::GConf.instance.glog.debug "Provider is nil"
        kg = javax.crypto.KeyGenerator.getInstance(skType)
      else
        GcryptoJce::GConf.instance.glog.debug "Provider is '#{prov.name}'"
        kg = javax.crypto.KeyGenerator.getInstance(skType, prov)
      end

      keyLen = opts[:keyLen]
      secRandom = opts[:secRandom]
      if keyLen.nil? or keyLen.to_i == 0
        raise GcryptoJce::Error, "Key length not given or invalid"
      else
        if secRandom.nil?
          kg.init(keyLen.to_i)
        else
          kg.init(keyLen.to_i, secRandom)
        end
      end 

      kg.generateKey
      
    end
    # end generate()
    #

    def SecretKey.is_secret_key?(obj)
      if obj.nil?
        false
      else
        obj.java_kind_of?(javax.crypto.spec.SecretKeySpec)
      end
    end

    def dump(sk, opts = { })
      file = opts[:file]
      baos = java.io.ByteArrayOutputStream.new
      
      if not (file.nil? or file.empty?)
        os = java.io.ObjectOutputStream.new(java.io.FileOutputStream.new(file))
      else
        os = java.io.ObjectOutputStream.new(baos)
      end

      os.writeObject(sk)
      os.flush
      os.close

      if not (file.nil? or file.empty?)
      else
        baos.toByteArray
      end
    end
    # end dump()

    def load(opts = { })
      file = opts[:file]
      bin = opts[:bin]
      if not (file.nil? or file.empty?)
        skBin = IoUtils.file_to_memory_byte_array(file)
      elsif not bin.nil?
        skBin = IoUtils.ensure_java_bytes(bin)
      else
        raise GcryptoJce::Error, "No file or buffer given to load secret key"
      end

      skOs = java.io.ObjectInputStream.new(java.io.ByteArrayInputStream.new(skBin))
      sk = skOs.readObject
      skOs.close
      
      sk
    end
    # end load()

    def load_from_bin(opts = { })
      file = opts[:file]
      bin = opts[:bin]
      if not (file.nil? or file.empty?)
        skBin = IoUtils.file_to_memory_byte_array(file)
      elsif not bin.nil?
        skBin = IoUtils.ensure_java_bytes(bin)
      else
        raise GcryptoJce::Error, "No file or buffer given to load secret key from binary"
      end
      
      skType = opts[:type]
      if skType.nil? or skType.empty?
        raise GcryptoJce::Error, "Secret Key type is not given to load from binary"
      end
      
      javax.crypto.spec.SecretKeySpec.new(skBin, skType)
    end

    def to_algo_string(key)
      key = key.to_s.upcase
      case key
      when "AES"
        "AES"
      when "3DES", "TRIPLEDES", "TRIPLE-DES", "3-DES"
        "3DES"
      else
        "AES"
        #raise GcryptoJce::Error, "Unknown algo '#{key}'"
      end
    end
    # 
    # end to_algo_string
    #

    def SecretKey.key_type(secKeySpec)
      algo = secKeySepc.algorithm
      case algo
      when "AES"
        "AES"
      when "3DES"
        "3DES"
      else
        raise GcryptoJce::Error, "Unknown key type '#{algo}'"
      end
    end
    # 
    # end key_type
    #
  
  end
  # 
  # end module SecretKey
  #

  class SecretKeyFactory
    extend SecretKey
  end
end
# 
# end module Gcrypto
#