/*
 * 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 Lesser 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.teleal.cling.test.model;

import org.teleal.cling.model.Constants;
import org.teleal.cling.model.ServerClientTokens;
import org.teleal.cling.model.message.header.*;
import org.teleal.cling.model.types.DeviceType;
import org.teleal.cling.model.types.NamedDeviceType;
import org.teleal.cling.model.types.NamedServiceType;
import org.teleal.cling.model.types.ServiceType;
import org.teleal.cling.model.types.UDADeviceType;
import org.teleal.cling.model.types.UDAServiceType;
import org.teleal.common.util.MimeType;
import org.testng.annotations.Test;

import java.net.URI;

import static org.testng.Assert.assertEquals;


public class HeaderParsingTest {

    @Test
    public void parseContentTypeHeader() {
        ContentTypeHeader header = new ContentTypeHeader(MimeType.valueOf("foo/bar;charset=\"utf-8\""));
        assertEquals(header.getString(), "foo/bar;charset=\"utf-8\"");
    }

    @Test
    public void parseDeviceType() {
        DeviceType deviceType = DeviceType.valueOf("urn:foo-bar:device:MyDeviceType:123");
        assertEquals(deviceType.getNamespace(), "foo-bar");
        assertEquals(deviceType.getType(), "MyDeviceType");
        assertEquals(deviceType.getVersion(), 123);
    }

    @Test
    public void parseUDADeviceType() {
        UDADeviceType deviceType = (UDADeviceType)DeviceType.valueOf("urn:schemas-upnp-org:device:MyDeviceType:123");
        assertEquals(deviceType.getType(), "MyDeviceType");
        assertEquals(deviceType.getVersion(), 123);
    }

    @Test(expectedExceptions = InvalidHeaderException.class)
    public void parseInvalidDeviceTypeHeader() {
        DeviceTypeHeader header = new DeviceTypeHeader();
        header.setString("urn:foo-bar:device:!@#:123");
    }

    @Test
    public void parseDeviceTypeHeaderURI() {
        DeviceTypeHeader header = new DeviceTypeHeader(URI.create("urn:schemas-upnp-org:device:MyDeviceType:123"));
        assertEquals(header.getValue().getNamespace(), "schemas-upnp-org");
        assertEquals(header.getValue().getType(), "MyDeviceType");
        assertEquals(header.getValue().getVersion(), 123);
        assertEquals(header.getString(), "urn:schemas-upnp-org:device:MyDeviceType:123");

    }

    @Test
    public void parseDeviceUSNHeader() {
        DeviceUSNHeader header = new DeviceUSNHeader();
        header.setString("uuid:MY-DEVICE-123::urn:schemas-upnp-org:device:MY-DEVICE-TYPE:1");
        assertEquals(header.getValue().getUdn().getIdentifierString(), "MY-DEVICE-123");
        assert header.getValue().getDeviceType() instanceof UDADeviceType;
    }

    @Test
    public void parseDeviceUSNHeaderStatic() {
        DeviceUSNHeader header = new DeviceUSNHeader(
                NamedDeviceType.fromString("uuid:MY-DEVICE-123::urn:schemas-upnp-org:device:MY-DEVICE-TYPE:1")
        );
        assertEquals(header.getValue().getUdn().getIdentifierString(), "MY-DEVICE-123");
        assert header.getValue().getDeviceType() instanceof UDADeviceType;
    }

    @Test(expectedExceptions = InvalidHeaderException.class)
    public void parseInvalidDeviceUSNHeader() {
        DeviceUSNHeader header = new DeviceUSNHeader();
        header.setString("uuid:MY-DEVICE-123--urn:schemas-upnp-org:device:MY-DEVICE-TYPE:1");
    }

    @Test(expectedExceptions = InvalidHeaderException.class)
    public void parseInvalidEXTHeader() {
        EXTHeader header = new EXTHeader();
        header.setString("MUST BE EMPTY STRING");
    }

    @Test
    public void parseHostHeaderConstructor() {
        HostHeader header = new HostHeader("foo.bar", 1234);
        assertEquals(header.getValue().getHost(), "foo.bar");
        assertEquals(header.getValue().getPort(), 1234);

        header = new HostHeader(1234);
        assertEquals(header.getValue().getHost(), Constants.IPV4_UPNP_MULTICAST_GROUP);
        assertEquals(header.getValue().getPort(), 1234);
    }

    @Test
    public void parseHostHeader() {
        HostHeader header = new HostHeader();
        assertEquals(header.getValue().getHost(), Constants.IPV4_UPNP_MULTICAST_GROUP);
        assertEquals(header.getValue().getPort(), Constants.UPNP_MULTICAST_PORT);

        header = new HostHeader();
        header.setString("foo.bar:1234");
        assertEquals(header.getValue().getHost(), "foo.bar");
        assertEquals(header.getValue().getPort(), 1234);

        header = new HostHeader();
        header.setString("foo.bar");
        assertEquals(header.getValue().getHost(), "foo.bar");
        assertEquals(header.getValue().getPort(), Constants.UPNP_MULTICAST_PORT);
    }

    @Test(expectedExceptions = InvalidHeaderException.class)
    public void parseInvalidHostHeader() {
        HostHeader header = new HostHeader();
        header.setString("foo.bar:abc");
    }

    @Test(expectedExceptions = InvalidHeaderException.class)
    public void parseInvalidLocationHeader() {
        LocationHeader header = new LocationHeader();
        header.setString("this://is.not...a valid URL");
    }

    @Test(expectedExceptions = InvalidHeaderException.class)
    public void parseInvalidMANHeader() {
        MANHeader header = new MANHeader("abc");
        header.setString("\"foo.bar\"; ns = baz"); // Not valid
    }

    @Test
    public void parseMANHeaderNoNS() {
        MANHeader header = new MANHeader("abc");
        header.setString("\"foo.bar\"");
        assert header.getValue().equals("foo.bar");
        assert header.getNamespace() == null;
        assert header.getString().equals("\"foo.bar\"");
    }

    @Test
    public void parseMANHeaderNS() {
        MANHeader header = new MANHeader("abc");
        header.setString("\"foo.bar\"; ns =12");
        assert header.getValue().equals("foo.bar");
        assert header.getNamespace().equals("12");
        assert header.getString().equals("\"foo.bar\"; ns=12");
    }

    @Test
    public void parseMaxAgeHeader() {
        MaxAgeHeader header = new MaxAgeHeader();
        header.setString("max-age=1234, foobar=baz");
        assertEquals(header.getValue(), new Integer(1234));
    }

    @Test(expectedExceptions = InvalidHeaderException.class)
    public void parseInvalidMaxAgeHeader() {
        MaxAgeHeader header = new MaxAgeHeader();
        header.setString("max-foo=123");
    }

    @Test
    public void parseMXHeader() {
        MXHeader header = new MXHeader();
        header.setString("111");
        assertEquals(header.getValue(), new Integer(111));
        
        header = new MXHeader();
        header.setString("123");
        assertEquals(header.getValue(), MXHeader.DEFAULT_VALUE);

    }

    @Test(expectedExceptions = InvalidHeaderException.class)
    public void parseInvalidMXHeader() {
        MXHeader header = new MXHeader();
        header.setString("abc");
    }

    @Test(expectedExceptions = InvalidHeaderException.class)
    public void parseInvalidNTSHeader() {
        NTSHeader header = new NTSHeader();
        header.setString("foo");
    }

    @Test(expectedExceptions = InvalidHeaderException.class)
    public void parseInvalidRootDeviceHeader() {
        RootDeviceHeader header = new RootDeviceHeader();
        header.setString("upnp:foodevice");
    }

    @Test
    public void parseServerHeader() {
        ServerHeader header = new ServerHeader();
        header.setString("foo/1 UPnP/1.1 bar/2");
        assertEquals(header.getValue().getOsName(), "foo");
        assertEquals(header.getValue().getOsVersion(), "1");
        assertEquals(header.getValue().getProductName(), "bar");
        assertEquals(header.getValue().getProductVersion(), "2");
        assertEquals(header.getValue().getMajorVersion(), 1);
        assertEquals(header.getValue().getMinorVersion(), 1);

        // Commas...
        header = new ServerHeader();
        header.setString("foo/1, UPnP/1.1, bar/2");
        assertEquals(header.getValue().getOsName(), "foo");
        assertEquals(header.getValue().getOsVersion(), "1");
        assertEquals(header.getValue().getProductName(), "bar");
        assertEquals(header.getValue().getProductVersion(), "2");
        assertEquals(header.getValue().getMajorVersion(), 1);
        assertEquals(header.getValue().getMinorVersion(), 1);

        // Whitespace in tokens
        header = new ServerHeader();
        header.setString("foo baz/1 UPnP/1.1 bar abc/2");
        assertEquals(header.getValue().getOsName(), "foo baz");
        assertEquals(header.getValue().getOsVersion(), "1");
        assertEquals(header.getValue().getProductName(), "bar abc");
        assertEquals(header.getValue().getProductVersion(), "2");
        assertEquals(header.getValue().getMajorVersion(), 1);
        assertEquals(header.getValue().getMinorVersion(), 1);

        // Commas and whitespace!
        header = new ServerHeader();
        header.setString("foo baz/1, UPnP/1.1, bar abc/2");
        assertEquals(header.getValue().getOsName(), "foo baz");
        assertEquals(header.getValue().getOsVersion(), "1");
        assertEquals(header.getValue().getProductName(), "bar abc");
        assertEquals(header.getValue().getProductVersion(), "2");
        assertEquals(header.getValue().getMajorVersion(), 1);
        assertEquals(header.getValue().getMinorVersion(), 1);

        // Absolutely not valid!
        header = new ServerHeader();
        header.setString("foo/1 UPnP/1.");
        assertEquals(header.getValue().getOsName(), ServerClientTokens.UNKNOWN_PLACEHOLDER);
        assertEquals(header.getValue().getOsVersion(), ServerClientTokens.UNKNOWN_PLACEHOLDER);
        assertEquals(header.getValue().getProductName(), ServerClientTokens.UNKNOWN_PLACEHOLDER);
        assertEquals(header.getValue().getProductVersion(), ServerClientTokens.UNKNOWN_PLACEHOLDER);
        assertEquals(header.getValue().getMajorVersion(), 1);
        assertEquals(header.getValue().getMinorVersion(), 0); // Assume UDA 1.0

    }

    @Test(expectedExceptions = InvalidHeaderException.class)
    public void parseInvalidServerHeader() {
        ServerHeader header = new ServerHeader();
        header.setString("foo/1 baz/123 bar/2");
    }

    @Test
    public void parseServiceType() {
        ServiceType serviceType = ServiceType.valueOf("urn:foo-bar:service:MyServiceType:123");
        assertEquals(serviceType.getNamespace(), "foo-bar");
        assertEquals(serviceType.getType(), "MyServiceType");
        assertEquals(serviceType.getVersion(), 123);
    }

    @Test
    public void parseUDAServiceType() {
        UDAServiceType serviceType = (UDAServiceType)ServiceType.valueOf("urn:schemas-upnp-org:service:MyServiceType:123");
        assertEquals(serviceType.getType(), "MyServiceType");
        assertEquals(serviceType.getVersion(), 123);
    }

    @Test(expectedExceptions = InvalidHeaderException.class)
    public void parseInvalidServiceTypeHeader() {
        ServiceTypeHeader header = new ServiceTypeHeader();
        header.setString("urn:foo-bar:service:!@#:123");
    }

    @Test
    public void parseServiceTypeHeaderURI() {
        ServiceTypeHeader header = new ServiceTypeHeader(URI.create("urn:schemas-upnp-org:service:MyServiceType:123"));
        assertEquals(header.getValue().getNamespace(), "schemas-upnp-org");
        assertEquals(header.getValue().getType(), "MyServiceType");
        assertEquals(header.getValue().getVersion(), 123);
        assertEquals(header.getString(), "urn:schemas-upnp-org:service:MyServiceType:123");

    }

    @Test
    public void parseServiceUSNHeader() {
        ServiceUSNHeader header = new ServiceUSNHeader();
        header.setString("uuid:MY-SERVICE-123::urn:schemas-upnp-org:service:MY-SERVICE-TYPE:1");
        assertEquals(header.getValue().getUdn().getIdentifierString(), "MY-SERVICE-123");
        assert header.getValue().getServiceType() instanceof UDAServiceType;
    }

    @Test
    public void parseServiceUSNHeaderStatic() {
        ServiceUSNHeader header = new ServiceUSNHeader(
                NamedServiceType.fromString("uuid:MY-SERVICE-123::urn:schemas-upnp-org:service:MY-SERVICE-TYPE:1")
        );
        assertEquals(header.getValue().getUdn().getIdentifierString(), "MY-SERVICE-123");
        assert header.getValue().getServiceType() instanceof UDAServiceType;
    }

    @Test(expectedExceptions = InvalidHeaderException.class)
    public void parseInvalidServiceUSNHeader() {
        ServiceUSNHeader header = new ServiceUSNHeader();
        header.setString("uuid:MY-SERVICE-123--urn:schemas-upnp-org:service:MY-SERVICE-TYPE:1");
    }

    @Test(expectedExceptions = InvalidHeaderException.class)
    public void parseInvalidSTAllHeader() {
        STAllHeader header = new STAllHeader();
        header.setString("ssdp:foo");
    }

    @Test(expectedExceptions = InvalidHeaderException.class)
    public void parseInvalidUDADeviceTypeHeader() {
        UDADeviceTypeHeader header = new UDADeviceTypeHeader();
        header.setString("urn:foo-bar:device:!@#:123");
    }

    @Test
    public void parseUDADeviceTypeHeaderURI() {
        UDADeviceTypeHeader header = new UDADeviceTypeHeader(URI.create("urn:schemas-upnp-org:device:MyDeviceType:123"));
        assertEquals(header.getValue().getNamespace(), "schemas-upnp-org");
        assertEquals(header.getValue().getType(), "MyDeviceType");
        assertEquals(header.getValue().getVersion(), 123);
        assertEquals(header.getString(), "urn:schemas-upnp-org:device:MyDeviceType:123");
    }

    @Test(expectedExceptions = InvalidHeaderException.class)
    public void parseInvalidUDAServiceTypeHeader() {
        UDAServiceTypeHeader header = new UDAServiceTypeHeader();
        header.setString("urn:foo-bar:service:!@#:123");
    }

    @Test
    public void parseUDAServiceTypeHeaderURI() {
        UDAServiceTypeHeader header = new UDAServiceTypeHeader(URI.create("urn:schemas-upnp-org:service:MyServiceType:123"));
        assertEquals(header.getValue().getNamespace(), "schemas-upnp-org");
        assertEquals(header.getValue().getType(), "MyServiceType");
        assertEquals(header.getValue().getVersion(), 123);
        assertEquals(header.getString(), "urn:schemas-upnp-org:service:MyServiceType:123");

    }

    @Test
    public void parseUDNHeader() {
        UDNHeader header = new UDNHeader();
        header.setString("uuid:MY-UUID-1234");
        assertEquals(header.getValue().getIdentifierString(), "MY-UUID-1234");
    }

    @Test(expectedExceptions = InvalidHeaderException.class)
    public void parseInvalidUDNHeaderPrefix() {
        UDNHeader header = new UDNHeader();
        header.setString("MY-UUID-1234");
    }

    @Test(expectedExceptions = InvalidHeaderException.class)
    public void parseInvalidUDNHeaderURN() {
        UDNHeader header = new UDNHeader();
        header.setString("uuid:MY-UUID-1234::urn:foo-bar:baz");
    }

    @Test(expectedExceptions = InvalidHeaderException.class)
    public void parseInvalidUSNRootDeviceHeader() {
        USNRootDeviceHeader header = new USNRootDeviceHeader();
        header.setString("uuid:MY-UUID-1234::upnp:rootfoo");
    }

    @Test
    public void parseSoapActionHeader() {
        SoapActionHeader header = new SoapActionHeader(URI.create("urn:schemas-upnp-org:service:MyServiceType:1#MyAction"));
        assertEquals(header.getValue().getServiceType().getNamespace(), "schemas-upnp-org");
        assertEquals(header.getValue().getServiceType().getType(), "MyServiceType");
        assertEquals(header.getValue().getServiceType().getVersion(), 1);
        assertEquals(header.getValue().getActionName(), "MyAction");
        assertEquals(header.getString(), "\"urn:schemas-upnp-org:service:MyServiceType:1#MyAction\"");

    }

    @Test
    public void parseSoapActionHeaderString() {
        SoapActionHeader header = new SoapActionHeader();
        header.setString("\"urn:schemas-upnp-org:service:MyServiceType:1#MyAction\"");
        assertEquals(header.getValue().getServiceType().getNamespace(), "schemas-upnp-org");
        assertEquals(header.getValue().getServiceType().getType(), "MyServiceType");
        assertEquals(header.getValue().getServiceType().getVersion(), 1);
        assertEquals(header.getValue().getActionName(), "MyAction");
        assertEquals(header.getString(), "\"urn:schemas-upnp-org:service:MyServiceType:1#MyAction\"");
    }

    @Test
    public void parseSoapActionHeaderQueryString() {
        SoapActionHeader header = new SoapActionHeader();
        header.setString("\"urn:schemas-upnp-org:control-1-0#QueryStateVariable\"");
        assertEquals(header.getValue().getServiceType(), null);
        assertEquals(header.getValue().getType(), "control-1-0");
        assertEquals(header.getValue().getVersion(), null);
        assertEquals(header.getValue().getActionName(), "QueryStateVariable");
        assertEquals(header.getString(), "\"urn:schemas-upnp-org:control-1-0#QueryStateVariable\"");
    }

    @Test
    public void parseEventSequenceHeaderString() {
        EventSequenceHeader header = new EventSequenceHeader();
        header.setString("0");
        assertEquals(header.getValue().getValue(), Long.valueOf(0));
        header.setString("001");
        assertEquals(header.getValue().getValue(), Long.valueOf(1));
        header.setString("123");
        assertEquals(header.getValue().getValue(), Long.valueOf(123));

    }

    @Test
    public void parseTimeoutHeaderString() {
        TimeoutHeader header = new TimeoutHeader();
        header.setString("Second-123");
        assertEquals(header.getValue(), Integer.valueOf(123));
        header.setString("Second-infinite");
        assertEquals(header.getValue(), TimeoutHeader.INFINITE_VALUE);
    }

    @Test
    public void parseCallbackHeaderString() {
        CallbackHeader header = new CallbackHeader();
        header.setString("<http://127.0.0.1/foo>");
        assertEquals(header.getValue().size(), 1);
        assertEquals(header.getValue().get(0).toString(), "http://127.0.0.1/foo");

        header.setString("<http://127.0.0.1/foo><http://127.0.0.1/bar>");
        assertEquals(header.getValue().size(), 2);
        assertEquals(header.getValue().get(0).toString(), "http://127.0.0.1/foo");
        assertEquals(header.getValue().get(1).toString(), "http://127.0.0.1/bar");

        header.setString("<http://127.0.0.1/foo> <http://127.0.0.1/bar>");
        assertEquals(header.getValue().size(), 2);
        assertEquals(header.getValue().get(0).toString(), "http://127.0.0.1/foo");
        assertEquals(header.getValue().get(1).toString(), "http://127.0.0.1/bar");
    }

    @Test
    public void parseSubscriptionIdHeaderString() {
        SubscriptionIdHeader header = new SubscriptionIdHeader();
        header.setString("uuid:123-123-123-123");
        assertEquals(header.getValue(), "uuid:123-123-123-123");
    }

    @Test(expectedExceptions = InvalidHeaderException.class)
    public void parseInvalidSubscriptionIdHeaderString() {
        SubscriptionIdHeader header = new SubscriptionIdHeader();
        header.setString("abc:123-123-123-123");
    }

    @Test
    public void parseInterfaceMacAddress() {
        InterfaceMacHeader header = new InterfaceMacHeader("00:17:ab:e9:65:a0");
        assertEquals(header.getValue().length, 6);
        assertEquals(header.getString().toUpperCase(), "00:17:AB:E9:65:A0");
    }

}
