package com.cloudant.sync.datastore;

import com.cloudant.sync.datastore.encryption.KeyProvider;
import com.cloudant.sync.datastore.encryption.NullKeyProvider;
import com.cloudant.sync.event.EventBus;
import com.cloudant.sync.event.Subscribe;
import com.cloudant.sync.notifications.DatabaseClosed;
import com.cloudant.sync.notifications.DatabaseCreated;
import com.cloudant.sync.notifications.DatabaseDeleted;
import com.cloudant.sync.notifications.DatabaseOpened;
import com.cloudant.sync.query.IndexManager;
import com.cloudant.sync.query.IndexManagerImpl;
import com.cloudant.sync.util.Misc;
import com.sun.javafx.scene.shape.PathUtils;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;

import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * Created by tomblench on 27/09/2016.
 */

public class DocumentStore {

    public final Database database;
    public final IndexManager query;
    protected final String databaseName; // only used for events
    private final File location; // needed for close

    /* This map should only be accessed inside synchronized(openDatastores) blocks */
    private static final Map<File, DocumentStore> documentStores = new HashMap<File, DocumentStore>();

    private static EventBus eventBus = new EventBus();

    private DocumentStore(File location, KeyProvider keyProvider) throws DatastoreException, IOException, SQLException {
        this.location = location;
        this.databaseName = location.getName();
        this.database = new DatabaseImpl(location, keyProvider);
        // this will be used to observe purge() events from database and forward them to indexmanager
        this.database.getEventBus().register(this);
        this.query = new IndexManagerImpl(database);
    }

    public static DocumentStore getInstance(File location) throws DatastoreNotCreatedException {
        return getInstance(location, new NullKeyProvider());
    }

    public static DocumentStore getInstance(File location, KeyProvider provider) throws DatastoreNotCreatedException {
        boolean created = checkPathAndCreateIfNeeded(location);
        try {
            synchronized (documentStores) {
                DocumentStore ds = documentStores.get(location);
                if (ds == null) {
                    ds = new DocumentStore(location, provider);
                    documentStores.put(location, ds);
                    eventBus.post(new DatabaseOpened(ds.databaseName));
                }
                if (created) {
                    eventBus.post(new DatabaseCreated(ds.databaseName));
                }
                return ds;
            }
        }
        catch (IOException e) {
            throw new DatastoreNotCreatedException("Database not found: " + location, e);
        } catch (SQLException e) {
            throw new DatastoreNotCreatedException("Database not initialized correctly: " + location, e);
        } catch (DatastoreException e) {
            throw new DatastoreNotCreatedException("Datastore not initialized correctly: " + location, e);
        }
    }

    private static boolean checkPathAndCreateIfNeeded(File location) {
        //logger.fine("Datastore path: " + directoryPath);

        boolean created = false;
        if(!location.exists()){
            created = location.mkdir();
        }

        Misc.checkArgument(location.isDirectory(), "Input path is not a valid directory");
        Misc.checkArgument(location.canWrite(), "Datastore directory is not writable");
        return created;
    }

    public void close() {
        synchronized (documentStores) {
            DocumentStore ds = documentStores.get(location);
            if (ds != null) {
                System.out.println("removing "+location);
                ((DatabaseImpl)database).close();
                ((IndexManagerImpl)query).close();
                documentStores.remove(location);
            }
            // TODO warn if not in map?
        }
        eventBus.post(new DatabaseClosed(databaseName));
    }

    public void delete() throws IOException {
        synchronized (documentStores) {
            DocumentStore ds = documentStores.remove(location);
            if (ds != null) {
                ((DatabaseImpl)database).close();
                ((IndexManagerImpl)query).close();
            }
            if (!location.exists()) {
                String msg = String.format(
                        "Datastore %s doesn't exist on disk", location
                );
                throw new IOException(msg);
            } else {
                FileUtils.deleteDirectory(location);
                eventBus.post(new DatabaseDeleted(databaseName));
            }
        }
    }

    public static List<File> listInstances() {
        return new ArrayList<File>(documentStores.keySet());
    }

    public static EventBus getEventBus() {
        return eventBus;
    }

}

@@ -198,50 +198,8 @@
 us;%0A
-import com.cloudant.sync.event.Subscribe;%0A
 impo
@@ -550,130 +550,41 @@
 sc;%0A
-import com.sun.javafx.scene.shape.PathUtils;%0A%0Aimport org.apache.commons.io.FileUtils;%0Aimport org.apache.commons.io.Filenam
+%0Aimport org.apache.commons.io.Fil
 eUti
@@ -699,38 +699,8 @@
 st;%0A
-import java.util.Collections;%0A
 impo
@@ -787,79 +787,22 @@
 til.
-concurrent.ConcurrentHashMap;%0Aimport java.util.concurrent.ConcurrentMap
+logging.Logger
 ;%0A%0A/
@@ -1312,24 +1312,124 @@
 ventBus();%0A%0A
+    private static final Logger logger = Logger.getLogger(DocumentStore.class.getCanonicalName());%0A%0A
     private 
@@ -3291,18 +3291,16 @@
         
-//
 logger.f
@@ -3328,21 +3328,16 @@
 %22 + 
-directoryPath
+location
 );%0A%0A
@@ -3823,66 +3823,8 @@
 ) %7B%0A
-                System.out.println(%22removing %22+location);%0A
     
@@ -3999,35 +3999,122 @@
     
-// TODO warn if not in map?
+else %7B%0A                throw new IllegalStateException(%22DocumentStore %22+location+%22 already closed%22);%0A            %7D
 %0A   
@@ -4651,16 +4651,53 @@
      );%0A
+                logger.warning(msg);%0A
         

Fix logging/imports, throw exception for DocumentStore already closed