/*
 *  Kontalk Java client
 *  Copyright (C) 2016 Kontalk Devteam <[email protected]>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.kontalk.client;

import java.util.Optional;
import java.util.logging.Logger;

import org.apache.commons.lang.StringUtils;
import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.packet.StanzaError;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smack.roster.Roster;
import org.jivesoftware.smackx.muc.packet.MUCUser;
import org.kontalk.misc.JID;
import org.kontalk.system.RosterHandler;

/**
 * Listen for presence packets.
 *
 * The presence stanza also may include a public key fingerprint
 * extension (custom Kontalk extension, based on XEP-0189) and/or a signature
 * extension for signing the status element (XEP-0027).
 *
 * @author Alexander Bikadorov {@literal <[email protected]>}
 */
class PresenceListener implements StanzaListener {
    private static final Logger LOGGER = Logger.getLogger(PresenceListener.class.getName());

    private final Roster mRoster;
    private final RosterHandler mHandler;

    public PresenceListener(Roster roster, RosterHandler handler) {
        mRoster = roster;
        mHandler = handler;

        ProviderManager.addExtensionProvider(
                PublicKeyPresence.ELEMENT_NAME,
                PublicKeyPresence.NAMESPACE,
                new PublicKeyPresence.Provider());

        ProviderManager.addExtensionProvider(
                PresenceSignature.ELEMENT_NAME,
                PresenceSignature.NAMESPACE,
                new PresenceSignature.Provider());
    }

    @Override
    public void processStanza(Stanza packet) {
        if (MUCUser.from(packet) != null) {
            // handled by MUC manager
            LOGGER.config("ignoring MUC presence, from: "+packet.getFrom());
            return;
        }

        LOGGER.config("packet: "+packet);

        Presence presence = (Presence) packet;

        JID jid = JID.fromSmack(presence.getFrom());

        ExtensionElement publicKeyExt = presence.getExtension(
                PublicKeyPresence.ELEMENT_NAME,
                PublicKeyPresence.NAMESPACE);
        PublicKeyPresence pubKey = publicKeyExt instanceof PublicKeyPresence ?
                (PublicKeyPresence) publicKeyExt :
                null;

        switch(presence.getType()) {
            case error:
                StanzaError error = presence.getError();
                if (error == null) {
                    LOGGER.warning("error presence does not contain error");
                    return;
                }
                mHandler.onPresenceError(jid, error.getType(),
                        error.getCondition());
                return;
            // NOTE: only handled here if Roster.SubscriptionMode is set to 'manual'
            case subscribe:
                byte[] key = pubKey != null ? pubKey.getKey() : null;
                if (key == null)
                    key = new byte[0];
                mHandler.onSubscriptionRequest(jid, key);
                return;
            case unsubscribe:
                // nothing to do(?)
                LOGGER.info(("ignoring unsubscribe, JID: "+jid));
                return;
        }

        // NOTE: a delay extension is sometimes included, don't know why;
        // ignoring mode, always null anyway

        // NOTE: using only the "best" presence to ignore unimportant updates
        // from multiple clients
        Presence bestPresence = mRoster.getPresence(jid.toBareSmack());

        mHandler.onPresenceUpdate(jid,
                bestPresence.getType(),
                Optional.ofNullable(bestPresence.getStatus()));

        if (pubKey != null) {
            String fp = StringUtils.defaultString(pubKey.getFingerprint()).toLowerCase();
            if (fp.isEmpty()) {
                LOGGER.warning("no fingerprint in public key presence extension");
            } else {
                mHandler.onFingerprintPresence(jid, fp);
            }
        }

        ExtensionElement signatureExt = bestPresence.getExtension(
                PresenceSignature.ELEMENT_NAME,
                PresenceSignature.NAMESPACE);
        if (signatureExt instanceof PresenceSignature) {
            PresenceSignature signing = (PresenceSignature) signatureExt;
            String signature = StringUtils.defaultString(signing.getSignature());
            if (!signature.isEmpty()) {
                mHandler.onSignaturePresence(jid, signature);
            } else {
                LOGGER.warning("no signature in signed presence extension");
            }
        }
    }
}