define([
    "jquery",
    "underscore",
    "constants",
    "utils",
    "storage",
    "logger",
    "classes/Provider",
    "settings",
    "eventMgr",
    "fileMgr",
    "editor",
    "helpers/googleHelper",
    "text!html/dialogExportGdrive.html",
    "text!html/dialogAutoSyncGdrive.html",
], function($, _, constants, utils, storage, logger, Provider, settings, eventMgr, fileMgr, editor, googleHelper, dialogExportGdriveHTML, dialogAutoSyncGdriveHTML) {

    return function(providerId, providerName, accountIndex) {
        var accountId = 'google.gdrive' + accountIndex;

        var gdriveProvider = new Provider(providerId, providerName);
        gdriveProvider.defaultPublishFormat = "template";
        gdriveProvider.exportPreferencesInputIds = [
            providerId + "-parentid",
        ];

        function createSyncIndex(id) {
            return "sync." + providerId + "." + id;
        }

        var merge = settings.conflictMode == 'merge';
        function createSyncAttributes(id, etag, content, title, discussionListJSON) {
            discussionListJSON = discussionListJSON || '{}';
            var syncAttributes = {};
            syncAttributes.provider = gdriveProvider;
            syncAttributes.id = id;
            syncAttributes.etag = etag;
            syncAttributes.contentCRC = utils.crc32(content);
            syncAttributes.titleCRC = utils.crc32(title);
            syncAttributes.discussionListCRC = utils.crc32(discussionListJSON);
            syncAttributes.syncIndex = createSyncIndex(id);
            if(merge === true) {
                // Need to store the whole content for merge
                syncAttributes.content = content;
                syncAttributes.title = title;
                syncAttributes.discussionList = discussionListJSON;
            }
            return syncAttributes;
        }

        function importFilesFromIds(ids) {
            googleHelper.downloadMetadata(ids, accountId, function(error, result) {
                if(error) {
                    return;
                }
                googleHelper.downloadContent(result, accountId, function(error, result) {
                    if(error) {
                        return;
                    }
                    var fileDescList = [];
                    var fileDesc;
                    _.each(result, function(file) {
                        var parsedContent = gdriveProvider.parseContent(file.content);
                        var syncLocations;
                        if(file.isRealtime) {
                            eventMgr.onError('Real time synchronization is not supported anymore. Please use standard synchronization.');
                        }
                        else {
                            var syncAttributes = createSyncAttributes(file.id, file.etag, parsedContent.content, file.title, parsedContent.discussionListJSON);
                            syncLocations = {};
                            syncLocations[syncAttributes.syncIndex] = syncAttributes;
                        }
                        fileDesc = fileMgr.createFile(file.title, parsedContent.content, parsedContent.discussionListJSON, syncLocations);
                        fileDescList.push(fileDesc);
                    });
                    if(fileDesc !== undefined) {
                        eventMgr.onSyncImportSuccess(fileDescList, gdriveProvider);
                        fileMgr.selectFile(fileDesc);
                    }
                });
            });
        }

        gdriveProvider.importFiles = function() {
            googleHelper.picker(function(error, docs) {
                if(error || docs.length === 0) {
                    return;
                }
                var importIds = [];
                _.each(docs, function(doc) {
                    var syncIndex = createSyncIndex(doc.id);
                    var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
                    if(fileDesc !== undefined) {
                        return eventMgr.onError('"' + fileDesc.title + '" is already in your local documents.');
                    }
                    importIds.push(doc.id);
                });
                importFilesFromIds(importIds);
            }, 'doc', accountId);
        };

        gdriveProvider.exportFile = function(event, title, content, discussionListJSON, callback) {
            var fileId = utils.getInputTextValue('#input-sync-export-' + providerId + '-fileid');
            if(fileId) {
                // Check that file is not synchronized with another an existing
                // document
                var syncIndex = createSyncIndex(fileId);
                var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
                if(fileDesc !== undefined) {
                    eventMgr.onError('File ID is already synchronized with "' + fileDesc.title + '".');
                    return callback(true);
                }
            }
            var parentId = utils.getInputTextValue('#input-sync-export-' + providerId + '-parentid');
            var data = gdriveProvider.serializeContent(content, discussionListJSON);
            googleHelper.upload(fileId, parentId, title, data, undefined, undefined, accountId, function(error, result) {
                if(error) {
                    return callback(error);
                }
                var syncAttributes = createSyncAttributes(result.id, result.etag, content, title, discussionListJSON);
                callback(undefined, syncAttributes);
            });
        };

        gdriveProvider.syncUp = function(content, contentCRC, title, titleCRC, discussionList, discussionListCRC, syncAttributes, callback) {
            if(
                (syncAttributes.contentCRC == contentCRC) && // Content CRC hasn't changed
                (syncAttributes.titleCRC == titleCRC) && // Title CRC hasn't changed
                (syncAttributes.discussionListCRC == discussionListCRC) // Discussion list CRC hasn't changed
            ) {
                return callback(undefined, false);
            }

            if(syncAttributes.isRealtime) {
                var fileDesc = fileMgr.getFileFromSyncIndex(syncAttributes.syncIndex);
                fileDesc.removeSyncLocation(syncAttributes);
                eventMgr.onSyncRemoved(fileDesc, syncAttributes);
                return eventMgr.onError('Real time synchronization is not supported anymore. Please use standard synchronization.');
            }

            var data = gdriveProvider.serializeContent(content, discussionList);
            googleHelper.upload(syncAttributes.id, undefined, title, data, undefined, syncAttributes.etag, accountId, function(error, result) {
                if(error) {
                    return callback(error, true);
                }
                syncAttributes.etag = result.etag;
                // Remove this deprecated flag if any
                delete syncAttributes.isRealtime;
                if(merge === true) {
                    // Need to store the whole content for merge
                    syncAttributes.content = content;
                    syncAttributes.title = title;
                    syncAttributes.discussionList = discussionList;
                }
                syncAttributes.contentCRC = contentCRC;
                syncAttributes.titleCRC = titleCRC;
                syncAttributes.discussionListCRC = discussionListCRC;
                callback(undefined, true);
            });
        };

        gdriveProvider.syncDown = function(callback) {
            var lastChangeId = parseInt(storage[accountId + ".gdrive.lastChangeId"], 10);
            googleHelper.checkChanges(lastChangeId, accountId, function(error, changes, newChangeId) {
                if(error) {
                    return callback(error);
                }
                var interestingChanges = [];
                _.each(changes, function(change) {
                    var syncIndex = createSyncIndex(change.fileId);
                    var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
                    var syncAttributes = fileDesc && fileDesc.syncLocations[syncIndex];
                    if(!syncAttributes) {
                        return;
                    }
                    // Store fileDesc and syncAttributes references to avoid 2 times search
                    change.fileDesc = fileDesc;
                    change.syncAttributes = syncAttributes;
                    // Delete
                    if(change.deleted === true) {
                        interestingChanges.push(change);
                        return;
                    }
                    // Modify
                    if(syncAttributes.etag != change.file.etag) {
                        interestingChanges.push(change);
                    }
                });
                googleHelper.downloadContent(interestingChanges, accountId, function(error, changes) {
                    if(error) {
                        callback(error);
                        return;
                    }
                    function mergeChange() {
                        if(changes.length === 0) {
                            storage[accountId + ".gdrive.lastChangeId"] = newChangeId;
                            return callback();
                        }
                        var change = changes.pop();
                        var fileDesc = change.fileDesc;
                        var syncAttributes = change.syncAttributes;
                        // File deleted
                        if(change.deleted === true) {
                            eventMgr.onError('"' + fileDesc.title + '" has been removed from ' + providerName + '.');
                            fileDesc.removeSyncLocation(syncAttributes);
                            return eventMgr.onSyncRemoved(fileDesc, syncAttributes);
                        }
                        var file = change.file;
                        var parsedContent = gdriveProvider.parseContent(file.content);
                        var remoteContent = parsedContent.content;
                        var remoteTitle = file.title;
                        var remoteDiscussionListJSON = parsedContent.discussionListJSON;
                        var remoteDiscussionList = parsedContent.discussionList;
                        var remoteCRC = gdriveProvider.syncMerge(fileDesc, syncAttributes, remoteContent, remoteTitle, remoteDiscussionList, remoteDiscussionListJSON);

                        // Update syncAttributes
                        syncAttributes.etag = file.etag;
                        if(merge === true) {
                            // Need to store the whole content for merge
                            syncAttributes.content = remoteContent;
                            syncAttributes.title = remoteTitle;
                            syncAttributes.discussionList = remoteDiscussionListJSON;
                        }
                        syncAttributes.contentCRC = remoteCRC.contentCRC;
                        syncAttributes.titleCRC = remoteCRC.titleCRC;
                        syncAttributes.discussionListCRC = remoteCRC.discussionListCRC;
                        utils.storeAttributes(syncAttributes);
                        setTimeout(mergeChange, 5);
                    }
                    setTimeout(mergeChange, 5);
                });
            });
        };

        gdriveProvider.publish = function(publishAttributes, frontMatter, title, content, callback) {
            var contentType = publishAttributes.format != "markdown" ? 'text/html' : undefined;
            googleHelper.upload(publishAttributes.id, undefined, publishAttributes.fileName || title, content, contentType, undefined, accountId, function(error, result) {
                if(error) {
                    callback(error);
                    return;
                }
                publishAttributes.id = result.id;
                callback();
            });
        };

        gdriveProvider.newPublishAttributes = function(event) {
            var publishAttributes = {};
            publishAttributes.id = utils.getInputTextValue('#input-publish-' + providerId + '-fileid');
            publishAttributes.fileName = utils.getInputTextValue('#input-publish-' + providerId + '-filename');
            if(event.isPropagationStopped()) {
                return undefined;
            }
            return publishAttributes;
        };

        // Initialize the AutoSync dialog fields
        gdriveProvider.setAutosyncDialogConfig = function() {
            var config = gdriveProvider.autosyncConfig;
            utils.setInputRadio('radio-autosync-' + providerId + '-mode', config.mode || 'off');
            utils.setInputValue('#input-autosync-' + providerId + '-parentid', config.parentId);
        };

        // Retrieve the AutoSync dialog fields
        gdriveProvider.getAutosyncDialogConfig = function() {
            var config = {};
            config.mode = utils.getInputRadio('radio-autosync-' + providerId + '-mode');
            config.parentId = utils.getInputTextValue('#input-autosync-' + providerId + '-parentid');
            return config;
        };

        // Perform AutoSync
        gdriveProvider.autosyncFile = function(title, content, discussionListJSON, config, callback) {
            var parentId = config.parentId;
            googleHelper.upload(undefined, parentId, title, content, undefined, undefined, accountId, function(error, result) {
                if(error) {
                    callback(error);
                    return;
                }
                var syncAttributes = createSyncAttributes(result.id, result.etag, content, title, discussionListJSON);
                callback(undefined, syncAttributes);
            });
        };

        // Disable publish on optional multi-account
        gdriveProvider.isPublishEnabled = settings.gdriveMultiAccount > accountIndex;

        eventMgr.addListener("onReady", function() {
            // Hide optional multi-account sub-menus
            $('.submenu-sync-' + providerId).toggle(settings.gdriveMultiAccount > accountIndex);

            // Create export dialog
            var modalUploadElt = document.querySelector('.modal-upload-' + providerId);
            modalUploadElt && (modalUploadElt.innerHTML = _.template(dialogExportGdriveHTML, {
                providerId: providerId,
                providerName: providerName
            }));

            // Create autosync dialog
            var modalAutosyncElt = document.querySelector('.modal-autosync-' + providerId);
            modalAutosyncElt && (modalAutosyncElt.innerHTML = _.template(dialogAutoSyncGdriveHTML, {
                providerId: providerId,
                providerName: providerName
            }));

            // Choose folder button in export modal
            $('.action-export-' + providerId + '-choose-folder').click(function() {
                googleHelper.picker(function(error, docs) {
                    if(error || docs.length === 0) {
                        return;
                    }
                    // Open export dialog
                    $(".modal-upload-" + providerId).modal();
                    // Set parent ID
                    utils.setInputValue('#input-sync-export-' + providerId + '-parentid', docs[0].id);
                }, 'folder', accountId);
            });

            // Choose folder button in autosync modal
            $('.action-autosync-' + providerId + '-choose-folder').click(function() {
                googleHelper.picker(function(error, docs) {
                    if(error || docs.length === 0) {
                        return;
                    }
                    // Open export dialog
                    $(".modal-autosync-" + providerId).modal();
                    // Set parent ID
                    utils.setInputValue('#input-autosync-' + providerId + '-parentid', docs[0].id);
                }, 'folder', accountId);
            });

            $('.action-remove-google-drive-state').click(function() {
                storage.removeItem('gdrive.state');
            });

            // Skip gdrive action if provider is not enabled in the settings
            if(accountIndex >= settings.gdriveMultiAccount) {
                return;
            }
            var state = utils.retrieveIgnoreError('gdrive.state');
            var userId = storage[accountId + '.userId'];
            if(state === undefined) {
                return;
            }
            if(userId && state.userId != userId) {
                if(accountIndex === settings.gdriveMultiAccount - 1) {
                    if(settings.gdriveMultiAccount === 3) {
                        eventMgr.onError('None of your 3 Google Drive accounts is able to perform this request.');
                        storage.removeItem('gdrive.state');
                    }
                    else {
                        $(".modal-add-google-drive-account").modal();
                    }
                }
                return;
            }

            storage.removeItem('gdrive.state');
            if(state.action == "create") {
                googleHelper.upload(undefined, state.folderId, constants.GDRIVE_DEFAULT_FILE_TITLE, settings.defaultContent, undefined, undefined, accountId, function(error, file) {
                    if(error) {
                        return;
                    }
                    var syncAttributes = createSyncAttributes(file.id, file.etag, file.content, file.title);
                    var syncLocations = {};
                    syncLocations[syncAttributes.syncIndex] = syncAttributes;
                    var fileDesc = fileMgr.createFile(file.title, file.content, undefined, syncLocations);
                    fileMgr.selectFile(fileDesc);
                    eventMgr.onMessage('"' + file.title + '" created successfully on ' + providerName + '.');
                });
            }
            else if(state.action == "open") {
                var importIds = [];
                _.each(state.ids, function(id) {
                    var syncIndex = createSyncIndex(id);
                    var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
                    if(fileDesc !== undefined) {
                        fileDesc !== fileMgr.currentFile && fileMgr.selectFile(fileDesc);
                    }
                    else {
                        importIds.push(id);
                    }
                });
                importFilesFromIds(importIds);
            }
        });

        return gdriveProvider;
    };
});