package hello;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;

import javax.servlet.http.HttpSession;

import org.apache.commons.logging.Log;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.fge.jsonpatch.JsonPatch;
import com.github.fge.jsonpatch.JsonPatchException;
import com.github.fge.jsonpatch.diff.JsonDiff;

@RestController
@RequestMapping("/todos")
public class TodoPatchController {

	private final ObjectMapper objectMapper = new ObjectMapper(); //.setSerializationInclusion(Include.NON_NULL);

	private TodoRepository todoRepository;

	@Autowired
	public TodoPatchController(TodoRepository todoRepository) {
		this.todoRepository = todoRepository;
	}
	
	
	// TODO: Consider if Spring Session is a suitable option here or if there should instead be some sort of ShadowStore implementation
	//       rather than relying on user-centric HttpSession.
	@RequestMapping(
			method=RequestMethod.PATCH, 
			consumes={"application/json", "application/json-patch+json"}, 
			produces={"application/json", "application/json-patch+json"})
	public ResponseEntity<JsonNode> patch(JsonPatch jsonPatch, HttpSession session) 
			throws JsonPatchException, IOException {
		
		// TODO: if jsonPatch is empty, don't bother applying it or saving the patched items
		
		// get shadow from session / calculate it if it isn't in session 
		JsonNode shadow = (JsonNode) session.getAttribute("/todos/shadow");

		Iterable<Todo> allTodos = getEntityList();
		JsonNode source = asJsonNode(allTodos);
		if (shadow == null) {
			shadow = source;
		}

		// apply patch to shadow
		shadow = jsonPatch.apply(shadow);

		// apply patch to source
		source = jsonPatch.apply(source);

		// TODO: This is very hacky...find better way that doesn't involve baking up a new array and
		//       saving *all* items at once
		ArrayList<Todo> patchedTodos = new ArrayList<Todo>();
		Iterator<JsonNode> elements = source.elements();
		while(elements.hasNext()) {
			JsonNode todoNode = elements.next();
			Todo todo = objectMapper.treeToValue(todoNode, Todo.class);
			patchedTodos.add(todo);
		}
		todoRepository.save(patchedTodos);
		
		// diff shadow against source to calcuate returnPatch
		JsonNode returnPatch = JsonDiff.asJson(shadow, source);
		
		// apply return patch to shadow
		shadow = JsonPatch.fromJson(returnPatch).apply(shadow);
		
		// update session with new shadow
		session.setAttribute("/todos/shadow", shadow);
		
		// return returnPatch
		return new ResponseEntity<JsonNode>(returnPatch, HttpStatus.OK);
	}
	
	
	protected Iterable<Todo> getEntityList() {
		return todoRepository.findAll();
	}
	
	private JsonNode asJsonNode(Object o) {
		return objectMapper.convertValue(o, JsonNode.class);
	}
}

@@ -82,24 +82,179 @@
 va.util.
-Iterator
+Arrays;%0Aimport java.util.Collection;%0Aimport java.util.Collections;%0Aimport java.util.HashSet;%0Aimport java.util.Iterator;%0Aimport java.util.List;%0Aimport java.util.Set
 ;%0A%0Aimpor
@@ -2437,16 +2437,43 @@
 at once%0A
+%09%09// BEGIN VERY HACKY CODE%0A
 %09%09ArrayL
@@ -2771,16 +2771,175 @@
 dTodos);
+%0A%0A%09%09List%3CTodo%3E allTodosList = (List%3CTodo%3E) allTodos;%0A%09%09allTodosList.removeAll(patchedTodos);%0A%09%09todoRepository.delete(allTodosList);%0A%09%09// END VERY HACKY CODE%0A%09%09
 %0A%09%09%0A%09%09//

Add hack to support deletes