package se.dat255.grupp12;
import android.widget.Toast;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.sql.SQLOutput;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Timer;
import java.util.TimerTask;
import retrofit.Callback;
import retrofit.RetrofitError;
import retrofit.client.Response;
/**
* Created by ville on 10/3/13.
*/
public class Modification {
private String action;
private Task task;
private TodoList list;
private long listID;
private long time;
private Change change;
private long id;
private static List<Modification> log = new LinkedList<Modification>();
private static List<Modification> logBackup = new LinkedList<Modification>();
private static long lastSync = 0;
private static boolean autoSyncEnabled = true;
final static private String ACTION_ADD = "ADDTASK";
final static private String ACTION_CHANGE = "CHANGETASK";
final static private String ACTION_REMOVE = "REMOVETASK";
final static private String ACTION_CHANGE_LIST = "CHANGELIST";
final static private String ACTION_ADDLIST = "ADDLIST";
final static private String ACTION_REMOVELIST = "REMOVELIST";
final static private String ACTION_CHANGE_LISTNAME = "CHANGELISTNAME";
/**
* A Modification which creates a list.
* @param list
*/
private Modification(TodoList list) {
this.action=ACTION_ADDLIST;
this.list=list;
this.time=System.currentTimeMillis() / 1000L;
this.listID=list.getId();
}
/**
* A Modification of an unsynced task.
* @param action
* @param task
* @param listID
* @param change
*/
private Modification(String action, Task task, long listID, Change change) {
this.action = action;
this.task = task;
this.listID = listID;
this.time = System.currentTimeMillis() / 1000L;
this.change = change;
this.id = task.getId();
}
/**
* A Modification of an unsynced task with unknown listID.
* @param action
* @param task
* @param change
*/
private Modification(String action, Task task, Change change) {
this(action,task,0,change);
}
/**
* A Modification for a task with an id.
* @param action
* @param id
* @param listID
* @param change
*/
private Modification(String action, long id, long listID, Change change) {
this.action = action;
this.time = System.currentTimeMillis() / 1000L;
this.change = change;
this.id = id;
this.listID = listID;
this.task = null;
}
public Modification() {} // For serialization.
public static void setAutosyncCallback(SyncCallback autosyncCallback) {
Modification.autosyncCallback = autosyncCallback;
}
public static class Change {
private String type;
private String from;
private String to;
final public static String TYPE_TITLE = "title";
final public static String TYPE_PRIO = "prio";
final public static String TYPE_ISDONE = "isdone";
final public static String TYPE_ASSIGNED = "isassigned";
final public static String TYPE_COLLABORATORADDED = "collaboratoradded";
final public static String TYPE_DEADLINE = "deadline";
final public static String TYPE_CONTENT = "content";
public Change(String type, String from, String to) {
this.type = type;
this.from = from;
this.to = to;
}
private Change(){} // For serialization.
public String getFrom() {
return from;
}
public String getTo() {
return to;
}
}
/**
* Log the addition of a new task
* @param task
*/
public static void logTaskAddition(Task task, long listID) {
// Only log task addition to synced lists.
if (listID >= 0) {
Modification mod = new Modification(ACTION_ADD,task,listID,null);
log(mod);
} else {
for (Modification existingMod : log) {
if (existingMod.action.equals(ACTION_ADDLIST) && existingMod.listID==listID) {
return;
}
}
// If we just synced, so that the list addition isnt in the log, log the addition
// and change the listID on sync.
Modification mod = new Modification(ACTION_ADD,task,listID,null);
log(mod);
}
}
public static void logTaskEdit(long id, long listID, Change change) {
// Dont log task edits for unsynced lists, they will send the entire list of tasks on next
// sync anyway. Also don't sync edits to unsynced tasks, as they will appear as created with
// the edited value.
if (listID >= 0 && id >= 0) {
Modification mod = new Modification(ACTION_CHANGE,id,listID,change);
log(mod);
}
}
private static Timer autoSyncer;
private static SyncCallback autosyncCallback;
private static void log(Modification mod) {
log.add(mod);
if(ServerConnection.isConnectedToServer() && autoSyncEnabled){
if (autoSyncer!=null) {
autoSyncer.cancel();
}
autoSyncer = new Timer();
autoSyncer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("thread synca()");
sync(autosyncCallback);
}
}, 3000);
}
}
public static void logTaskRemoval(long id, long listID) {
// Dont log task removals for unsynced lists, they will send the entire list of tasks on next
// sync anyway.
if (listID>=0) {
Modification mod = new Modification(ACTION_REMOVE,id,listID,null);
log(mod);
}
}
public static void logTaskRemoval(Task task, long listID) {
if (task.getId()<0) {
// The task has not been synced yet, the best thing we can do is to search through
// history, remove the addition of the task and pretend nothing ever happened at all.
ListIterator<Modification> i = log.listIterator();
while (i.hasNext()) {
Modification mod = i.next();
if (listID == mod.listID && mod.action.equals(ACTION_ADD) && mod.task.equals(task)) {
i.remove();
}
}
} else {
logTaskRemoval(task.getId(), listID);
}
}
public static void logTodoRemoval(TodoList todoList) {
Modification mod = new Modification(ACTION_REMOVELIST, todoList.getId(), todoList.getId(),
null);
log(mod);
}
public static void logTodoListNameChange(TodoList todolist, String name){
Change change = new Change(Change.TYPE_TITLE, todolist.getName(), name);
Modification mod = new Modification(ACTION_CHANGE_LIST, todolist.getId(), todolist.getId(), change);
log(mod);
}
public static void logListCreation(TodoList list) {
Modification mod = new Modification(list);
log(mod);
}
public static void logPersonAddedToList(Contact contact, long TodoListId){
Change change = new Change(Change.TYPE_COLLABORATORADDED, null, contact.getEmail());
Modification mod = new Modification(ACTION_CHANGE_LIST, TodoListId, TodoListId, change);
log(mod);
}
// Should be private in the future.
public static List<Modification> getLog() {
return log;
}
public interface SyncCallback {
public void hasSynced(boolean value);
}
public static void sync(final SyncCallback callback) {
ServerConnection.server().syncModifications(Modification.getLog(), new Callback<RemoteService.SyncResponse>() {
@Override
public void success(RemoteService.SyncResponse s, Response response) {
; //NOP
System.out.println(s);
for (Modification.Change change : s.idChanges) {
System.out.println(change.getFrom() + " to " + change.getTo());
}
System.out.println("SIZE: "+s.userLists.size());
System.out.println("SIGN: "+s.userLists);
for (TodoList list : s.userLists) {
System.out.println("LISTNAME: "+list.getName());
for (Task task : list.getTasks()) {
System.out.println("TASK: "+task.getTitle());
}
}
if (s.userLists.size()>0) {
long lastListID = TodoList.getCurrentList().getId();
long newListID = 0;
TodoList.setTodoLists(s.userLists);
if (lastListID!=0) {
if (lastListID<0) {
for (Modification.Change change : s.idChanges) {
if (Long.parseLong(change.getFrom())==lastListID) {
newListID = Long.parseLong(change.getTo());
// Fix listIDs in modification log
for (Modification mod : log) {
System.out.println("runaway task "+mod.task);
if (mod.listID==lastListID) {
mod.listID=newListID;
}
}
}
}
if (newListID==0) {
// Fallback to My Tasks.
TodoList.setCurrentList(TodoList.THE_ASSIGNED_LIST);
} else {
TodoList.setCurrentList(TodoList.getListWithID(newListID));
}
} else {
try {
TodoList.setCurrentList(
TodoList.getListWithID(lastListID)
);
} catch (IllegalArgumentException e) {
// Fallback to My Tasks.
TodoList.setCurrentList(TodoList.THE_ASSIGNED_LIST);
}
}
}
callback.hasSynced(true);
ServerConnection.setConnectedToServer(true);
} else {
// This user has no lists. Thus, we havent succeeded.
callback.hasSynced(false);
ServerConnection.setConnectedToServer(false);
}
}
@Override
public void failure(RetrofitError retrofitError) {
; //NOP
//System.out.println(retrofitError.getMessage());
System.out.println(retrofitError);
restoreHistory();
callback.hasSynced(false);
ServerConnection.setConnectedToServer(false);
}
});
resetHistory();
}
/**
* Clears the history log and resets the timestamp of the last sync.
*/
private static void resetHistory() {
logBackup = log;
log = new LinkedList<Modification>();
lastSync = System.currentTimeMillis() / 1000L;
}
/**
* Restores the history backup. For use when sync goes wrong.
*/
private static void restoreHistory() {
log = logBackup;
}
/**
* Returns the modification as JSON, as a workaround for the retrofit json parser
* being quite funky in its serializing.
* @return the modification in JSON format.
*/
@Override
public String toString() {
Gson gson = new Gson();
return gson.toJson(this,Modification.class);
}
public static void setLog(List<Modification> list){
log = list;
}
public static void toggleAutoSync(boolean enabled) {
autoSyncEnabled = enabled;
}
}