/*
 * Copyright (C) 2010 Teleal GmbH, Switzerland
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.teleal.cling.test;

import org.teleal.cling.DefaultUpnpServiceConfiguration;
import org.teleal.cling.UpnpService;
import org.teleal.cling.UpnpServiceConfiguration;
import org.teleal.cling.controlpoint.ControlPoint;
import org.teleal.cling.controlpoint.ControlPointImpl;
import org.teleal.cling.model.meta.LocalDevice;
import org.teleal.cling.model.message.IncomingDatagramMessage;
import org.teleal.cling.model.message.OutgoingDatagramMessage;
import org.teleal.cling.model.message.StreamRequestMessage;
import org.teleal.cling.model.message.StreamResponseMessage;
import org.teleal.cling.protocol.ProtocolFactory;
import org.teleal.cling.protocol.ProtocolFactoryImpl;
import org.teleal.cling.protocol.async.SendingNotificationAlive;
import org.teleal.cling.registry.Registry;
import org.teleal.cling.registry.RegistryImpl;
import org.teleal.cling.registry.RegistryMaintainer;
import org.teleal.cling.transport.Router;
import org.teleal.cling.transport.impl.NetworkAddressFactoryImpl;
import org.teleal.cling.transport.spi.NetworkAddressFactory;
import org.teleal.cling.transport.spi.StreamClient;
import org.teleal.cling.transport.spi.UpnpStream;

import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.ArrayList;
import java.util.List;


public class MockUpnpService implements UpnpService {

    protected final UpnpServiceConfiguration configuration;
    protected final ControlPoint controlPoint;
    protected final ProtocolFactory protocolFactory;
    protected final Registry registry;
    protected final Router router;

    protected final NetworkAddressFactory networkAddressFactory;

    private List<IncomingDatagramMessage> incomingDatagramMessages = new ArrayList();
    private List<OutgoingDatagramMessage> outgoingDatagramMessages = new ArrayList();
    private List<UpnpStream> receivedUpnpStreams = new ArrayList();
    private List<StreamRequestMessage> sentStreamRequestMessages = new ArrayList();
    private List<byte[]> broadcastedBytes = new ArrayList();

    public MockUpnpService() {
        this(false, false);
    }

    public MockUpnpService(final boolean sendsAlive, final boolean maintainsRegistry) {

        this.configuration = new DefaultUpnpServiceConfiguration() {
            @Override
            protected NetworkAddressFactory createNetworkAddressFactory(int streamListenPort) {
                // We are only interested in 127.0.0.1
                return new NetworkAddressFactoryImpl(streamListenPort) {
                    @Override
                    protected boolean isUsableNetworkInterface(NetworkInterface iface) throws Exception {
                        return (iface.isLoopback());
                    }

                    @Override
                    protected boolean isUsableAddress(NetworkInterface networkInterface, InetAddress address) {
                        return (address.isLoopbackAddress() && address instanceof Inet4Address);
                    }

                };
            }
        };

        this.protocolFactory = new ProtocolFactoryImpl(this) {
            @Override
            public SendingNotificationAlive createSendingNotificationAlive(LocalDevice localDevice) {
                return new SendingNotificationAlive(getUpnpService(), localDevice) {
                    @Override
                    protected void execute() {
                        if (sendsAlive) super.execute();
                    }
                };
            }
        };

        this.registry = new RegistryImpl(configuration, protocolFactory) {
            @Override
            protected RegistryMaintainer createRegistryMaintainer() {
                return maintainsRegistry ? super.createRegistryMaintainer() : null;
            }
        };

        this.networkAddressFactory = this.configuration.createNetworkAddressFactory();

        this.router = new Router() {

            int counter = -1;

            public UpnpServiceConfiguration getConfiguration() {
                return configuration;
            }

            public ProtocolFactory getProtocolFactory() {
                return protocolFactory;
            }

            public StreamClient getStreamClient() {
                return null;
            }

            public NetworkAddressFactory getNetworkAddressFactory() {
                return networkAddressFactory;
            }

            public void shutdown() {

            }

            public void received(IncomingDatagramMessage msg) {
                incomingDatagramMessages.add(msg);
            }

            public void received(UpnpStream stream) {
                receivedUpnpStreams.add(stream);
            }

            public void send(OutgoingDatagramMessage msg) {
                outgoingDatagramMessages.add(msg);
            }

            public StreamResponseMessage send(StreamRequestMessage msg) {
                sentStreamRequestMessages.add(msg);
                counter++;
                return getStreamResponseMessages() != null
                        ? getStreamResponseMessages()[counter]
                        : getStreamResponseMessage(msg);
            }

            public void broadcast(byte[] bytes) {
                broadcastedBytes.add(bytes);
            }
        };

        this.controlPoint = new ControlPointImpl(configuration, protocolFactory, registry);
    }

    public UpnpServiceConfiguration getConfiguration() {
        return configuration;
    }

    public ControlPoint getControlPoint() {
        return controlPoint;
    }

    public ProtocolFactory getProtocolFactory() {
        return protocolFactory;
    }

    public Registry getRegistry() {
        return registry;
    }

    public Router getRouter() {
        return router;
    }

    public void shutdown() {
        getRouter().shutdown();
        getRegistry().shutdown();
    }

    public List<IncomingDatagramMessage> getIncomingDatagramMessages() {
        return incomingDatagramMessages;
    }

    public List<OutgoingDatagramMessage> getOutgoingDatagramMessages() {
        return outgoingDatagramMessages;
    }

    public List<UpnpStream> getReceivedUpnpStreams() {
        return receivedUpnpStreams;
    }

    public List<StreamRequestMessage> getSentStreamRequestMessages() {
        return sentStreamRequestMessages;
    }

    public List<byte[]> getBroadcastedBytes() {
        return broadcastedBytes;
    }

    public StreamResponseMessage[] getStreamResponseMessages() {
        return null;
    }

    public StreamResponseMessage getStreamResponseMessage(StreamRequestMessage request) {
        return null;
    }
}
