use OpenSSL::NativeLib;
use NativeCall;

constant MD5-CTX-SIZE      = 92;
constant MD5_DIGEST_LENGTH = 16;

class OpenSSL::Digest::MD5
{
    has $!context;

    sub MD5_Init(Blob)                 returns int32 is native(&gen-lib) { * }
    sub MD5_Update(Blob, Blob, size_t) returns int32 is native(&gen-lib) { * }
    sub MD5_Final(Blob, Blob)          returns int32 is native(&gen-lib) { * }

    submethod BUILD() {
        $!context = buf8.allocate(MD5-CTX-SIZE);
        self.init;
    }

    method init() {
        MD5_Init($!context) or die "Bad Call to MD5_Init";
        return self;
    }

    multi method add(Str $msg) {
        self.add($msg.encode('ascii'));
    }

    multi method add(Blob $msg) {
        MD5_Update($!context, $msg, $msg.bytes) or die "Bad Call to MD5_Update";
        return self;
    }

    method addfile(Str $filename, Int $bufsiz = 64 * 1024) {
        my $fh = open($filename, :bin) or die "open $filename";
        LEAVE { .close with $fh }
        self.add($fh.read($bufsiz)) while not $fh.eof;
        return self;
    }

    method hash() {
        my $digest = buf8.allocate(MD5_DIGEST_LENGTH);
        MD5_Final($digest, $!context) or die "Bad Call to MD5_Final";
        self.init;
        return $digest;
    }

    method hex() {
        self.hash.list».fmt("%02x").join;
    }
}
