/*
 * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*global define, console, tizen*/

/**
 * Filesystem module.
 *
 * @requires {@link core/event}
 * @requires {@link core/filesystem}
 * @namespace ArchiveManager/model/filesystem
 * @memberof ArchiveManager/model
 */
define({
    name: 'model/filesystem',
    requires: [
        'core/event',
        'core/filesystem'
    ],
    def: function modelFilesystem(req) {
        'use strict';

        /**
         * Core event module.
         * @type {Module}
         */
        var e = req.core.event,

            /**
             * Core filesystem module.
             * @type {Module}
             */
            coreFilesystem = req.core.filesystem,

            /**
             * Accepted storage type.
             * @const {string}
             */
            STORAGE_TYPE = 'INTERNAL',

            /**
             * Regular expression recognizing storages names
             * which are not supported by application.
             * @const {RegExp}
             */
            STORAGES_EXCLUDED_REGEXP =
                new RegExp('^(internal|ringtones|wgt-).*');

        /**
         * Error callback.
         * @param {event} ev
         */
        function onError(ev) {
            console.error('error:' + ev.message);
        }

        /**
         * tizen.filesystem.listStorages API method success callback.
         * Prepares storages list and calls storages.loaded event.
         * @param {FileSystemStorage[]} storages
         */
        function onGetStoragesSuccess(storages) {
            var tmp = [],
                len = storages.length,
                i = 0;

            for (i; i < len; i += 1) {
                if (!STORAGES_EXCLUDED_REGEXP.test(storages[i].label)) {
                    if (storages[i].type === STORAGE_TYPE) {
                        tmp.push(storages[i]);
                    }
                }
            }

            e.fire('storages.loaded', {storages: tmp});
        }

        /**
         * Creates request for obtaining storages list.
         * @memberof ArchiveManager/model/filesystem
         */
        function getStorages() {
            try {
                tizen.filesystem.listStorages(onGetStoragesSuccess);
            } catch (ev) {
                onError(ev);
            }
        }

        /**
         * listFiles API method success callback.
         * Fires file.loaded event
         * @param {File[]} files
         */
        function onListFilesSuccess(files) {
            e.fire('files.loaded', {files: files});
        }

        /**
         * Creates request for obtaining files list.
         * @memberof ArchiveManager/model/filesystem
         * @param {string} path
         */
        function getFiles(path) {
            coreFilesystem.dir(path, onListFilesSuccess);
        }

        /**
         * Indicates that the size of the folder have been counted.
         * Fires foldersize.read event.
         * @param {Object} request
         */
        function onGetFolderResponse(request) {
            if (request.pending === 0) {
                e.fire('foldersize.read', request);
            }
        }

        /**
         * Counts number of data for each given directory.
         * Goes recursively down the directory tree.
         * Collects information about data size of files in request object.
         * @param {File} folder
         * @param {string} path
         * @param {Object} request
         */
        function getFolderSize(folder, path, request) {
            var len = 0,
                i = 0,
                file = null,
                name = '';

            request.pending += 1;

            folder.listFiles(function success(files) {
                request.pending -= 1;
                len = files.length;
                for (i = 0; i < len; i += 1) {
                    file = files[i];
                    name = file.path.replace(path + '/', '').split('/').shift();
                    if (file.isFile) {
                        if (request.sizes[name] === undefined) {
                            request.sizes[name] = 0;
                        }
                        request.sizes[name] += file.fileSize;
                    } else {
                        getFolderSize(file, path, request);
                    }
                }
                onGetFolderResponse(request);
            }, function error(ev) {
                request.pending -= 1;
                onGetFolderResponse(request);
                onError(ev);
            });
        }

        /**
         * Counts number of data of files for given files list.
         * @param {string} parentPath
         * @param {File[]} files List of files which data need to be counted.
         * @param {string[]} urlList.
         */
        function countData(parentPath, files, urlList) {
            var len = files.length,
                i = 0,
                file = null,
                name = '',
                request = {
                    sizes: {},
                    pending: 0
                };

            for (i = 0; i < len; i += 1) {
                file = files[i];
                name = file.name;
                if (urlList.indexOf(file.fullPath) !== -1) {
                    if (file.isFile) {
                        request.sizes[name] = file.fileSize;
                    } else {
                        request.sizes[name] = 0;
                        getFolderSize(file, parentPath, request);
                    }
                }
            }
            onGetFolderResponse(request);
        }

        /**
         * Counts number of data of files be archived.
         * @memberof ArchiveManager/model/filesystem
         * @param {string} parentPath
         * @param {File[]} files List of files to be archived.
         * @fires "foldersize.read"
         */
        function countNumberOfData(parentPath, files) {
            var len = files.length,
                i = 0,
                urlList = [];

            // Build a list of urls for filtering.
            for (i = 0; i < len; i += 1) {
                urlList.push(files[i].path);
            }

            coreFilesystem.dir(parentPath, function onDirSuccess(files) {
                countData(parentPath, files, urlList);
            });
        }

        /**
         * Finds collision free name for list of files names.
         * @param {string} name Proposed name.
         * @param {array} files List of files.
         */
        function resolveName(name, files) {
            var namesObject = {},
                length = files.length,
                newName = '',
                i = 0;

            for (i = 0; i < length; i += 1) {
                namesObject[files[i].name] = true;
            }

            i = 2;
            newName = name;

            while (namesObject.hasOwnProperty(newName)) {
                newName = name + ' (' + i + ')';
                i += 1;
            }

            e.fire('name.resolved', {name: newName});
        }

        /**
         * Finds collision free name for given path.
         * @memberof ArchiveManager/model/filesystem
         * @param {string} name Proposed name.
         * @param {string} path Destination path.
         * @fires "name.resolved"
         */
        function resolveNameForPath(name, path) {
            coreFilesystem.dir(path, function onDirSuccess(files) {
                resolveName(name, files);
            });
        }

        /**
         * Creates new empty directory in specified location.
         * @memberof ArchiveManager/model/filesystem
         * @param {string} name Dir name.
         * @param {string} path Destination path.
         * @fires "dir.created"
         */
        function createDir(name, path) {
            coreFilesystem.openDir(path, function onOpenDirSuccess(dir) {
                e.fire(
                    'dir.created',
                    {dir: coreFilesystem.createDir(dir, name)}
                );
            }, onError);
        }

        /**
         * Checks if there is a file with given name on given path.
         * @memberof ArchiveManager/model/filesystem
         * @param {string} path
         * @param {string} fileName
         * @fires "duplicate.detected"
         * @fires "duplicate.not.detected"
         */
        function checkDuplicates(path, fileName) {
            tizen.filesystem.resolve(
                path,
                function onResolveSuccess(dir) {
                    try {
                        dir.resolve(fileName);
                        e.fire('duplicate.detected');
                    } catch (error) {
                        e.fire('duplicate.not.detected');
                    }
                }
            );
        }

        return {
            getStorages: getStorages,
            getFiles: getFiles,
            countNumberOfData: countNumberOfData,
            resolveNameForPath: resolveNameForPath,
            createDir: createDir,
            checkDuplicates: checkDuplicates
        };
    }
});
