package peergos.shared.user;

import peergos.shared.*;
import peergos.shared.cbor.*;
import peergos.shared.crypto.*;
import peergos.shared.crypto.hash.*;
import peergos.shared.hamt.*;
import peergos.shared.io.ipfs.multihash.*;
import peergos.shared.storage.*;
import peergos.shared.util.*;

import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;

public class OwnedKeyChamp {

    public final Multihash root;
    private final ChampWrapper champ;
    private final ContentAddressedStorage ipfs;

    public OwnedKeyChamp(Multihash root, ChampWrapper champ, ContentAddressedStorage ipfs) {
        this.root = root;
        this.champ = champ;
        this.ipfs = ipfs;
    }

    public static CompletableFuture<Multihash> createEmpty(PublicKeyHash owner,
                                                           SigningPrivateKeyAndPublicHash writer,
                                                           ContentAddressedStorage ipfs) {
        Champ newRoot = Champ.empty();
        byte[] raw = newRoot.serialize();
        return IpfsTransaction.call(owner,
                tid -> ipfs.put(owner, writer.publicKeyHash, writer.secret.signatureOnly(raw), raw, tid), ipfs);
    }

    public static CompletableFuture<OwnedKeyChamp> build(Multihash root, ContentAddressedStorage ipfs) {
        return ChampWrapper.create(root, b -> reverse(b.data), ipfs)
                .thenApply(c -> new OwnedKeyChamp(root, c, ipfs));
    }

    private static byte[] reverse(byte[] in) {
        byte[] reversed = new byte[in.length];
        for (int i=0; i < in.length; i++)
            reversed[i] = in[in.length - i - 1];
        return reversed;
    }

    public CompletableFuture<Optional<OwnerProof>> get(PublicKeyHash ownedKey) {
        return champ.get(ownedKey.serialize())
                .thenCompose(res -> res.isPresent() ?
                        ipfs.get(res.get()).thenApply(raw -> raw.map(OwnerProof::fromCbor)) :
                        CompletableFuture.completedFuture(Optional.empty()));
    }

    public CompletableFuture<Multihash> add(PublicKeyHash owner,
                                            SigningPrivateKeyAndPublicHash writer,
                                            OwnerProof proof,
                                            TransactionId tid) {
        return ipfs.put(owner, writer, proof.serialize(), tid)
                .thenCompose(valueHash ->
                        champ.put(owner, writer, reverse(proof.ownedKey.serialize()), MaybeMultihash.empty(), valueHash, tid));
    }

    public CompletableFuture<Multihash> remove(PublicKeyHash owner,
                                               SigningPrivateKeyAndPublicHash writer,
                                               PublicKeyHash key,
                                               TransactionId tid) {
        byte[] keyBytes = reverse(key.serialize());
        return champ.get(keyBytes)
                .thenCompose(existing -> champ.remove(owner, writer, keyBytes, existing, tid));
    }

    public CompletableFuture<Boolean> contains(PublicKeyHash ownedKey) {
        return champ.get(ownedKey.multihash.getHash())
                .thenApply(MaybeMultihash::isPresent);
    }

    public <T> CompletableFuture<T> applyToAllMappings(T identity,
                                                       BiFunction<T, Pair<PublicKeyHash, OwnerProof>, CompletableFuture<T>> consumer,
                                                       ContentAddressedStorage ipfs) {
        return champ.applyToAllMappings(identity,
                (acc, pair) -> ! pair.right.isPresent() ? CompletableFuture.completedFuture(acc) :
                        ipfs.get(pair.right.get())
                                .thenApply(raw -> OwnerProof.fromCbor(raw.get()))
                                .thenCompose(proof -> consumer.apply(acc,
                                        new Pair<>(PublicKeyHash.fromCbor(CborObject.fromByteArray(reverse(pair.left.data))), proof))));
    }
}

@@ -520,21 +520,22 @@
 ;%0A%0A    p
-ublic
+rivate
  OwnedKe
@@ -1364,23 +1364,14 @@
  -%3E 
-reverse(
 b.data
-)
 , ip
@@ -1657,32 +1657,138 @@
 eversed;%0A    %7D%0A%0A
+    private static byte%5B%5D keyToBytes(PublicKeyHash key) %7B%0A        return reverse(key.serialize());%0A    %7D%0A%0A
     public Compl
@@ -1877,35 +1877,35 @@
 amp.get(
-ownedKey.serialize(
+keyToBytes(ownedKey
 ))%0A     
@@ -2561,23 +2561,26 @@
 writer, 
-reverse
+keyToBytes
 (proof.o
@@ -2586,28 +2586,16 @@
 ownedKey
-.serialize()
 ), Maybe
@@ -2952,39 +2952,30 @@
 Bytes = 
-reverse(key.serialize()
+keyToBytes(key
 );%0A     
@@ -3209,35 +3209,27 @@
 get(
-ownedKey.multihash.getHash(
+keyToBytes(ownedKey
 ))%0A 

Fix owned key champ - don't reverse the key bytes more than once!