/*
 This file is part of GNUnet.
  (C) 2012, 2013 Christian Grothoff (and other contributing authors)

  GNUnet 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, or (at your
  option) any later version.

  GNUnet 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 GNUnet; see the file COPYING.  If not, write to the
  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  Boston, MA 02111-1307, USA.
 */

package org.gnunet.transport;

import org.gnunet.transport.messages.AddressIterateMessage;
import org.gnunet.transport.messages.AddressIterateResponseMessage;
import org.gnunet.transport.messages.BlacklistInitMessage;
import org.gnunet.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Get active addresses of peers.
 */
public class AddressQuery {
    private static final Logger logger = LoggerFactory
            .getLogger(AddressQuery.class);
    private final boolean oneShot;
    private final PeerIdentity peerIdentity;
    private final PeerAddressMonitorCallback monitorCallback;
    private final PeerAddressListCallback listCallback;
    /**
     * Client connecting to the transport service.
     */
    private Client client;


    private final class AddressMonitorReceiver extends RunaboutMessageReceiver {
        public void visit(AddressIterateResponseMessage m) {
            if (m.content == null) {
                // uglyness in the api, when using one-shot the service
                // may send a second message indicating end of list.
                if (listCallback != null) {
                    listCallback.onDone();
                } else {
                    client.reconnect();
                    sendInitMessage();
                }
                return;
            }
            if (listCallback != null) {
                if (m.content.addrLen == 0 && m.content.pluginLen == 0) {
                    logger.warn("empty peer address list item");
                } else {
                    HelloAddress helloAddress = new HelloAddress();
                    helloAddress.peer = m.content.peerIdentity;
                    // FIXME: address and plugin
                    listCallback.onPeerAddress(helloAddress);
                }
            } else {
                if (m.content.addrLen == 0 && m.content.pluginLen == 0) {
                    monitorCallback.onPeerDisconnect(m.content.peerIdentity);
                } else {
                    HelloAddress helloAddress = new HelloAddress();
                    helloAddress.peer = m.content.peerIdentity;
                    // FIXME: address and plugin
                    monitorCallback.onPeerAddress(helloAddress);
                }
            }
        }
        @Override
        public void handleError() {
            client.reconnect();
            client.send(new BlacklistInitMessage());
        }
    }

    /**
     * Monitor active addresses of the given peer, or of all peers if 'null' is
     * passed as peer identity.
     *
     * @param configuration configuration to use for connecting to
     *                      the transport service
     * @param peerIdentity peer identity to monitor addresses of, null
     *                     to monitor addresses of all connected peers
     * @param peerAddressCallback callback to call when receiving an address
     *                            for the specified peer
     */
    public AddressQuery(Configuration configuration,
                        PeerIdentity peerIdentity,
                        PeerAddressMonitorCallback peerAddressCallback) {
        this.oneShot = false;
        this.peerIdentity = peerIdentity;
        this.monitorCallback = peerAddressCallback;
        this.listCallback = null;

        client = new Client("transport", configuration);
        client.send(new BlacklistInitMessage());
        client.installReceiver(new AddressMonitorReceiver());

        createAndInitClient(configuration);
    }

    /**
     * Monitor active addresses of the given peer, or of all peers if 'null' is
     * passed as peer identity.
     *
     * @param configuration configuration to use for connecting to
     *                      the transport service
     * @param peerIdentity peer identity to monitor addresses of, null
     *                     to monitor addresses of all connected peers
     * @param listCallback callback to call when receiving an address
     *                            for the specified peer
     */
    public AddressQuery(Configuration configuration,
                        PeerIdentity peerIdentity,
                        PeerAddressListCallback listCallback) {
        this.oneShot = true;
        this.peerIdentity = peerIdentity;
        this.listCallback = listCallback;
        this.monitorCallback = null;

        createAndInitClient(configuration);

    }

    private void createAndInitClient(Configuration configuration) {
        client = new Client("transport", configuration);
        client.send(new BlacklistInitMessage());
        client.installReceiver(new AddressMonitorReceiver());
        sendInitMessage();
    }

    private void sendInitMessage() {
        AddressIterateMessage m = new AddressIterateMessage();
        m.oneShot = oneShot;
        if (peerIdentity == null) {
            m.peer = new PeerIdentity();
        } else {
            m.peer = peerIdentity;
        }
        // value seems to be deprecated in the C api, we're not using
        // service-managed timeouts anyway, so send FOREVER
        m.timeout = AbsoluteTime.FOREVER.asMessage();
        client.send(m);
    }
}
