define(['cryptojs-md5'], function () {

    function loadImage(elem, url) {

        if (elem.tagName !== "IMG") {

            return new Promise(function (resolve, reject) {

                var tmp = new Image();

                tmp.onload = function () {
                    elem.style.backgroundImage = "url('" + url + "')";
                    resolve(elem);
                };
                tmp.src = url;
            });

        } else {
            elem.setAttribute("src", url);
            return Promise.resolve(elem);
        }
    }

    // Request Quota (only for File System API)  
    var requestedBytes = 1024 * 1024 * 1500;
    var imageCacheDirectoryEntry;
    var imageCacheFolder = 'images';

    function createDir(rootDirEntry, folders, callback, errorCallback) {
        // Throw out './' or '/' and move on to prevent something like '/foo/.//bar'.
        if (folders[0] == '.' || folders[0] == '') {
            folders = folders.slice(1);
        }
        rootDirEntry.getDirectory(folders[0], { create: true }, function (dirEntry) {
            // Recursively add the new subfolder (if we still have another to create).
            if (folders.length > 1) {
                createDir(dirEntry, folders.slice(1), callback, errorCallback);
            } else {
                callback(dirEntry);
            }
        }, errorCallback);
    }

    navigator.webkitPersistentStorage.requestQuota(
        requestedBytes, function (grantedBytes) {

            var requestMethod = window.webkitRequestFileSystem || window.requestFileSystem;

            requestMethod(PERSISTENT, grantedBytes, function (fs) {

                fileSystem = fs;

                createDir(fileSystem.root, imageCacheFolder.split('/'), function (dirEntry) {

                    imageCacheDirectoryEntry = dirEntry;

                    // TODO: find a better time to schedule this
                    setTimeout(cleanCache, 60000);
                });

            });

        });

    function toArray(list) {
        return Array.prototype.slice.call(list || [], 0);
    }

    function cleanCache() {

        var dirReader = imageCacheDirectoryEntry.createReader();
        var entries = [];

        var onReadFail = function () {
            console.log('dirReader.readEntries failed');
        };

        // Keep calling readEntries() until no more results are returned.
        var readEntries = function () {
            dirReader.readEntries(function (results) {
                if (!results.length) {
                    entries.forEach(cleanFile);
                } else {
                    entries = entries.concat(toArray(results));
                    readEntries();
                }
            }, onReadFail);
        };

        // Start reading the directory.
        readEntries();
    }

    function cleanFile(fileEntry) {
        if (!fileEntry.isFile) {
            return;
        }

        fileEntry.file(function (file) {

            getLastModified(file, fileEntry).then(function (lastModifiedDate) {

                var elapsed = new Date().getTime() - lastModifiedDate;
                // 40 days
                var maxElapsed = 3456000000;
                if (elapsed >= maxElapsed) {

                    var fullPath = fileEntry.fullPath;
                    console.log('deleting file: ' + fullPath);

                    fileEntry.remove(function () {
                        console.log('File deleted: ' + fullPath);
                    }, function () {
                        console.log('Failed to delete file: ' + fullPath);
                    });
                }
            });

        });
    }

    function getLastModified(file, fileEntry) {

        var lastModifiedDate = file.lastModified || file.lastModifiedDate || file.modificationTime;
        if (lastModifiedDate) {
            if (lastModifiedDate.getTime) {
                lastModifiedDate = lastModifiedDate.getTime();
            }
            return Promise.resolve(lastModifiedDate);
        }

        return new Promise(function (resolve, reject) {

            fileEntry.getMetadata(function (metadata) {
                var lastModifiedDate = metadata.lastModified || metadata.lastModifiedDate || metadata.modificationTime;
                if (lastModifiedDate) {
                    if (lastModifiedDate.getTime) {
                        lastModifiedDate = lastModifiedDate.getTime();
                    }
                }
                resolve(lastModifiedDate);
            });
        });
    }

    function getCacheKey(url) {

        // Try to strip off the domain to share the cache between local and remote connections
        var index = url.indexOf('://');

        if (index != -1) {
            url = url.substring(index + 3);

            index = url.indexOf('/');

            if (index != -1) {
                url = url.substring(index + 1);
            }

        }

        return CryptoJS.MD5(url).toString();
    }

    function downloadToFile(url, dir, filename, callback, errorCallback) {

        console.log('Downloading ' + url);

        var xhr = new XMLHttpRequest();
        xhr.open('GET', url, true);
        xhr.responseType = "arraybuffer";

        xhr.onload = function (e) {
            if (this.status == 200) {
                writeData(dir, filename, this.getResponseHeader('Content-Type'), this.response, callback, errorCallback);
            } else {
                errorCallback();
            }
        }

        xhr.send();
    }

    function writeData(dir, filename, fileType, data, callback, errorCallback) {

        dir.getFile(filename, { create: true }, function (fileEntry) {

            // Create a FileWriter object for our FileEntry (log.txt).
            fileEntry.createWriter(function (fileWriter) {

                fileWriter.onwriteend = function (e) {
                    callback(fileEntry);
                };

                fileWriter.onerror = errorCallback;

                // Create a new Blob and write it to log.txt.
                var blob = new Blob([data], { type: fileType });

                fileWriter.write(blob);

            }, errorCallback);

        }, errorCallback);
    }

    function getImageUrl(originalUrl) {

        return new Promise(function (resolve, reject) {

            if (originalUrl.indexOf('tag=') != -1) {
                originalUrl += "&accept=webp";
            }

            var key = getCacheKey(originalUrl);

            var fileEntryCallback = function (fileEntry) {
                resolve(fileEntry.toURL());
            };

            var errorCallback = function (e) {
                console.log('Imagestore error: ' + e.name);
                reject();
            };

            if (!fileSystem || !imageCacheDirectoryEntry) {
                errorCallback('');
                return;
            }

            var path = '/' + imageCacheFolder + "/" + key;

            fileSystem.root.getFile(path, { create: false }, fileEntryCallback, function () {

                downloadToFile(originalUrl, imageCacheDirectoryEntry, key, fileEntryCallback, errorCallback);
            });
        });
    }

    var fileSystem;

    return {
        loadImage: function (elem, url) {

            return getImageUrl(url).then(function (localUrl) {

                return loadImage(elem, localUrl);

            }, function () {
                return loadImage(elem, url);
            });
        }
    };

});