package com.conveyal.taui.controllers;

import com.conveyal.osmlib.OSM;
import com.conveyal.r5.util.ExceptionUtils;
import com.conveyal.taui.AnalysisServerException;
import com.conveyal.taui.models.Region;
import com.conveyal.taui.persistence.OSMPersistence;
import com.conveyal.taui.persistence.Persistence;
import com.conveyal.taui.util.JsonUtil;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import spark.Request;
import spark.Response;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import static spark.Spark.delete;
import static spark.Spark.get;
import static spark.Spark.post;
import static spark.Spark.put;

/**
 * Created by matthewc on 7/12/16.
 */
public class RegionController {
    private static final Logger LOG = LoggerFactory.getLogger(RegionController.class);
    private static final FileItemFactory fileItemFactory = new DiskFileItemFactory();

    public static Region getRegion (Request req, Response res) {
        return Persistence.regions.findByIdFromRequestIfPermitted(req);
    }

    public static Collection<Region> getAllRegions (Request req, Response res) {
        return Persistence.regions.findAllForRequest(req);
    }

    public static Map<String, List<FileItem>> getFilesFromRequest (Request req) {
        try {
            ServletFileUpload sfu = new ServletFileUpload(fileItemFactory);
            return sfu.parseParameterMap(req.raw());
        } catch (FileUploadException e) {
            throw AnalysisServerException.fileUpload("Error uploading files. " + ExceptionUtils.asString(e));
        }
    }

    public static Region getRegionFromFiles(Map<String, List<FileItem>> files) {
        try {
            InputStream is = files.get("region").get(0).getInputStream();
            final Region region = JsonUtil.objectMapper.readValue(is, Region.class);
            is.close();

            return region;
        } catch (IOException e) {
            throw AnalysisServerException.badRequest("Error parsing region. " + ExceptionUtils.asString(e));
        }
    }

    public static OSM uploadOSM(Region region, Map<String, List<FileItem>> files) throws Exception {
        boolean hasCustomOSM = files.containsKey("customOpenStreetMapData");
        if (!hasCustomOSM) return null;

        // Set the status to Started
        region.statusCode = Region.StatusCode.STARTED;
        Persistence.regions.put(region);

        File customOsmData = File.createTempFile("uploaded-osm", ".pbf");
        files.get("customOpenStreetMapData").get(0).write(customOsmData);
        OSM osm = OSMPersistence.cache.put(region._id, customOsmData);
        customOsmData.delete();

        region.customOsm = true;
        region.statusCode = Region.StatusCode.DONE;
        Persistence.regions.put(region);

        return osm;
    }

    public static Region create(Request req, Response res) throws Exception {
        final Map<String, List<FileItem>> files = getFilesFromRequest(req);
        Region region = getRegionFromFiles(files);

        // Set the `accessGroup` and `createdBy`
        region.accessGroup = req.attribute("accessGroup");
        region.createdBy = req.attribute("email");

        // Upload custom OSM data
        uploadOSM(region, files);

        // Create the region
        Persistence.regions.create(region);

        return region;
    }

    public static Region update(Request req, Response res) throws Exception {
        final Region existingRegion = Persistence.regions.findByIdFromRequestIfPermitted(req);
        final Map<String, List<FileItem>> files = getFilesFromRequest(req);
        Region region = getRegionFromFiles(files);

        // Update custom OSM if exists
        uploadOSM(existingRegion, files);

        // Set updatedBy
        region.updatedBy = req.attribute("email");
        // And update the `nonce` and `updatedAt`
        Persistence.regions.put(region);

        return region;
    }

    public static Region deleteRegion (Request req, Response res) {
        return Persistence.regions.removeIfPermitted(req.params("_id"), req.attribute("accessGroup"));
    }

    public static void register () {
        get("/api/region", RegionController::getAllRegions, JsonUtil.objectMapper::writeValueAsString);
        get("/api/region/:_id", RegionController::getRegion, JsonUtil.objectMapper::writeValueAsString);
        get("/api/region/:region/projects", ProjectController::getAllProjects, JsonUtil.objectMapper::writeValueAsString);
        get("/api/region/:region/bookmark", BookmarkController::getAllBookmarks, JsonUtil.objectMapper::writeValueAsString);
        post("/api/region/:region/bookmark", BookmarkController::createBookmark, JsonUtil.objectMapper::writeValueAsString);
        post("/api/region", RegionController::create, JsonUtil.objectMapper::writeValueAsString);
        put("/api/region/:_id", RegionController::update, JsonUtil.objectMapper::writeValueAsString);
        delete("/api/region/:_id", RegionController::deleteRegion, JsonUtil.objectMapper::writeValueAsString);
    }
}

@@ -2430,19 +2430,20 @@
  static 
-OSM
+void
  uploadO
@@ -2526,118 +2526,18 @@
     
-boolean hasCustomOSM = files.containsKey(%22customOpenStreetMapData%22);%0A        if (!hasCustomOSM) return null;%0A%0A
+try %7B%0A    
     
@@ -2569,16 +2569,20 @@
 Started%0A
+    
         
@@ -2628,32 +2628,36 @@
 TARTED;%0A        
+    
 Persistence.regi
@@ -2666,32 +2666,36 @@
 s.put(region);%0A%0A
+    
         File cus
@@ -2760,16 +2760,20 @@
         
+  
+  
 files.ge
@@ -2842,17 +2842,11 @@
     
-OSM osm =
+   
  OSM
@@ -2903,16 +2903,20 @@
         
+ 
+   
 customOs
@@ -2932,32 +2932,36 @@
 ete();%0A%0A        
+  
+  
 region.customOsm
@@ -2969,32 +2969,36 @@
 = true;%0A        
+   
+ 
 region.statusCod
@@ -3025,32 +3025,36 @@
 e.DONE;%0A        
+    
 Persistence.regi
@@ -3083,19 +3083,199 @@
     
-return osm;
+%7D catch (Exception e) %7B%0A            Persistence.regions.remove(region);%0A            throw AnalysisServerException.fileUpload(%22Error processing OSM. %22 + ExceptionUtils.asString(e));%0A        %7D%0A
 %0A   
@@ -3659,136 +3659,136 @@
  // 
-Upload custom OSM data%0A        uploadOSM(region, files);%0A%0A        // Create the region%0A        Persistence.regions.create(region
+Create the region%0A        Persistence.regions.create(region);%0A%0A        // Upload custom OSM data%0A        uploadOSM(region, files
 );%0A%0A

update region after creating it