class WsRosterController < WebsocketRails::BaseController
    require 'xmpp4r/roster'
    require 'xmpp4r/vcard'

    def initialize
        super

        @storages_arr = [:clients, :rosters]
        @storages_hash = [:link_roster_client, :presences]
    end

    def initialize_storage
        @storages_arr.each do |storage|
            connection_store[storage] = []
        end

        @storages_hash.each do |storage|
            connection_store[storage] = {}
        end
    end

    ##
    # Pripoj sa na jabber ucty.
    def connect
        initialize_storage()

        # TODO: Pouzit najprv:
        # clients = Token.fing_user_accounts_having_to_token(session[:token])
        # ale toto, az ked budem mat dokonceny multiaccount (settings a popup)
        cookies = env['rack.request.cookie_hash']
        cipher_key = cookies['key']
        cipher_iv = cookies['iv']

        clients = session[:users].map do |jid, encrypted_pass|
            decrypted_pass = Security::decrypt(encrypted_pass, cipher_key, cipher_iv)
            {jid: jid, pass: decrypted_pass}
        end

        clients.each do |client|
            begin
                client = Signin.try_login(client[:jid], client[:pass])
                connection_store[:clients] << client
            rescue Signin::LoginError
                send_message 'app.client.cannot_connect', true
            end
        end
    end

    ##
    # Inicializuj roster so zoznamom ludi v nom.
    # Vrat zoznam ludi (ich JID).
    def init_roster
        all_jids = []

        connection_store[:clients].each do |client|
            roster = Jabber::Roster::Helper.new(client)

            connection_store[:rosters] << roster
            connection_store[:link_roster_client][roster] = client

            roster.get_roster()
            roster.wait_for_roster()

            roster.items.each do |jid, contact|
                all_jids << {
                    jid: jid.to_s,
                    belongsTo: client.jid.strip.to_s
                }
            end
        end

        trigger_success contacts: all_jids
    end

    ##
    # Stiahni vcard ludi v rosteri
    def start_fetching_vcards
        connection_store[:rosters].each do |roster|
            client = connection_store[:link_roster_client][roster]

            roster.items.each do |jid, contact|
                Thread.new do
                    vcard = get_vcard(client, jid.to_s)
                    send_message 'app.roster.vcard', jid: jid.to_s, vcard: vcard
                end
            end
        end
    end

    ##
    # Zacni pocuvat zmeny stavov v rosteri
    def start_polling_contacts_state
        connection_store[:rosters].each do |roster|
            roster.add_subscription_callback do |roster_item, stanza|
                # stan unsubscribe = niekto uz ma nesubscribuje
                # stan unsubscribed = niekto ma uz nesubscribuje a aj si ma vymazal
                # stan subscribed = niekto moze vidiet moj stav

                client = connection_store[:link_roster_client][roster]

                send_message 'app.roster.subscriptionChanged',
                             action: stanza.type,
                             jid: roster_item.jid.strip.to_s,
                             belongsTo: client.jid.strip.to_s
            end

            roster.add_presence_callback do |roster_item, old_presence, new_presence|
                if new_presence.type == :unavailable
                    result = {status: :offline, message: ''}
                else
                    status = uniform_presence(new_presence.show)
                    result = { status: status, message: new_presence.status.to_s }
                end

                send_message 'app.roster.statusChanged',
                             jid: roster_item.jid.strip.to_s, status: result
            end
        end
    end

    ##
    # Nastav ma ako online
    #
    # Musi sa zavolat az po start_polling_contacts_state, inak sa nemusia
    # zachytit stavy ostatnych v rosteri.
    def set_presence
        connection_store[:clients].each do |client|
            presence = Jabber::Presence.new.set_type(:available)
            client.send(presence)
            connection_store[:presences][client] = presence
        end
    end

    def remove_contact
        jid = message[:jid]
        client_jid = message[:client]

        found_roster_item = nil
        connection_store[:rosters].each do |roster|
            roster_item = roster.find(jid)
            if roster_item.first[1] && connection_store[:link_roster_client][roster].jid.strip.to_s == client_jid
                found_roster_item = roster_item.first[1]
                break
            end
        end

        found_roster_item && found_roster_item.remove()
    end

    ##
    # Ziskaj informacie o mne (meno, stav, status...)
    def myself
        # TODO: v pripade viacerych uctov zjednotit meno a stav
        vcard = {}
        jid = presence = ''

        connection_store[:clients].each do |client|
            vcard = get_vcard(client)
            jid = client.jid.strip.to_s
            presence = uniform_presence(connection_store[:presences][client].show)
        end

        trigger_success jid: jid, vcard: vcard, status: presence
    end

    def me_update_status
        status_message = message[:message]
        state = message[:state]

        xmpp_state = case state
                     when 'away' then :away
                     when 'dnd' then :dnd
                     else nil
        end

        presence = Jabber::Presence.new
        presence.show = xmpp_state
        presence.status = status_message

        connection_store[:clients].each do |client|
            client.send(presence)
        end
    end

    def me_update_vcard

    end

    def disconnect
        connection_store[:clients] && connection_store[:clients].each do |client|
            client.close()
        end

        @storages_arr.each do |storage|
            connection_store.delete(storage)
        end

        @storages_hash.each do |storage|
            connection_store.delete(storage)
        end
    end

    ##
    # Pridaj noveho priatela do zoznamu
    def add_friend(data)

    end

    private

    def get_vcard(me, contact_jid = nil)
        vcard = Jabber::Vcard::Helper.get(me, contact_jid)

        { name: pull_name_from_vcard(vcard) || contact_jid,
          avatar: vcard['PHOTO/TYPE'] && ('data:' + vcard['PHOTO/TYPE'] + ';base64,' + vcard['PHOTO/BINVAL']) || ''
        }
    end

    def pull_name_from_vcard(vcard)
        vcard && (vcard['FN'] || vcard['NICKNAME'])
    end

    def uniform_presence(xmpp_presence)
        case xmpp_presence
            when :away, :xa then :away
            when :dnd       then :dnd
            else                 :online
        end
    end
end