exports.addWorksheetToDB = function (userID, worksheetName, uuid, parent, callback) {
    var action = logging.action("dashboard.worksheet_add",
        {worksheetUUID: uuid, worksheetName: worksheetName, parent: parent, userID: userID});
    db.connectAndQuery(
        "INSERT INTO worksheets " +
            "(name, owner, parent, last_edited, document_ref, deleted) VALUES ($1, $2, $3, NOW(), $4, FALSE)",
        [worksheetName, userID, parent, uuid],
        function (err) {
            if (err) return logging.handleErrorWithAction(err, action, callback);
            db.connectAndQuery(
                "SELECT id FROM worksheets WHERE document_ref = $1",
                [uuid],
                function (err, result) {
                    if (err) return logging.handleErrorWithAction(err, action, callback);
                    if (result.rowCount !== 1) {
                        action.error("unable_to_select");
                        return callback("Error selecting worksheet from database.");
                    }
                    var newID = result.rows[0].id;
                    action.finish({worksheetID: newID});
                    callback(null, newID);
                }
            )
        }
    );
};
exports.authorize = function (token, uuid, callback) {
    postToEditServer(
        editServerURL + 'authorize/' + encodeURIComponent(uuid) + '/' + encodeURIComponent(token),
        callback,
        logging.action("dashboard.edit_server_authorize", {worksheetUUID: uuid, authToken: token})
    );
};
exports.delete = function (uuid, callback) {
    postToEditServer(
        editServerURL + 'worksheets/delete/' + encodeURIComponent(uuid),
        callback,
        logging.action("dashboard.edit_server_delete", {worksheetUUID: uuid})
    );
};
 editServer.authorize(token, documentUUID, function (err) {
     if (err) return logging.handleErrorWithAction(err, action, callback);
     worksheets.updateLastEditedDate(response.locals.worksheet.id, function (err) {
         if (err) return logging.handleErrorWithAction(err, action, next);
         action.finish();
         response.json({status: 'ok', uuid: documentUUID, token: token});
     });
 });
 worksheets.create(name, user, function (err, newID) {
     if (err) return logging.handleErrorWithAction(err, action, next);
     worksheets.loadWorksheet(newID, function (err, worksheet) {
         if (err) return logging.handleErrorWithAction(err, action, next);
         action.finish();
         response.json({worksheet: worksheet});
     })
 });
 editServer.create(documentUUID, function (err) {
     if (err) return logging.handleErrorWithAction(err, action, callback);
     exports.addWorksheetToDB(user.id, name, documentUUID, null, function (err, newID) {
         if (err) return logging.handleErrorWithAction(err, action, callback);
         action.finish();
         callback(null, newID);
     });
 });
 function (err, result) {
     if (err) return logging.handleErrorWithAction(err, action, callback);
     var worksheets = [];
     result.rows.map(function (ws) {
         worksheets.push(mapWorksheet(ws))
     });
     action.finish();
     callback(null, worksheets);
 });
 exports.loadWorksheetFromDocumentRef(oldUUID, function (err, parentWorksheet) {
     if (err) return logging.handleErrorWithAction(err, action, callback);
     // then we add the new worksheet, with the old worksheet as parent, and no owner.
     exports.addWorksheetToDB(null, 'Copy of ' + parentWorksheet.name, newUUID, parentWorksheet.id, function (err) {
         if (err) return logging.handleErrorWithAction(err, action, callback);
         action.finish();
         callback(err);
     });
 })
 function (err, result) {
     if (err) return logging.handleErrorWithAction(err, action, callback);
     if (result.rowCount !== 1) {
         action.error("not_claimable");
         return callback(new Error("Worksheet not claimable."));
     }
     action.finish();
     callback(null);
 });
 function (err, result) {
     if (err) return logging.handleErrorWithAction(err, action, callback);
     if (result.rowCount !== 1) {
         action.error("incorrect_row_count");
         return callback(new Error("Worksheet not found."));
     }
     action.finish();
     callback(null);
 });
exports.rename = function (request, response, next) {
    var worksheetID = request.params.id;
    var newName = request.params.newName;
    var action = logging.action("dashboard.worksheet_rename_mw", {worksheetID: worksheetID, newName: newName});
    worksheets.rename(worksheetID, newName, function (err) {
        if (err) return logging.handleErrorWithAction(err, action, next);
        action.finish();
        response.json({status: 'ok'});
    });
};
 function (err, result) {
     if (err) return logging.handleErrorWithAction(err, action, callback);
     if (result.rowCount !== 1) {
         action.error("unable_to_select");
         return callback("Error selecting worksheet from database.");
     }
     var newID = result.rows[0].id;
     action.finish({worksheetID: newID});
     callback(null, newID);
 }
exports.registerFork = function (request, response, next) {
    var oldUUID = request.params.oldUUID;
    var newUUID = request.params.newUUID;
    var action = logging.action("dashboard.worksheet_register_fork_mw", {worksheetUUID: oldUUID, newUUID: newUUID});
    worksheets.registerFork(oldUUID, newUUID, function (err) {
        if (err) return logging.handleErrorWithAction(err, action, next);
        action.finish();
        response.json({status: 'ok'});
    });
};
exports.loadWorksheet = function (request, response, next) {
    var worksheetID = request.params.id;
    var action = logging.action("dashboard.worksheet_load_mw", {worksheetID: worksheetID});
    worksheets.loadWorksheet(worksheetID, function (err, worksheet) {
        if (err) return logging.handleErrorWithAction(err, action, next);
        response.locals.worksheet = worksheet;
        action.finish();
        next();
    });
};
exports.loadAllWorksheets = function (request, response, next) {
    var ownerID = request.session.userID;
    var action = logging.action("dashboard.worksheet_load_all_mw", {ownerID: ownerID});
    worksheets.loadAllWorksheets(ownerID, function (err, worksheets) {
        if (err) return logging.handleErrorWithAction(err, action, next);
        response.locals.worksheets = worksheets;
        action.finish();
        next();
    })
};
exports.delete = function (request, response, next) {
    var worksheetID = request.params.id;
    var documentUUID = response.locals.worksheet.documentRef;
    var action = logging.action("dashboard.worksheet_delete_mw", {worksheetID: worksheetID, worksheetUUID: documentUUID});
    worksheets.delete(worksheetID, documentUUID, function (err) {
        if (err) return logging.handleErrorWithAction(err, action, next);
        action.finish();
        response.json({status: 'ok'});
    });
};
 worksheets.isWorksheetOwner(userID, worksheetID, function (err, result) {
     if (err) return logging.handleErrorWithAction(err, action, next);
     if (result) {
         action.finish();
         return next();
     }
     else {
         action.warn("not_owner");
         return next(new Error("You are not the worksheet owner."));
     }
 });
exports.requireEditServer = function (request, response, next) {
    var action = logging.action("dashboard.require_edit_server");
    if (request.body.secret !== sharedSecret) {
        action.error("not_authorized");
        response.json({status: "You are not an authorized admin app."});
    }
    else {
        action.finish();
        next();
    }
};
exports.rename = function (worksheetID, newName, callback) {
    var action = logging.action("dashboard.worksheet_rename", {worksheetID: worksheetID, newName: newName});
    db.connectAndQuery(
        "UPDATE worksheets SET name = $1 WHERE id = $2",
        [newName, worksheetID],
        function (err) {
            if (err) return logging.handleErrorWithAction(err, action, callback);
            action.finish();
            callback();
        }
    );
};
exports.create = function (name, user, callback) {
    var documentUUID = uuid.v4();
    var action = logging.action("dashboard.worksheet_create", {worksheetUUID: documentUUID, userID: user.id});
    editServer.create(documentUUID, function (err) {
        if (err) return logging.handleErrorWithAction(err, action, callback);
        exports.addWorksheetToDB(user.id, name, documentUUID, null, function (err, newID) {
            if (err) return logging.handleErrorWithAction(err, action, callback);
            action.finish();
            callback(null, newID);
        });
    });
};
 editServer.delete(documentUUID, function (err) {
     if (err) return logging.handleErrorWithAction(err, action, callback);
     db.connectAndQuery(
         "UPDATE worksheets SET deleted = TRUE WHERE id = $1",
         [worksheetID],
         function (err) {
             if (err) return logging.handleErrorWithAction(err, action, callback);
             action.finish();
             callback();
         }
     );
 });
exports.create = function (request, response, next) {
    var name = request.params.name;
    var user = response.locals.user;
    var action = logging.action("dashboard.worksheet_create_mw", {userID: user.id, worksheetName: '"' + name + '"'});
    worksheets.create(name, user, function (err, newID) {
        if (err) return logging.handleErrorWithAction(err, action, next);
        worksheets.loadWorksheet(newID, function (err, worksheet) {
            if (err) return logging.handleErrorWithAction(err, action, next);
            action.finish();
            response.json({worksheet: worksheet});
        })
    });
};
exports.registerFork = function (oldUUID, newUUID, callback) {
    var action = logging.action('dashboard.worksheet_register_fork', {worksheetUUID: oldUUID, newUUID: newUUID});
    // first we get the worksheet ID of the old worksheet
    exports.loadWorksheetFromDocumentRef(oldUUID, function (err, parentWorksheet) {
        if (err) return logging.handleErrorWithAction(err, action, callback);
        // then we add the new worksheet, with the old worksheet as parent, and no owner.
        exports.addWorksheetToDB(null, 'Copy of ' + parentWorksheet.name, newUUID, parentWorksheet.id, function (err) {
            if (err) return logging.handleErrorWithAction(err, action, callback);
            action.finish();
            callback(err);
        });
    })
};
 function (err, response, rawBody) {
     if (err) return logging.handleErrorWithAction(err, action, callback);
     var body = JSON.parse(rawBody);
     if (!body || !body.status) {
         action.error("no_response");
         return callback(new Error("No meaningful response from edit-server."));
     }
     if (body.status !== "ok") {
         action.error("bad_status", {status: body.status});
         return callback(new Error(body.status));
     }
     action.finish();
     callback(null, body.thumbnail);
 });
exports.delete = function (worksheetID, documentUUID, callback) {
    var action = logging.action("dashboard.worksheet_delete", {worksheetID: worksheetID, worksheetUUID: documentUUID});
    editServer.delete(documentUUID, function (err) {
        if (err) return logging.handleErrorWithAction(err, action, callback);
        db.connectAndQuery(
            "UPDATE worksheets SET deleted = TRUE WHERE id = $1",
            [worksheetID],
            function (err) {
                if (err) return logging.handleErrorWithAction(err, action, callback);
                action.finish();
                callback();
            }
        );
    });
};
exports.authorizeEdit = function (request, response, next) {
    // we generate an authorization token, send an authorization message to the edit-server,
    // and then return the token to the client.
    var token = uuid.v4();
    var documentUUID = response.locals.worksheet.documentRef;
    var action = logging.action("dashboard.worksheet_authorize_edit_mw", {worksheetUUID: documentUUID, authToken: token});
    editServer.authorize(token, documentUUID, function (err) {
        if (err) return logging.handleErrorWithAction(err, action, callback);
        worksheets.updateLastEditedDate(response.locals.worksheet.id, function (err) {
            if (err) return logging.handleErrorWithAction(err, action, next);
            action.finish();
            response.json({status: 'ok', uuid: documentUUID, token: token});
        });
    });
};
exports.claim = function (userID, uuid, callback) {
    var action = logging.action('dashboard.worksheet_claim', {worksheetUUID: uuid, userID: userID});
    db.connectAndQuery(
        "UPDATE worksheets SET owner = $1 WHERE document_ref = $2 AND owner IS NULL AND deleted = FALSE",
        [userID, uuid],
        function (err, result) {
            if (err) return logging.handleErrorWithAction(err, action, callback);
            if (result.rowCount !== 1) {
                action.error("not_claimable");
                return callback(new Error("Worksheet not claimable."));
            }
            action.finish();
            callback(null);
        });
};
exports.loadAllWorksheets = function (userID, callback) {
    var action = logging.action("dashboard.worksheet_load_all", {userID: userID});
    db.connectAndQuery(
        "SELECT * FROM worksheets WHERE owner = $1 AND deleted = FALSE ORDER BY last_edited DESC",
        [userID],
        function (err, result) {
            if (err) return logging.handleErrorWithAction(err, action, callback);
            var worksheets = [];
            result.rows.map(function (ws) {
                worksheets.push(mapWorksheet(ws))
            });
            action.finish();
            callback(null, worksheets);
        });
};
exports.loadWorksheetFromDocumentRef = function (documentRef, callback) {
    var action = logging.action("dashboard.worksheet_load_from_uuid", {worksheetUUID: documentRef});
    db.connectAndQuery(
        "SELECT * FROM worksheets WHERE document_ref = $1 AND deleted = FALSE",
        [documentRef],
        function (err, result) {
            if (err) return logging.handleErrorWithAction(err, action, callback);
            if (result.rowCount !== 1) {
                action.error("incorrect_row_count");
                return callback(new Error("Worksheet not found."));
            }
            action.finish();
            callback(null, mapWorksheet(result.rows[0]));
        });
};
exports.updateLastEditedDate = function (worksheetID, callback) {
    var action = logging.action("dashboard.worksheet_update_edit_date", {worksheetID: worksheetID});
    db.connectAndQuery(
        "UPDATE worksheets SET last_edited = NOW() WHERE id = $1 AND deleted = FALSE",
        [worksheetID],
        function (err, result) {
            if (err) return logging.handleErrorWithAction(err, action, callback);
            if (result.rowCount !== 1) {
                action.error("incorrect_row_count");
                return callback(new Error("Worksheet not found."));
            }
            action.finish();
            callback(null);
        });
};