package com.collective.celos;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;

/**
 * Brutally simple persistent implementation of StateDatabase
 * that stores slot states as JSON files in the file system.
 * 
 * The database has a top-level directory.  Inside it, there
 * is a subdirectory for each workflow.  Inside each workflow directory
 * is one directory per day.  Inside each day directory is one JSON file
 * per workflow run.
 * 
 * /
 *   workflow-1/
 *     2013-12-02/
 *       16:00:00.000Z
 *       17:00:00.000Z
 *     ...
 *   workflow-2/
 *     2013-12-02/
 *       16:00:00.000Z
 *       17:00:00.000Z
 *     ...
 *   ...
 *   
 * A JSON file looks like this:
 * 
 * {"status":"WAITING"}
 * 
 * or
 * 
 * {"status":"RUNNING","externalID":"23873218-13202130978213-W"}
 */
public class FileSystemStateDatabase implements StateDatabase {

    private static final String CHARSET = "UTF-8";

    private final ObjectMapper mapper = new ObjectMapper();
    private final ScheduledTimeFormatter formatter = new ScheduledTimeFormatter();
    private final File dir;
    
    /**
     * Creates a new DB that stores data in the given directory, which must exist.
     */
    public FileSystemStateDatabase(File dir) throws IOException {
        this.dir = Util.requireNonNull(dir);
        if (!dir.exists()) {
            throw new IOException("Database directory " + dir + " doesn't exist.");
        }
    }

    @Override
    public SlotState getSlotState(SlotID id) throws Exception {
        File file = getSlotFile(id);
        if (!file.exists()) {
            return null;
        } else {
            String json = FileUtils.readFileToString(file, CHARSET);
            return SlotState.fromJSONNode(id, (ObjectNode) mapper.readTree(json));
        }
    }

    @Override
    public void putSlotState(SlotState state) throws Exception {
        String json = mapper.writeValueAsString(state.toJSONNode());
        File file = getSlotFile(state.getSlotID());
        FileUtils.forceMkdir(file.getParentFile());
        FileUtils.write(file, json, CHARSET);
    }

    private File getSlotFile(SlotID slotID) {
        return new File(getDayDir(slotID), getSlotFileName(slotID));
    }

    /** Returns the directory containing a day's data inside the workflow dir. */
    private File getDayDir(SlotID slotID) {
        File workflowDir = getWorkflowDir(slotID);
        File dayDir = new File(workflowDir, formatter.formatDatestamp(slotID.getScheduledTime()));
        return dayDir;
    }

    /** Returns the directory containing all data for the slot's workflow. */
    private File getWorkflowDir(SlotID slotID) {
        return dir.toPath().resolve("state").resolve(slotID.getWorkflowID().toString()).toFile();
    }
    
    private String getSlotFileName(SlotID slotID) {
        return formatter.formatTimestamp(slotID.getScheduledTime());
    }

}

@@ -131,16 +131,17 @@
 ctNode;%0A
+%0A
 import o
@@ -288,19 +288,16 @@
 Database
-%0A *
  that st
@@ -305,16 +305,19 @@
 res slot
+%0A *
  states 
@@ -399,30 +399,88 @@
 tory
-.  Inside it
+, with one sub directory, called state.%0A * %0A * Inside the state directory
 , there
-%0A *
  is 
@@ -512,24 +512,26 @@
 orkflow.
- 
  Inside
+%0A *
  each wo
@@ -546,19 +546,16 @@
 irectory
-%0A *
  is one 
@@ -572,17 +572,16 @@
 per day.
- 
  Inside 
@@ -590,32 +590,35 @@
 ch day directory
+%0A *
  is one JSON fil
@@ -618,19 +618,16 @@
 SON file
-%0A *
  per wor
@@ -645,21 +645,16 @@
  * %0A * /
-%0A *  
  workflo
@@ -657,23 +657,16 @@
 kflow-1/
-%0A *    
  2013-12
@@ -665,33 +665,24 @@
  2013-12-02/
-%0A *      
  16:00:00.00
@@ -679,33 +679,24 @@
 6:00:00.000Z
-%0A *      
  17:00:00.00
@@ -697,32 +697,20 @@
 :00.000Z
-%0A *     ...%0A *  
+ ...
  workflo
@@ -716,20 +716,16 @@
 ow-2/%0A *
-    
  2013-12
@@ -728,25 +728,16 @@
 3-12-02/
-%0A *      
  16:00:0
@@ -746,17 +746,8 @@
 000Z
-%0A *      
  17:
@@ -760,32 +760,20 @@
 000Z
-%0A *     ...%0A *  
+ ...
  ...%0A * 
   %0A 
@@ -768,18 +768,16 @@
  ...%0A * 
-  
 %0A * A JS
@@ -1027,16 +1027,74 @@
 %22UTF-8%22;
+%0A    private static final String STATE_DIR_NAME = %22state%22;
 %0A%0A    pr
@@ -1253,17 +1253,58 @@
 al File 
-d
+toplevelDir;%0A    private final File stateD
 ir;%0A    
@@ -1482,17 +1482,25 @@
    this.
-d
+toplevelD
 ir = Uti
@@ -1535,17 +1535,25 @@
    if (!
-d
+toplevelD
 ir.exist
@@ -1617,17 +1617,25 @@
 ory %22 + 
-d
+toplevelD
 ir + %22 d
@@ -1653,32 +1653,82 @@
 t.%22);%0A        %7D%0A
+        stateDir = new File(dir, STATE_DIR_NAME);%0A
     %7D%0A%0A    @Over
@@ -2953,46 +2953,27 @@
 urn 
-dir.toPath().resolv
+new Fil
 e(
-%22
 state
-%22).resolve(
+Dir, 
 slot
@@ -3006,17 +3006,8 @@
 g())
-.toFile()
 ;%0A  

Refactor a bit