/*
 * 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.data;

import org.teleal.cling.DefaultUpnpServiceConfiguration;
import org.teleal.cling.binding.LocalServiceBinder;
import org.teleal.cling.model.DefaultServiceManager;
import org.teleal.cling.model.ValidationError;
import org.teleal.cling.model.ValidationException;
import org.teleal.cling.model.message.OutgoingDatagramMessage;
import org.teleal.cling.model.meta.DeviceIdentity;
import org.teleal.cling.model.meta.DeviceService;
import org.teleal.cling.model.meta.LocalDevice;
import org.teleal.cling.model.meta.LocalService;
import org.teleal.cling.model.meta.RemoteDevice;
import org.teleal.cling.model.meta.RemoteDeviceIdentity;
import org.teleal.cling.model.meta.RemoteService;
import org.teleal.cling.transport.impl.NetworkAddressFactoryImpl;
import org.teleal.cling.transport.spi.DatagramProcessor;
import org.teleal.common.util.Exceptions;

import java.lang.reflect.Constructor;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.logging.Logger;


public class SampleData {

    private static Logger log = Logger.getLogger(SampleData.class.getName());

    /* ###################################################################################### */


    public static URL getLocalBaseURL() {
        try {
            return new URL("http://127.0.0.1:" + NetworkAddressFactoryImpl.DEFAULT_TCP_HTTP_LISTEN_PORT + "/");
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        }
    }

    /* ###################################################################################### */

    public static DeviceIdentity createLocalDeviceIdentity() {
        return createLocalDeviceIdentity(1800);
    }

    public static DeviceIdentity createLocalDeviceIdentity(int maxAgeSeconds) {
        return new DeviceIdentity(SampleDeviceRoot.getRootUDN(), maxAgeSeconds);
    }

    public static LocalDevice createLocalDevice() {
        return createLocalDevice(createLocalDeviceIdentity());
    }

    public static LocalDevice createLocalDevice(DeviceIdentity identity) {
        try {

            Constructor<LocalDevice> ctor = LocalDevice.getConstructor();
            Constructor<LocalService> serviceConstructor = LocalService.getConstructor();

            return new SampleDeviceRootLocal(
                    identity,
                    new SampleServiceOne().newInstance(serviceConstructor),
                    new LocalDevice[]{
                            new SampleDeviceEmbeddedOneLocal(
                                    new DeviceIdentity(SampleDeviceEmbeddedOne.getEmbeddedOneUDN(), identity),
                                    null,
                                    new LocalDevice[]{
                                            new SampleDeviceEmbeddedTwoLocal(
                                                    new DeviceIdentity(SampleDeviceEmbeddedTwo.getEmbeddedTwoUDN(), identity),
                                                    null,
                                                    null
                                            ).newInstance(ctor)
                                    }
                            ).newInstance(ctor)
                    }
            ).newInstance(ctor);

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static LocalService getFirstService(LocalDevice device) {
        return device.getDeviceServices()[0].getService();
    }

    /* ###################################################################################### */

    public static RemoteDeviceIdentity createRemoteDeviceIdentity() {
        return createRemoteDeviceIdentity(1800);
    }

    public static RemoteDeviceIdentity createRemoteDeviceIdentity(int maxAgeSeconds) {
        RemoteDeviceIdentity rootIdentity = null;
        try {
            rootIdentity = new RemoteDeviceIdentity(
                    SampleDeviceRoot.getRootUDN(),
                    maxAgeSeconds,
                    SampleDeviceRoot.getDeviceDescriptorURL(),
                    null,
                    InetAddress.getByName("127.0.0.1")
            );
        } catch (UnknownHostException e) {
            throw new RuntimeException(e);
        }
        return rootIdentity;
    }

    public static RemoteDevice createRemoteDevice() {
        return createRemoteDevice(createRemoteDeviceIdentity());
    }

    public static RemoteDevice createRemoteDevice(RemoteDeviceIdentity identity) {
        try {

            Constructor<RemoteDevice> deviceConstructor = RemoteDevice.getConstructor();
            Constructor<RemoteService> serviceConstructor = RemoteService.getConstructor();

            return new SampleDeviceRoot(
                    identity,
                    new SampleServiceOne().newInstance(serviceConstructor),
                    new RemoteDevice[]{
                            new SampleDeviceEmbeddedOne(
                                    new RemoteDeviceIdentity(SampleDeviceEmbeddedOne.getEmbeddedOneUDN(), identity),
                                    null,
                                    new RemoteDevice[]{
                                            new SampleDeviceEmbeddedTwo(
                                                    new RemoteDeviceIdentity(SampleDeviceEmbeddedTwo.getEmbeddedTwoUDN(), identity),
                                                    null,
                                                    null
                                            ).newInstance(deviceConstructor)
                                    }
                            ).newInstance(deviceConstructor)
                    }
            ).newInstance(deviceConstructor);

        } catch (Exception e) {
/*
            Throwable cause = Exceptions.unwrap(e);
            if (cause instanceof ValidationException) {
                ValidationException ex = (ValidationException) cause;
                for (ValidationError validationError : ex.getErrors()) {
                    log.severe(validationError.toString());
                }
            }
*/
            throw new RuntimeException(e);
        }
    }

    public static RemoteService getFirstService(RemoteDevice device) {
        return device.getDeviceServices()[0].getService();
    }
    
    /* ###################################################################################### */

    public static <T> DeviceService<LocalService<T>> newDeviceService(LocalServiceBinder binder, Class<T> clazz) {
        return newDeviceService(binder.read(clazz), clazz);
    }

    public static <T> DeviceService<LocalService<T>> newDeviceService(DeviceService<LocalService<T>> deviceService, Class<T> clazz) {
        deviceService.getService().setManager(
                new DefaultServiceManager(deviceService.getService(), clazz)
        );
        return deviceService;
    }

    /* ###################################################################################### */

 /*   public static void assertTestDataMatchServiceTwo(Service svc) {

        Service<DeviceService> service = svc;

        Device sampleDevice = (service.getDeviceService().getDevice().isLocal()) ? getLocalDevice() : getRemoteDevice();
        Service<DeviceService> sampleService = getServiceTwo((Device) sampleDevice.getEmbeddedDevices().get(0));

        assertEquals(service.getActions().size(), sampleService.getActions().size());

        assertEquals(service.getActions().get("GetFoo").getName(), sampleService.getActions().get("GetFoo").getName());
        assertEquals(service.getActions().get("GetFoo").getArguments().size(), sampleService.getActions().get("GetFoo").getArguments().size());
        assertEquals(service.getActions().get("GetFoo").getArguments().get(0).getName(), service.getActions().get("GetFoo").getArguments().get(0).getName());
        assertEquals(service.getActions().get("GetFoo").getArguments().get(0).getDirection(), sampleService.getActions().get("GetFoo").getArguments().get(0).getDirection());
        assertEquals(service.getActions().get("GetFoo").getArguments().get(0).getRelatedStateVariableName(), sampleService.getActions().get("GetFoo").getArguments().get(0).getRelatedStateVariableName());

        assertEquals(service.getStateVariables().size(), sampleService.getStateVariables().size());
        assertTrue(service.getStateVariables().containsKey("Foo"));

        assertEquals(service.getStateVariables().get("Foo").getName(), "Foo");
        assertTrue(service.getStateVariables().get("Foo").isSendEvents());
        assertEquals(service.getStateVariables().get("Foo").getDatatype(), Datatype.Builtin.BOOLEAN.getDatatype());

    }

    public static void assertTestDataMatchServiceThree(Service svc) {
        assertTestDataMatchServiceTwo(svc);
    }
*/

    public static void debugMsg(OutgoingDatagramMessage msg) {
        DatagramProcessor proc = new DefaultUpnpServiceConfiguration().getDatagramProcessor();
        proc.write(msg);
    }


}