/*
 * Copyright (c) 2012 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.
 */

/*jslint devel: true*/
/*global $, tizen, app */

/**
 * @class Model
 */
function Model() {
    'use strict';

    // unified addressbook object
    this.addressBook = tizen.contact.getUnifiedAddressBook();
    // sms service object
    this.smsService = null;
    // phone number attribute filter object
    this.phoneNumbersFilter = new tizen.AttributeFilter(
        'phoneNumbers.number',
        'EXISTS'
    );
    // message list object
    this.messagesList = {};
    // storega for loaded contacts
    this.contactsLoaded = null;
}

(function strict() {
    'use strict';
    Model.prototype = {

        /**
         * Dictionary of folders (by type).
         * @type {object}
         */
        messageFolders: {},

        /**
         * Initializes model object.
         */
        init: function Model_init() {
            this.loadContacts();
            this.initSmsService();
        },

        /**
         * Initializes sms service.
         */
        initSmsService: function Model_initSmsService() {
            var self = this,
                filter = null;

            try {
                tizen.messaging.getMessageServices(
                    'messaging.sms',
                    function onMessageServicesFound(s) {
                        self.smsService = s[0];
                        filter = new tizen.AttributeFilter(
                            'serviceId',
                            'EXACTLY',
                            self.smsService.id
                        );
                        self.messagesChangeListener();
                        self.smsService.messageStorage.findFolders(
                            filter,
                            function onFindFolderSuccess(folders) {
                                var folder = null,
                                    i = 0,
                                    length = folders.length;

                                for (i = 0; i < length; i += 1) {
                                    folder = folders[i];
                                    self.messageFolders[folder.type] =
                                        folder.id;
                                }
                                self.prepareMessages(
                                    app.fillUpMessagePage.bind(app)
                                );
                            }
                        );
                    }
                );
            } catch (error) {
                console.error(error.message);
            }
        },

        /**
         * Handles message change events.
         */
        messagesChangeListener: function Model_initSmsService() {
            var self = this,
                messageChangeCallback = {
                    messagesupdated: function onMessagesUpdated(messages) {
                        if (messages[0].messageStatus !== 'SENDING') {
                            app.ui.changeMessageStatus(messages[0]);
                        }
                    },
                    messagesadded: function onMessagesAdded() {
                        self.prepareMessages(app.ui.showMessageChat);
                    },
                    messagesremoved: function onMessagesRemoved() {
                        self.prepareMessages(app.ui.showMessageChat);
                    }
                };
            this.smsService.messageStorage.addMessagesChangeListener(
                messageChangeCallback
            );
        },

        /**
         * Finds messages and sorts them.
         * @param {function} callback
         */
        prepareMessages: function Model_prepareMessages(callback) {
            var self = this;
            try {
                this.smsService.messageStorage.findMessages(
                    new tizen.AttributeFilter(
                        'type',
                        'EXACTLY',
                        'messaging.sms'
                    ),
                    function onMessagesLoaded(messages) {
                        function compare(a, b) {
                            if (a.timestamp > b.timestamp) {
                                return -1;
                            }
                            if (a.timestamp < b.timestamp) {
                                return 1;
                            }
                            return 0;
                        }
                        messages.sort(compare);
                        self.messagesList = self.groupMessages(messages);
                        app.ui.loadCallerList();
                        callback();
                    },
                    function onError() {
                        console.error('prepareMessage: error');
                    }
                );
            } catch (err) {
                console.error(err);
            }
        },

        /**
         * Groups messages by recipients and data.
         * @param {object} messages Sms messages list object.
         * @return {object} Message list object.
         */
        groupMessages: function Model_groupMessages(messages) {
            var i, obj = {}, folderId;
            for (i in messages) {
                if (messages.hasOwnProperty(i)) {
                    folderId = messages[i].folderId;
                    if ((folderId !== null &&
                            this.messageFolders.hasOwnProperty('DRAFTS') &&
                            folderId !== this.messageFolders.DRAFTS) ||
                            messages[i].messageStatus === 'SENDING') {
                        if (messages.hasOwnProperty(i)) {
                            obj = this.groupMessagesSingle(messages[i], obj);
                        }
                    }
                }
            }
            return obj;
        },

        /**
         * Groups message by recipients.
         * @param {string} message Sms message object.
         * @param {object} obj Message list object.
         * @return {object} Updated message list object.
         */
        groupMessagesSingle: function Model_groupMessagesSingle(message, obj) {
            var key, j;
            if (message.from) {
                key = message.from;
                obj[key] = this.pushData(message, obj[key]);
            } else {
                for (j in message.to) {
                    if (message.to.hasOwnProperty(j)) {
                        key = message.to[j];
                        obj[key] = this.pushData(message, obj[key]);
                    }
                }
            }
            return obj;
        },

        /**
         * Collects message data.
         * @param {string} message Sms message object.
         * @param {object} obj Message list object.
         * @return {object} Updated obj object.
         */
        pushData: function Model_pushData(message, obj) {
            obj = obj || this.getGroupObject();
            obj.messages.push(message);
            if (app.helpers.objectLength(obj.lastMessage) === 0) {
                obj.lastMessage = message;
            }
            return obj;
        },

        /**
         * Creates empty message group object.
         * @return {object} Message group object.
         */
        getGroupObject: function Model_getGroupObject() {
            return {
                messages: [],
                lastMessage: {},
                last: {
                    body: {
                        plainBody: null
                    },
                    timestamp: new Date()
                }
            };
        },

        /**
         * Returns message list object.
         * @return {object} Message list object.
         */
        getMessages: function Model_getMessages() {
            return this.messagesList;
        },

        /**
         * Returns contact name to given phone number.
         * @param {string} number Phone number.
         * @return {string} Name or phone number.
         */
        getNameByNumber: function Model_getNameByNumber(number) {
            var i, j, contact, name;
            for (i in this.contactsLoaded) {
                if (this.contactsLoaded.hasOwnProperty(i)) {
                    contact = this.contactsLoaded[i];
                    for (j in contact.phoneNumbers) {
                        if (contact.phoneNumbers.hasOwnProperty(j) &&
                                contact.phoneNumbers[j].number === number) {
                            name = contact.name.displayName;
                            break;
                        }
                    }
                }
            }
            return name || number;
        },

        /**
         * Returns given text encoding.
         * @param {string} text
         * @return {string} Encoding identifier.
         */
        getTextEncoding: function Model_getTextEncoding(text) {
            if (app.gsmEncoding.test(text)) {
                return 'gsm';
            }
            return 'utf';
        },

        /**
         * Returns sms text length in an given encoding.
         * @param {string} text
         * @param {string} encoding Encoding identifier.
         * @return {number} Number of sms characters.
         */
        getTextLength: function Model_getTextLength(text, encoding) {
            if (encoding === 'gsm') {
                return text.replace(app.doubleChars, '__').length;
            }
            return text.length;
        },

        /**
         * Removes given message from current service.
         * @param {object} message Message object.
         * @param {function} callback Function called on success.
         */
        removeMessage: function Model_removeMessage(message, callback) {
            this.smsService.messageStorage
                .removeMessages([message], callback, function onError(e) {
                    console.error(e);
                });
        },

        /**
         * Removes list element and moves message text to input.
         * @param {object} message Sms message object.
         * @param {jQuery} li List DOM element.
         */
        editDraft: function Model_editDraft(message, li) {
            this.removeMessage(message, function onMessageRemoved() {
                $('#text').val(message.body.plainBody).trigger('input');
                li.remove();
            });
        },

        /**
         * Marks message as readed.
         * @param {object} message Sms message object.
         */
        setRead: function Model_setRead(message) {
            message.isRead = true;
            this.smsService.messageStorage.updateMessages([message]);
        },

        /**
         * Sends given text as message to given number.
         * @param {string[]} numbers Array of message recipients.
         * @param {string} text
         * @param {function} onSuc Called on success.
         * @param {function} onErr Called on fail.
         */
        sendMessage: function Model_sendMessage(numbers, text, onSuc, onErr) {
            var message;
            onSuc = onSuc || function noop() { return; };
            onErr = onErr || function noop() { return; };
            message = new tizen.Message(
                'messaging.sms',
                {plainBody: text, to: numbers}
            );
            try {
                this.smsService.sendMessage(
                    message,
                    onSuc,
                    function onError(e) {
                        onSuc();
                        console.error(
                            'Could not send the message. Error: ' +
                                e.name + ': ' +
                                e.message,
                            e
                        );
                        onErr(e);
                    }
                );
            } catch (e) {
                onSuc();
                console.error(
                    'Could not send the message. Exception: ' +
                        e.name + ':' +
                        e.message,
                    e
                );
                onErr(e);
            }
        },

        /**
         * Loads contacts to contactsLoaded array.
         * @param {function} callback Called on success.
         */
        loadContacts: function Model_loadContacts(callback) {
            var contactsFoundCB, errorCB;

            this.contactsLoaded = null;

            contactsFoundCB = function onContactsFound(contacts) {
                var i;
                this.contactsLoaded = [];
                for (i in contacts) {
                    if (
                        contacts.hasOwnProperty(i) &&
                            this.getNumberFromContact(contacts[i])
                    ) {
                        contacts[i].primaryNumber =
                            this.getNumberFromContact(contacts[i]);
                        this.contactsLoaded.push(contacts[i]);
                    }
                }
                if (callback instanceof Function) {
                    callback();
                }
            };

            errorCB = function onError(error) {
                console.error(
                    'Model_loadContacts, problem with find() method: ' +
                        error.message
                );
            };

            this.addressBook.find(contactsFoundCB.bind(this), errorCB);
        },

        /**
         * Returns contact phone number.
         * @param {object} contact Contact object.
         * @return {string || boolean}
         */
        getNumberFromContact: function Model_getNumberFromContact(contact) {
            if (!contact.phoneNumbers.length) {
                return false;
            }
            // removing formatting blank spaces and dashes
            return contact.phoneNumbers[0].number.replace(
                new RegExp('[^\\d]', 'g'),
                ''
            );
        }
    };
}());
