// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.persistence;

import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.TenantName;
import com.yahoo.vespa.hosted.controller.Application;
import com.yahoo.vespa.hosted.controller.NotExistsException;
import com.yahoo.vespa.hosted.controller.tenant.AthenzTenant;
import com.yahoo.vespa.hosted.controller.tenant.Tenant;
import com.yahoo.vespa.hosted.controller.tenant.UserTenant;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * A controller db implementation backed by in-memory structures. Useful for testing.
 * 
 * @author Stian Kristoffersen
 */
public class MemoryControllerDb implements ControllerDb {

    private final Map<TenantName, Tenant> tenants = new HashMap<>();
    private final Map<String, Application> applications = new HashMap<>();

    private void createTenant(Tenant tenant) {
        if (tenants.containsKey(tenant.name())) {
            throw new IllegalArgumentException("Tenant '" + tenant + "' already exists");
        }
        tenants.put(tenant.name(), tenant);
    }

    private void updateTenant(Tenant tenant) {
        if (!tenants.containsKey(tenant.name())) {
            throw new NotExistsException(tenant.name().value());
        }
        tenants.put(tenant.name(), tenant);
    }

    @Override
    public void createTenant(UserTenant tenant) {
        createTenant((Tenant) tenant);
    }

    @Override
    public void createTenant(AthenzTenant tenant) {
        createTenant((Tenant) tenant);
    }

    @Override
    public void updateTenant(AthenzTenant tenant) {
        updateTenant((Tenant) tenant);
    }

    @Override
    public void deleteTenant(TenantName name) {
        if (tenants.remove(name) == null) {
            throw new NotExistsException(name.value());
        }
    }

    @Override
    public Optional<Tenant> getTenant(TenantName name) {
        return getTenant(name, Tenant.class);
    }

    @Override
    public Optional<AthenzTenant> getAthenzTenant(TenantName name) {
        return getTenant(name, AthenzTenant.class);
    }

    private <T extends Tenant> Optional<T> getTenant(TenantName name, Class<T> type) {
        return Optional.ofNullable(tenants.get(name)).map(type::cast);
    }

    @Override
    public List<Tenant> listTenants() {
        return new ArrayList<>(tenants.values());
    }

    @Override
    public void store(Application application) {
        applications.put(path(application.id()), application);
    }

    @Override
    public void deleteApplication(ApplicationId applicationId) {
        applications.remove(path(applicationId));
    }

    @Override
    public Optional<Application> getApplication(ApplicationId applicationId) {
        return Optional.ofNullable(applications.get(path(applicationId)));
    }

    @Override
    public List<Application> listApplications() {
        return new ArrayList<>(applications.values());
    }

    @Override
    public List<Application> listApplications(TenantName name) {
        return applications.values().stream()
                                    .filter(a -> a.id().tenant().equals(name))
                                    .collect(Collectors.toList());
    }

}

@@ -321,69 +321,8 @@
 on;%0A
-import com.yahoo.vespa.hosted.controller.NotExistsException;%0A
 impo
@@ -500,62 +500,8 @@
 t;%0A%0A
-import java.util.ArrayList;%0Aimport java.util.HashMap;%0A
 impo
@@ -523,30 +523,8 @@
 st;%0A
-import java.util.Map;%0A
 impo
@@ -549,44 +549,8 @@
 nal;
-%0Aimport java.util.stream.Collectors;
 %0A%0A/*
@@ -599,28 +599,22 @@
  by 
-in-memory structures
+a mock curator
 . Us
@@ -752,604 +752,51 @@
 al M
-ap%3CTenantName, Tenant%3E tenants = new HashMap%3C%3E();%0A    private final Map%3CString, Application%3E applications = new HashMap%3C%3E();%0A%0A    private void createTenant(Tenant tenant) %7B%0A        if (tenants.containsKey(tenant.name())) %7B%0A            throw new IllegalArgumentException(%22Tenant '%22 + tenant + %22' already exists%22);%0A        %7D%0A        tenants.put(tenant.name(), tenant);%0A    %7D%0A%0A    private void updateTenant(Tenant tenant) %7B%0A        if (!tenants.containsKey(tenant.name())) %7B%0A            throw new NotExistsException(tenant.name().value());%0A        %7D%0A        tenants.put(tenant.name(), tenant);%0A    %7D
+ockCuratorDb curator = new MockCuratorDb();
 %0A%0A  
@@ -866,37 +866,35 @@
        c
-reateTenant((Tenant) 
+urator.writeTenant(
 tenant);
@@ -980,29 +980,27 @@
    c
-reateTenant((Tenant) 
+urator.writeTenant(
 tena
@@ -1089,30 +1089,28 @@
     
-updateTenant((Tenant) 
+curator.writeTenant(
 tena
@@ -1195,109 +1195,35 @@
     
-if (tenants.remove(name) == null) %7B%0A            throw new NotExistsException(name.value());%0A        %7D
+curator.removeTenant(name);
 %0A   
@@ -1316,36 +1316,31 @@
 urn 
-getTenant(name, Tenant.class
+curator.readTenant(name
 );%0A 
@@ -1447,207 +1447,37 @@
 urn 
-getTenant(name, AthenzTenant.class);%0A    %7D%0A%0A    private %3CT extends Tenant%3E Optional%3CT%3E getTenant(TenantName name, Class%3CT%3E type) %7B%0A        return Optional.ofNullable(tenants.get(name)).map(type::cast
+curator.readAthenzTenant(name
 );%0A 
@@ -1555,40 +1555,28 @@
 urn 
-new ArrayList%3C%3E(tenants.values()
+curator.readTenants(
 );%0A 
@@ -1656,31 +1656,22 @@
     
-applications.put(path(a
+curator.writeA
 ppli
@@ -1680,16 +1680,9 @@
 tion
-.id()), 
+(
 appl
@@ -1789,32 +1789,33 @@
     
-applications.remove(path
+curator.removeApplication
 (app
@@ -1825,17 +1825,16 @@
 ationId)
-)
 ;%0A    %7D%0A
@@ -1946,49 +1946,31 @@
 urn 
-Optional.ofNullable(applications.get(path
+curator.readApplication
 (app
@@ -1980,18 +1980,16 @@
 ationId)
-))
 ;%0A    %7D%0A
@@ -2072,25 +2072,21 @@
 urn 
-new ArrayList%3C%3E(a
+curator.readA
 ppli
@@ -2092,25 +2092,17 @@
 ications
-.values()
+(
 );%0A    %7D
@@ -2201,182 +2201,37 @@
 urn 
-applications.values().stream()%0A                                    .filter(a -%3E a.id().tenant().equals(name))%0A                                    .collect(Collectors.toList()
+curator.readApplications(name
 );%0A 

Use serialization in MemoryControllerDb