/*
 * Copyright (c) 2015 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, tizen, console*/

/**
 * Sound Profiles module.
 * @requires {@link core/event}
 * @requires {@link core/storage/localstorage}
 * @namespace SoundManager/models/soundProfiles
 * @memberof SoundManager/models
 */
define({
    name: 'models/soundProfiles',
    requires: [
        'core/event',
        'core/storage/localstorage'
    ],
    def: function soundProfiles(req) {
        'use strict';

        /**
         * Current sound profile.
         * @type {object}
         */
        var currentProfile = {},

            /**
             * User's profiles.
             *
             * A profile is an object composed of the following fields:
             * id {number} Identifier of the profile.
             * title {string} Name of the profile.
             * SYSTEM {float} Volume of system sound.
             * NOTIFICATION {float} Volume of notification sound.
             * ALARM {float} Volume of alarm sound.
             * MEDIA {float} Volume of media sound.
             * VOICE {float} Volume of voice sound.
             * RINGTONE {float} Volume of ringtone sound.
             *
             * @type {array}
             */
            profiles = [],

            /**
             * ID of active sound profile.
             * @type {number}
             */
            activeProfileId = null,

            /**
             * Event module instance.
             * @type {Module}
             */
            event = req.core.event,

            /**
             * LocalStorage module instance.
             * @type {Module}
             */
            storage = req.core.storage.localstorage,

            /**
             * Counter of volume changes that should not be handled.
             * @type {number}
             */
            ignoreHandlingVolumeChangeCounter = 0,

            /**
             * Sound types array.
             * @type {string[]}
             */
            SOUND_TYPES = [
                'SYSTEM',
                'NOTIFICATION',
                'ALARM',
                'MEDIA',
                'VOICE',
                'RINGTONE'
            ],

            /**
             * Name of storage holding profiles.
             * @type {string}
             */
            KEY = 'soundProfiles';

        /**
         * Returns the list of stored sound profiles.
         * @memberof SoundManager/models/soundProfiles
         * @returns {array}
         */
        function getProfiles() {
            return profiles;
        }

        /**
         * Handles volume change.
         * @param {string} soundType
         * @param {number} volume
         * @fires 'changed'
         */
        function onVolumeChanged(soundType, volume) {
            if (ignoreHandlingVolumeChangeCounter > 0) {
                ignoreHandlingVolumeChangeCounter -= 1;
                return;
            }
            if (currentProfile[soundType] !== volume) {
                currentProfile[soundType] = volume;
                if (activeProfileId !== null) {
                    activeProfileId = null;
                    event.fire('changed');
                }
            }
        }

        /**
         * Returns array of types of sound.
         * @memberof SoundManager/models/soundProfiles
         * @returns {string[]}
         */
        function getSoundTypes() {
            return SOUND_TYPES;
        }

        /**
         * Returns sound profile.
         * @memberof SoundManager/models/soundProfiles
         * @param {number} id Identifier of the profile.
         * @returns {object}
         */
        function getProfile(id) {
            var i = 0,
                profile = null;

            for (i = 0; i < profiles.length; i += 1) {
                profile = profiles[i];
                if (profile.id === id) {
                    return profile;
                }
            }

            return null;
        }

        /**
         * Returns sound profile with a given title.
         * @memberof SoundManager/models/soundProfiles
         * @param {string} title Profile's title.
         * @returns {object}
         */
        function getProfileByTitle(title) {
            var i = 0, profile = null;

            for (i = 0; i < profiles.length; i += 1) {
                profile = profiles[i];
                if (profile.title === title) {
                    return profile;
                }
            }

            return null;
        }

        /**
         * Writes profiles to the storage.
         * @returns {boolean} true if save finished successfully, false
         * otherwise.
         */
        function save() {
            return storage.set(KEY, {
                active: activeProfileId,
                profiles: profiles
            });
        }

        /**
         * Changes volume of system sound and increases the counter of ignored
         * volume changes, because the handler should react only on the changes
         * made from outside of the application.
         * @param {string} soundType
         * @param {number} volume
         */
        function changeSystemSoundVolume(soundType, volume) {
            try {
                currentProfile[soundType] = volume;
                ignoreHandlingVolumeChangeCounter += 1;
                tizen.sound.setVolume(soundType, volume);
            } catch (e) {
                console.error(
                    'Setting ' + soundType + ' error: ' + e.message
                );
            }
        }

        /**
         * Updates volume value in a given sound profile and writes changes to
         * local storage.
         * @memberof SoundManager/models/soundProfiles
         * @param {number} profileId Identifier of the profile.
         * @param {string} soundType Type of sound.
         * @param {number} volume Volume value.
         * @returns {boolean} true if update finished successfully, false
         * otherwise.
         */
        function updateProfile(profileId, soundType, volume) {
            var profile = getProfile(profileId);

            if (profile) {
                profile[soundType] = volume;
                if (profile.id === activeProfileId) {
                    changeSystemSoundVolume(soundType, volume);
                }
                return save();
            }
            return false;
        }

        /**
         * Creates new profile based on current system sound settings.
         * @memberof SoundManager/models/soundProfiles
         * @param {string} title Created profile's title.
         * @fires 'changed'
         * @returns {boolean} true if creation finished successfully, false
         * otherwise.
         */
        function createProfile(title) {
            var currentProfileCopy = JSON.parse(JSON.stringify(currentProfile)),
                id = profiles.length ? profiles[profiles.length - 1].id + 1 : 0;
            currentProfileCopy.title = title;
            currentProfileCopy.id = id;
            profiles.push(currentProfileCopy);
            if (save()) {
                event.fire('changed');
                return true;
            }
            return false;
        }

        /**
         * Returns index of a profile in the profiles array or -1 if the array
         * doesn't contain the profile with given ID.
         * @param {number} profileId profile's ID.
         * @returns {number}
         */
        function getProfileIndex(profileId) {
            var i = 0;

            for (i = 0; i < profiles.length; i += 1) {
                if (profiles[i].id === profileId) {
                    return i;
                }
            }

            return -1;
        }

        /**
         * Deletes profile.
         * @memberof SoundManager/models/soundProfiles
         * @param {number} id Profile's ID.
         * @fires 'changed'
         */
        function deleteProfile(id) {
            var index = getProfileIndex(id);
            if (index !== -1) {
                profiles.splice(index, 1);
            }
            if (save()) {
                event.fire('changed');
            }
        }

        /**
         * Updates current system sound settings with values from given profile.
         * @param {object} profile
         */
        function changeSystemProfile(profile) {
            var i = 0,
                soundType = null;

            for (i = 0; i < SOUND_TYPES.length; i += 1) {
                soundType = SOUND_TYPES[i];
                changeSystemSoundVolume(soundType, profile[soundType]);
            }
        }

        /**
         * Sets active profile.
         * @memberof SoundManager/models/soundProfiles
         * @param {number} id Profile's ID.
         * @fires 'changed'
         */
        function setActive(id) {
            if (activeProfileId !== id) {
                changeSystemProfile(getProfile(id));
                activeProfileId = id;
                if (save()) {
                    event.fire('changed');
                }
            }
        }

        /**
         * Returns an object containing stored profiles and id of the active
         * profile or an empty object if there are no  saved profiles in the
         * storage.
         * @returns {object}
         */
        function getProfilesFromStorage() {
            return storage.get(KEY) || {profiles: [], active: 0};
        }

        /**
         * Initializes module.
         * Loads profiles from local storage, reads current system sound
         * settings and registers volume change listener.
         * @memberof SoundManager/models/soundProfiles
         */
        function init() {
            var i = 0,
                soundType = null,
                storedData = getProfilesFromStorage();

            profiles = storedData.profiles;
            activeProfileId = parseInt(storedData.active, 10);

            for (i = 0; i < SOUND_TYPES.length; i += 1) {
                soundType = SOUND_TYPES[i];
                try {
                    currentProfile[soundType] =
                        tizen.sound.getVolume(soundType);
                } catch (e) {
                    console.error(e.message);
                }
            }

            try {
                tizen.sound.setVolumeChangeListener(onVolumeChanged);
            } catch (e) {
                console.error(e.message);
            }
        }

        /**
         * Returns ID of currently active profile.
         * @memberof SoundManager/models/soundProfiles
         * @returns {number}
         */
        function getActiveProfileId() {
            return activeProfileId;
        }

        return {
            init: init,
            getSoundTypes: getSoundTypes,
            getProfiles: getProfiles,
            getActiveProfileId: getActiveProfileId,
            getProfile: getProfile,
            getProfileByTitle: getProfileByTitle,
            updateProfile: updateProfile,
            createProfile: createProfile,
            deleteProfile: deleteProfile,
            setActive: setActive
        };
    }
});
