/*
 *  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.io.IOException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.util.SuccessCallback;
import org.jivesoftware.smackx.iqregister.packet.Registration;
import org.jivesoftware.smackx.xdata.Form;
import org.jivesoftware.smackx.xdata.FormField;
import org.jivesoftware.smackx.xdata.packet.DataForm;
import org.kontalk.misc.Callback;

/**
 * Send request and listen to response for private data over
 * 'jabber:iq:register' namespace.
 *
 * Temporary server connection is established when requesting key.
 *
 * TODO not used
 *
 * @author Alexander Bikadorov {@literal <[email protected]>}
 */
public final class PrivateKeyReceiver implements SuccessCallback<IQ> {
    private static final Logger LOGGER = Logger.getLogger(PrivateKeyReceiver.class.getName());

    private static final String FORM_TYPE_VALUE = "http://kontalk.org/protocol/register#privatekey";
    private static final String FORM_TOKEN_VAR = "token";

    private final Callback.Handler<String> mHandler;
    private KonConnection mConn = null;

    public PrivateKeyReceiver(Callback.Handler<String> handler) {
        mHandler = handler;
    }

    public void sendRequest(EndpointServer server, boolean validateCertificate,
            final String registrationToken) {
        // create connection
        mConn = new KonConnection(server, validateCertificate);

        Thread thread = new Thread("Private Key Request") {
            @Override
            public void run() {
                PrivateKeyReceiver.this.sendRequestAsync(registrationToken);
            }
        };
        thread.setDaemon(true);
        thread.start();
    }

    private void sendRequestAsync(String registrationToken) {
        // connect
        try {
            mConn.connect();
        } catch (XMPPException | SmackException | IOException | InterruptedException ex) {
            LOGGER.log(Level.WARNING, "can't connect to "+mConn.getServer(), ex);
            mHandler.handle(new Callback<>(ex));
            return;
        }

        Registration iq = new Registration();
        iq.setType(IQ.Type.set);
        iq.setTo(mConn.getXMPPServiceDomain());
        Form form = new Form(DataForm.Type.submit);

        // form type field
        FormField type = new FormField(FormField.FORM_TYPE);
        type.setType(FormField.Type.hidden);
        type.addValue(FORM_TYPE_VALUE);
        form.addField(type);

        // token field
        FormField fieldKey = new FormField(FORM_TOKEN_VAR);
        fieldKey.setLabel("Registration token");
        fieldKey.setType(FormField.Type.text_single);
        fieldKey.addValue(registrationToken);
        form.addField(fieldKey);

        iq.addExtension(form.getDataFormToSend());

        mConn.sendIqRequestAsync(iq)
             .onSuccess(this)
             .onError(exception -> mHandler.handle(new Callback<>(exception)));
    }

    @Override
    public void onSuccess(IQ packet) {
        LOGGER.info("response: "+packet);

        mConn.disconnect();

        if (!(packet instanceof IQ)) {
            LOGGER.warning("response not an IQ packet");
            finish(null);
            return;
        }
        IQ iq = (IQ) packet;

        if (iq.getType() != IQ.Type.result) {
            LOGGER.warning("ignoring response with IQ type: "+iq.getType());
            this.finish(null);
            return;
        }

        DataForm response = iq.getExtension(DataForm.ELEMENT, DataForm.NAMESPACE);
        if (response == null) {
            this.finish(null);
            return;
        }

        String token = null;
        List<FormField> fields = response.getFields();
        for (FormField field : fields) {
            if ("token".equals(field.getVariable())) {
                token = field.getValues().get(0).toString();
                break;
            }
        }

        this.finish(token);
    }

    private void finish(String token) {
        mHandler.handle(token == null ?
                new Callback<>() :
                new Callback<>(token));
    }

}