class WsChatController < WsController

    require 'message_builder'

    def start_polling_messages
        connection_store[:clients].each do |client|
            client.add_message_callback do |message|
                #noinspection RubyAssignmentExpressionInConditionalInspection
                if message.body
                    process_incoming_message(message.from, message.to, message.body, message.attribute('chat_id').to_s)
                elsif request = message.first_element('sync_contacts_request')
                    # toto mozem prijat len ako admin multichatu
                    send_contacts(message.from, message.to, request.attribute('chat_id').to_s)
                elsif answer = message.first_element('synced_contacts')
                    # toto mozem prijat len ako ucastnik multichatu (nie admin)
                    contacts = xml_contacts_to_array(answer)
                    set_contacts_in_multichat(find_client(message.to.strip.to_s), answer.attribute('chat_id').to_s, message.from.to_s, contacts)
                    sync_contacts_frontend(message.from, message.to, answer.attribute('chat_id').to_s, contacts)
                elsif answer = message.first_element('exported_chat')
                    contacts = xml_contacts_to_array(message.first_element('exported_chat'))
                    import_people_in_chat(message.from, message.to, answer.attribute('chat_id').to_s, contacts)
                elsif message.attribute('destroy_multichat')
                    destroy_multichat(message.to, message.attribute('chat_id').to_s)
                elsif message.attribute('req_update_contacts')
                    added   = xml_contacts_to_array(message.first_element('added'))
                    removed = xml_contacts_to_array(message.first_element('removed'))
                    update_attendants_in_multichat(message.from, message.to, message.attribute('chat_id').to_s, removed, added)
                elsif message.attribute('new_owner')
                    make_me_new_owner(message.to, message.attribute('chat_id').to_s)
                end
                #TODO: upozornit na pisanie spravy
                #TODO: odoslat informaciu o tom, ze pisem spravu
                #else
                #    send_message 'app.chat.messageState',
                #                 state: message.chat_state,
                #                 from: message.from.strip.to_s,
                #                 to: message.to.strip.to_s,
                #                 message: message.body,
                #                 chat_id: if message.attribute(:is_simulating) then message.attribute(:chat_id) end
            end
        end
    end

    def make_me_new_owner(me, chat_id)
        client = find_client(me.strip.to_s)

        connection_store[:opened_chats][client][chat_id][:attendants] -= [me.to_s]
        connection_store[:opened_chats][client][chat_id][:attendants] += [connection_store[:opened_chats][client][chat_id][:owner]]

        connection_store[:opened_chats][client][chat_id][:owner] = me.to_s

        contacts = connection_store[:opened_chats][client][chat_id][:attendants]
        contacts.each do |contact|
            client.send(MessageBuilder::send_multichat_contacts(client.jid.to_s, contact, chat_id, contacts))
        end

        send_message 'app.chat.makeMeNewOwner',
                     me: me.strip.to_s,
                     chat_id: chat_id
    end

    def import_people_in_chat(owner, me, chat_id, contacts)
        #Rails.logger.debug ['imported chat arrived', message.to_s, chat_id]
        client = find_client(me.strip.to_s)
        set_contacts_in_multichat(client, chat_id, owner.to_s, contacts)

        send_message 'app.chat.importChat',
                     owner:    owner.strip.to_s,
                     chat_id:  chat_id,
                     contacts: strip_all(contacts)
    end

    # Owner vytvori najprv u seba novy multichat
    def new_multichat
        me = message[:chatOwner]
        hash = Digest::SHA2.hexdigest(me)
        chat_id = hash + Time.now.to_f.to_s
        client = find_client(me)

        set_contacts_in_multichat(client, chat_id, client.jid.to_s)

        trigger_success id: chat_id
    end

    # Owner posle novemu cloveku informaciu o chat_id a kontaktoch
    def add_to_multichat
        client = find_client(message[:chatOwner])

        chat_id  = message[:chatId]
        add      = message[:jid]
        add_resource = find_multichat_supported(add)

        if add_resource
            connection_store[:opened_chats][client][chat_id][:attendants] << add_resource
            #Rails.logger.debug ['adding to multichat', add_resource]

            contacts = connection_store[:opened_chats][client][chat_id][:attendants]

            contacts.each do |contact|
                client.send(MessageBuilder::export_multichat(client.jid.to_s, contact, chat_id, contacts))
            end

            trigger_success
        else
            #Rails.logger.debug ['adding to multichat failure', connection_store[:presences][add]]
            trigger_failure
        end
    end

    def send_chat_message
        me = message[:from]
        client = find_client(me)
        my_jid = client.jid.to_s

        if client
            chat_id = message[:chatId]
            if chat_id
                attendants = connection_store[:opened_chats][client][chat_id][:attendants] + [connection_store[:opened_chats][client][chat_id][:owner]]
                attendants -= [my_jid]

                messages = MessageBuilder::build_multi_messages(message[:message], my_jid, attendants, chat_id)
            else
                messages = [MessageBuilder::build_message(message[:message], my_jid, message[:to])]
            end

            # Xmpp4r doesn't support XEP-0033 (multicast messages)
            messages.each do |message|
                client.send(message)
            end

            stripped_me = client.jid.strip.to_s
            stripped_to = Jabber::JID.new(message[:to]).strip!.to_s
            if can_save_conversation?(stripped_me, stripped_to, chat_id)
                Rails.logger.debug ['saving sent message', stripped_me]
                save_encrypted_message(message[:message], stripped_me, stripped_me, stripped_to, chat_id)
            end

            trigger_success message[:message]
        else
            trigger_failure
        end
    end

    def request_sync_chat_contacts
        client = find_client(message[:me])
        chat_id = message[:chatId]

        owner = connection_store[:opened_chats][client][chat_id][:owner]
        client.send(MessageBuilder::ask_for_multichat_contacts(client.jid.strip.to_s, owner, chat_id))
    end

    def i_closed_multichat
        chat_id = message[:chatId]
        client  = find_client(message[:me])
        me      = client.jid.to_s
        owner   = connection_store[:opened_chats][client][chat_id][:owner]

        if owner == me
            attendants = connection_store[:opened_chats][client][chat_id][:attendants]
            attendants.each do |attendant|
                client.send(MessageBuilder::kick_from_multichat(me, attendant, chat_id))
            end
        else
            changes = {removed: [me]}
            client.send(MessageBuilder::req_update_multichat_contacts(me, owner, chat_id, changes))
        end

        connection_store[:opened_chats][client].delete(chat_id)
    end

    def kick_from_multichat
        chat_id = message[:chatId]
        client  = find_client(message[:me])
        kick_stripped = message[:kick]

        kick = find_full_jid_in_multichat(kick_stripped, client, chat_id)

        return if kick.nil?

        contacts = connection_store[:opened_chats][client][chat_id][:attendants]
        contacts -= [kick]
        connection_store[:opened_chats][client][chat_id][:attendants] = contacts

        client.send(MessageBuilder::kick_from_multichat(client.jid.to_s, kick, chat_id))

        if contacts.empty?
            connection_store[:opened_chats][client].delete(chat_id)
            send_message 'app.chat.destroyMultichat', chat_id: chat_id
        else
            contacts.each do |contact|
                client.send(MessageBuilder::send_multichat_contacts(client.jid.to_s, contact, chat_id, contacts))
            end
        end
    end

    def switch_ownership
        chat_id = message[:chatId]
        client  = find_client(message[:me])
        new_owner = message[:newOwner]

        new_owner_jid = find_full_jid_in_multichat(new_owner, client, chat_id)

        client.send(MessageBuilder::make_new_owner(client.jid.to_s, new_owner_jid, chat_id)) if new_owner_jid
    end

    def can_save_conversation?(stripped_me = nil, stripped_attendant = nil, chat_id = nil)
        me = message[:me] || stripped_me
        attendant = message[:attendant] || stripped_attendant
        _chat_id = message[:chatId] || chat_id

        result = User.can_save_conversation?(me, attendant, _chat_id)

        if message[:me]
            trigger_success result
        else
            result
        end
    end

    def set_history_saving
        save = message[:enable]
        me = message[:me]
        attendant = message[:attendant]
        chat_id = message[:chatId]

        trigger_failure unless find_client(me)

        save = save.is_a?(FalseClass) ? 0 : 1

        User.set_history_saving(save, me, attendant, chat_id) && trigger_success
    end

    def load_history
        me = message[:me]
        attendant = message[:attendant]
        chat_id = message[:chatId]
        page = message[:step]
        per_page = 10

        client = find_client(me)
        trigger_failure unless client

        history = History.page_history(me, attendant || chat_id, page, per_page)

        credentials = User.crendentials_for_token(session[:token])
        found_account = credentials.detect do |c| c['jid'] == client.jid.strip.to_s end
        user_pass = Security::decrypt(found_account['pass'], connection_store[:cipher_key], connection_store[:cipher_iv])

        history.each do |record|
            record['message'] = Security::decrypt(record['message'], Security::create_key_from_pass(user_pass), Rails.application.config.aes_iv)
        end

        trigger_success history: history
    end

    private

    def process_incoming_message(from, me, body, chat_id = nil)
        stripped_me = me.strip.to_s
        stripped_from = from.strip.to_s

        if can_save_conversation?(stripped_me, stripped_from, chat_id)
            Rails.logger.debug ['saving received message', stripped_me, chat_id]
            id_message = save_encrypted_message(body, stripped_me, stripped_from, stripped_from, chat_id)
        else
            id_message = nil
        end

        send_message 'app.chat.messageReceived',
                     from: from.to_s,
                     to: stripped_me,
                     message: body,
                     chat_id: chat_id,
                     id_message: id_message
    end

    def send_contacts(to, me, chat_id)
        client = find_client(me.strip.to_s)
        contacts = connection_store[:opened_chats][client][chat_id][:attendants]
        client.send(MessageBuilder::send_multichat_contacts(me.to_s, to.to_s, chat_id, contacts))
    end

    def update_attendants_in_multichat(owner, me, chat_id, removed, added)
        client = find_client(me.strip.to_s)

        contacts = connection_store[:opened_chats][client][chat_id][:attendants]
        contacts -= removed
        contacts += added

        connection_store[:opened_chats][client][chat_id][:attendants] = contacts

        if contacts.empty?
            destroy_multichat(me, chat_id)
        else
            contacts.each do |contact|
                client.send(MessageBuilder::send_multichat_contacts(me.to_s, contact, chat_id, contacts))
            end

            sync_contacts_frontend(owner, me, chat_id, contacts)
        end
    end

    def find_full_jid_in_multichat(kick_stripped, client, chat_id)
        contacts = connection_store[:opened_chats][client][chat_id][:attendants]
        contacts.find do |contact|
            contact =~ /^#{kick_stripped}/
        end
    end

    def save_encrypted_message(message, stripped_me, stripped_from, stripped_with, chat_id)
        credentials = User.crendentials_for_token(session[:token])
        found_account = credentials.detect do |c| c['jid'] == stripped_me end
        user_pass = Security::decrypt(found_account['pass'], connection_store[:cipher_key], connection_store[:cipher_iv])
        encrypted_msg = Security::encrypt(message, user_pass, Rails.application.config.aes_iv)
        History.save_message(stripped_me, stripped_from, encrypted_msg, stripped_with, chat_id)
    end
end