var jwt = {
    jwsHeader: "eyJhbGciOiJFUzI1NksiLCJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdfQ..",
    getNonce: function()
    {
        var result           = '';
        var characters       = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
        var charactersLength = characters.length;
        for ( var i = 0 ; i < 10 ; i++ )
        {
           result += characters.charAt ( Math.floor ( Math.random() * charactersLength ) );
        }
        result += ( new Date() ).getTime().toString();
        return result;
    },
    sign: function ( message , privateKey )
    {
        if ( privateKey.indexOf ( '0x' ) === 0 )
        {
            privateKey = privateKey.substr ( 2 );
        }
        var hash = CryptoJS.SHA256( message ).toString ( CryptoJS.enc.Hex );
        console.log ( 'Message: ' + message );
        console.log ( 'Hash: ' + hash );
        var signature = EthJS.Util.ecsign ( hexStringToUint8Array ( hash ) , hexStringToUint8Array ( privateKey ) );
        return uint8ArrayToHexString ( signature.r ) + uint8ArrayToHexString ( signature.s ) + signature.v.toString ( 16 );

    },
    recoverKey: function ( message , signature )
    {
        if ( signature.indexOf ( '0x' ) === 0 )
        {
            signature = signature.substr ( 2 );
        }
        var hash = CryptoJS.SHA256( message ).toString ( CryptoJS.enc.Hex );
        var recoveredPublicKey = uint8ArrayToHexString ( EthJS.Util.ecrecover ( hexStringToUint8Array ( hash ) , parseInt ( signature.substr ( signature.length - 2 ) , 16 ) , EthJS.Util.toBuffer ( hexStringToUint8Array ( signature.substr ( 0 , 64 ) ) ) , EthJS.Util.toBuffer ( hexStringToUint8Array ( signature.substr ( 64 , 64 ) ) ) ) );
        return recoveredPublicKey;
    },
    createPresentationProof: function ( presentation , did )
    {
        var nonce = this.getNonce();
        var now = parseInt ( new Date().getTime() / 1000 );
        var jwtPayload = 
        {
            "iss": presentation.holder,
            "iat": now,
            "nbf": now,
            "nonce": nonce,
            "vp": presentation
        };
        var signature = this.sign ( serializeObject ( jwtPayload ) , did.verificationMethod[0].privateKeyHex );
        var date = new Date();
        date.setTime ( now * 1000 );
        var proof = {
            "challenge": nonce,
            "created": date.toISOString().replace ( 'Z' , '+00:00' ),
            "proofPurpose": "authentication",
            "verificationMethod": did.verificationMethod[0].id,
            "type": "EcdsaSecp256k1Signature2019",
            "jws": this.jwsHeader + hexToB64 ( signature )
        }
        return proof;
    },
    createRequestProof: function ( data , did )
    {
        var nonce = this.getNonce();
        var jwtPayload = 
        {
            "iss": did.id,
            "iat": data.timestamp,
            "nonce": nonce,
            "data": data
        };
        var signature = this.sign ( serializeObject ( jwtPayload ) , did.verificationMethod[0].privateKeyHex );
        var date = new Date();
        date.setTime ( data.timestamp * 1000 );
        var proof = {
            "challenge": nonce,
            "created": date.toISOString().replace ( 'Z' , '+00:00' ),
            "proofPurpose": "authentication",
            "verificationMethod": did.verificationMethod[0].id,
            "type": "EcdsaSecp256k1Signature2019",
            "jws": this.jwsHeader + hexToB64 ( signature )
        }
        return proof;
    },
    validateResponseProof: function ( data , proof , did )
    {
        var jwtPayload = 
        {
            "iss": did.id,
            "iat": parseInt ( new Date ( proof.created ).getTime() / 1000 ),
            "nonce": proof.challenge,
            "data": data
        };
        var recoveredPublicKey = this.recoverKey ( serializeObject ( jwtPayload ) , b64ToHex ( proof.jws.substr ( this.jwsHeader.length ) ).padStart ( 130 , '0' ) );
        var x = hexToB64 ( recoveredPublicKey.substr ( 0 , 64 ) );
        var y = hexToB64 ( recoveredPublicKey.substr ( 64 , 64 ) );      
        for ( var i = 0 ; i < did.verificationMethod.length ; i++ )
        {
            if ( did.verificationMethod[i].id == proof.verificationMethod )
            {
                return ( did.verificationMethod[i].publicKeyJwk.x == x && did.verificationMethod[i].publicKeyJwk.y == y );
            }
        }
        return false;
    },
    keysToDid: function ( keys )
    {
        let private = EthJS.Util.toBuffer ( keys.privateKey );
        let public = EthJS.Util.privateToPublic ( private ).toString ( 'hex' );
        var did = { id: 'did:agnia:' + keys.address.substr ( 2 ) , verificationMethod: [] };
        did.verificationMethod.push ( { 
            controller: did.id ,
            id: did.id + '#key-1' ,
            type: "EcdsaSecp256k1VerificationKey2019" ,
            publicKeyJwk: {
                crv: 'secp256k1',
                kid: 'key-1',
                kty: 'EC',
                x: hexToB64 ( public.substr ( 0 , 64 ) ),
                y: hexToB64 ( public.substr ( 64 , 64 ) )
        } } );
        return did;
    }
}