package iot.core.services.device.registry.client;

import java.util.Date;
import java.util.HashMap;
import java.util.Optional;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

import io.vertx.core.Vertx;
import iotcore.service.device.AlwaysPassingDeviceSchemaValidator;
import iotcore.service.device.Device;
import iotcore.service.device.InMemoryDeviceRegistry;

public class Main {

    static Client createLocalClient() {
        return new LocalClient(new InMemoryDeviceRegistry(new AlwaysPassingDeviceSchemaValidator()));
    }

    static Client createAmqpClient(final Vertx vertx) {
        return AmqpClient.create()
                .build(vertx);
    }

    public static void main(final String[] args) throws Exception {

        final Vertx vertx = Vertx.vertx();

        try (final Client client = createAmqpClient(vertx)) {

            asyncSave(client, "id2");
            asyncFind(client, "id2");

            syncSave(client, "id1");
            syncFind(client, "id1");

            Thread.sleep(1_000);

        } finally {
            vertx.close();
        }

    }

    private static void syncSave(final Client client, final String id) {
        final Device device = createNewDevice(id);

        final String result = client.sync().save(device);
        System.out.format("save[sync]: %s -> %s%n", device, result);
    }

    private static void syncFind(final Client client, final String id) {
        final Optional<Device> result = client.sync().findById(id);
        System.out.format("find[sync]: %s -> %s%n", id,
                result
                        .map(Object::toString)
                        .orElse("<null>"));
    }

    private static void asyncSave(final Client client, final String id) throws InterruptedException {

        final Semaphore s = new Semaphore(0);

        final Device device = createNewDevice(id);

        final CompletionStage<String> f = client.async().save(device);

        f.whenComplete((result, error) -> {
            if (error != null) {
                System.err.println("save[async]: operation failed");
                error.printStackTrace();
            } else {
                System.out.format("save[async]: %s -> %s%n", device, result);
            }
        })
                .thenRun(s::release);

        if (!s.tryAcquire(5, TimeUnit.SECONDS)) {
            System.out.println("Cancel");
            f.toCompletableFuture().cancel(true);
        }
    }

    private static void asyncFind(final Client client, final String id) throws InterruptedException {
        final Semaphore s = new Semaphore(0);

        final CompletionStage<Optional<Device>> f = client.async().findById(id);

        f.whenComplete((result, error) -> {
            if (error != null) {
                System.err.println("find[async]: operation failed");
                error.printStackTrace();
            } else {
                System.out.format("find[async]: %s -> %s%n", id,
                        result
                                .map(Object::toString)
                                .orElse("<null>"));
            }
        })
                .thenRun(s::release);

        if (!s.tryAcquire(5, TimeUnit.SECONDS)) {
            System.out.println("Cancel");
            f.toCompletableFuture().cancel(true);
        }
    }

    private static Device createNewDevice(final String id) {
        final Date now = new Date();
        final Device device = new Device(id, now, now, "my:device", new HashMap<>());
        return device;
    }
}

@@ -994,27 +994,105 @@
         sync
-Sav
+Create(client, %22id1%22);%0A            syncSave(client, %22id1%22);%0A            syncUpdat
 e(client, %22i
@@ -1266,229 +1266,723 @@
 sync
-Save(final Client client, final String id) %7B%0A        final Device device = createNewDevice(id);%0A%0A        final String result = client.sync().save(device);%0A        System.out.format(%22save%5Bsync%5D: %25s -%3E %25s%25n%22, device, result
+Create(final Client client, final String id) %7B%0A        final Device device = createNewDevice(id);%0A%0A        final String result = client.sync().create(device);%0A        System.out.format(%22create%5Bsync%5D: %25s -%3E %25s%25n%22, device, result);%0A    %7D%0A%0A    private static void syncSave(final Client client, final String id) %7B%0A        final Device device = createNewDevice(id);%0A%0A        final String result = client.sync().save(device);%0A        System.out.format(%22save%5Bsync%5D: %25s -%3E %25s%25n%22, device, result);%0A    %7D%0A%0A    private static void syncUpdate(final Client client, final String id) %7B%0A        final Device device = createNewDevice(id);%0A%0A        client.sync().update(device);%0A        System.out.format(%22update%5Bsync%5D: %25s%25n%22, device
 );%0A 

Implement sync test for create and update