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

import org.teleal.cling.support.contentdirectory.descriptor.DIDLParser;
import org.teleal.cling.support.contentdirectory.descriptor.dc.Creator;
import org.teleal.cling.support.contentdirectory.descriptor.didl.Container;
import org.teleal.cling.support.contentdirectory.descriptor.didl.DIDLLite;
import org.teleal.cling.support.contentdirectory.descriptor.didl.Item;
import org.teleal.cling.support.contentdirectory.descriptor.didl.Res;
import org.teleal.cling.support.contentdirectory.descriptor.upnp.CreateClass;
import org.teleal.cling.support.contentdirectory.descriptor.upnp.LongDescription;
import org.teleal.cling.support.contentdirectory.descriptor.upnp.SearchClass;
import org.teleal.cling.support.contentdirectory.descriptor.upnp.StorageUsed;
import org.teleal.cling.support.contentdirectory.descriptor.upnp.WriteStatus;
import org.teleal.cling.support.contentdirectory.descriptor.upnp.WriteStatusValue;
import org.testng.annotations.Test;

import java.io.StringWriter;
import java.math.BigInteger;
import java.util.List;

import static org.testng.Assert.assertEquals;

/**
 *
 */
public class DidlParserTest {

    protected DIDLParser parser = new DIDLParser();

    @Test
    public void browseRoot() throws Exception {

        DIDLLite didl = parser.readResource("org/teleal/cling/test/support/contentdirectory/samples/browseRoot.xml");
        assertSampleRoot(didl);

        StringWriter writer = new StringWriter(100);
        parser.createMarshaller().marshal(didl, writer);

        parser.debugXML(writer);

        didl = parser.readWriter(writer);
        assertSampleRoot(didl);
    }

    @Test
    public void browseRootChildren() throws Exception {

        DIDLLite didl = parser.readResource("org/teleal/cling/test/support/contentdirectory/samples/browseRootChildren.xml");
        assertSampleRootChildren(didl);

        StringWriter writer = new StringWriter(100);
        parser.createMarshaller().marshal(didl, writer);

        parser.debugXML(writer);

        didl = parser.readWriter(writer);
        assertSampleRootChildren(didl);
    }

    @Test
    public void browseFolder() throws Exception {

        DIDLLite didl = parser.readResource("org/teleal/cling/test/support/contentdirectory/samples/browseFolder.xml");
        assertSampleFolder(didl);

        StringWriter writer = new StringWriter(100);
        parser.createMarshaller().marshal(didl, writer);

        parser.debugXML(writer);

        didl = parser.readWriter(writer);
        assertSampleFolder(didl);
    }

    @Test
    public void browseItems() throws Exception {

        DIDLLite didl = parser.readResource("org/teleal/cling/test/support/contentdirectory/samples/browseItems.xml");
        assertSampleItems(didl);

        StringWriter writer = new StringWriter(100);
        parser.createMarshaller().marshal(didl, writer);

        parser.debugXML(writer);

        didl = parser.readWriter(writer);
        assertSampleItems(didl);
    }

    protected void assertSampleRoot(DIDLLite didl) {

        assert DIDLParser.hasContainers(didl);
        List<Container> containers = DIDLParser.getChildren(didl, Container.class);
        assertEquals(containers.size(), 1);

        Container rootContainer = containers.get(0);

        assertEquals(rootContainer.getId(), "0");
        assertEquals(rootContainer.getParentID(), "-1");
        assertEquals(rootContainer.getChildCount(), new Integer(3));
        assertEquals(rootContainer.isRestricted(), true);
        assertEquals(rootContainer.isSearchable(), Boolean.TRUE);
        assertEquals(rootContainer.getTitle().getValue(), "My multimedia stuff");

        Creator creator = DIDLParser.getFirstChild(rootContainer, Creator.class);
        assertEquals(creator.getValue(), "John Doe");

        LongDescription longDescription = DIDLParser.getFirstChild(rootContainer, LongDescription.class);
        assertEquals(longDescription.getValue(), "This is a long description!");

        org.teleal.cling.support.contentdirectory.descriptor.upnp.Class upnpClass = DIDLParser.getFirstChild(rootContainer, org.teleal.cling.support.contentdirectory.descriptor.upnp.Class.class);
        assertEquals(upnpClass.getName(), "Folder");
        assertEquals(upnpClass.getValue(), "object.container.storageFolder");

        StorageUsed storagedUsed = DIDLParser.getFirstChild(rootContainer, StorageUsed.class);
        assertEquals(storagedUsed.getValue(), new Long(907000));

        WriteStatus writeStatus = DIDLParser.getFirstChild(rootContainer, WriteStatus.class);
        assertEquals(writeStatus.getValue(), WriteStatusValue.WRITABLE);

        List<SearchClass> searchClasses = DIDLParser.getChildren(rootContainer, SearchClass.class);
        assertEquals(searchClasses.size(), 5);

        assertEquals(searchClasses.get(0).isIncludeDerived(), false);
        assertEquals(searchClasses.get(0).getValue(), "object.container.album.musicAlbum");

        assertEquals(searchClasses.get(1).isIncludeDerived(), false);
        assertEquals(searchClasses.get(1).getValue(), "object.container.album.photoAlbum");

        assertEquals(searchClasses.get(2).isIncludeDerived(), false);
        assertEquals(searchClasses.get(2).getValue(), "object.item.audioItem.musicTrack");

        assertEquals(searchClasses.get(3).isIncludeDerived(), false);
        assertEquals(searchClasses.get(3).getValue(), "object.item.imageItem.photo");

        assertEquals(searchClasses.get(4).isIncludeDerived(), true);
        assertEquals(searchClasses.get(4).getValue(), "object.item.imageItem.photo.vendorAlbumArt");
        assertEquals(searchClasses.get(4).getName(), "Vendor Album Art");

    }

    protected void assertSampleRootChildren(DIDLLite didl) {

        assert DIDLParser.hasContainers(didl);
        List<Container> containers = DIDLParser.getChildren(didl, Container.class);
        assertEquals(containers.size(), 3);

        Container myMusicContainer = containers.get(0);
        assertEquals(myMusicContainer.getId(), "1");
        assertEquals(myMusicContainer.getParentID(), "0");
        assertEquals(myMusicContainer.getChildCount(), new Integer(2));
        assertEquals(myMusicContainer.isRestricted(), false);
        assert myMusicContainer.isSearchable() == null;
        assertEquals(myMusicContainer.getTitle().getValue(), "My Music");
        org.teleal.cling.support.contentdirectory.descriptor.upnp.Class upnpClass = DIDLParser.getFirstChild(myMusicContainer, org.teleal.cling.support.contentdirectory.descriptor.upnp.Class.class);
        assertEquals(upnpClass.getValue(), "object.container.storageFolder");
        StorageUsed storagedUsed = DIDLParser.getFirstChild(myMusicContainer, StorageUsed.class);
        assertEquals(storagedUsed.getValue(), new Long(730000));
        WriteStatus writeStatus = DIDLParser.getFirstChild(myMusicContainer, WriteStatus.class);
        assertEquals(writeStatus.getValue(), WriteStatusValue.WRITABLE);
        List<SearchClass> searchClasses = DIDLParser.getChildren(myMusicContainer, SearchClass.class);
        assertEquals(searchClasses.size(), 2);
        assertEquals(searchClasses.get(0).isIncludeDerived(), false);
        assertEquals(searchClasses.get(0).getValue(), "object.container.album.musicAlbum");
        assertEquals(searchClasses.get(1).isIncludeDerived(), false);
        assertEquals(searchClasses.get(1).getValue(), "object.item.audioItem.musicTrack");
        List<CreateClass> createClasses = DIDLParser.getChildren(myMusicContainer, CreateClass.class);
        assertEquals(createClasses.size(), 1);
        assertEquals(createClasses.get(0).isIncludeDerived(), false);
        assertEquals(createClasses.get(0).getValue(), "object.container.album.musicAlbum");

        Container myPhotosContainer = containers.get(1);
        assertEquals(myPhotosContainer.getId(), "2");
        assertEquals(myPhotosContainer.getParentID(), "0");
        assertEquals(myPhotosContainer.getChildCount(), new Integer(2));
        assertEquals(myPhotosContainer.isRestricted(), false);
        assert myPhotosContainer.isSearchable() == null;
        assertEquals(myPhotosContainer.getTitle().getValue(), "My Photos");
        upnpClass = DIDLParser.getFirstChild(myPhotosContainer, org.teleal.cling.support.contentdirectory.descriptor.upnp.Class.class);
        assertEquals(upnpClass.getValue(), "object.container.storageFolder");
        storagedUsed = DIDLParser.getFirstChild(myPhotosContainer, StorageUsed.class);
        assertEquals(storagedUsed.getValue(), new Long(177000));
        writeStatus = DIDLParser.getFirstChild(myPhotosContainer, WriteStatus.class);
        assertEquals(writeStatus.getValue(), WriteStatusValue.WRITABLE);
        searchClasses = DIDLParser.getChildren(myPhotosContainer, SearchClass.class);
        assertEquals(searchClasses.size(), 2);
        assertEquals(searchClasses.get(0).isIncludeDerived(), false);
        assertEquals(searchClasses.get(0).getValue(), "object.container.album.photoAlbum");
        assertEquals(searchClasses.get(1).isIncludeDerived(), false);
        assertEquals(searchClasses.get(1).getValue(), "object.item.imageItem.photo");
        createClasses = DIDLParser.getChildren(myPhotosContainer, CreateClass.class);
        assertEquals(createClasses.size(), 1);
        assertEquals(createClasses.get(0).isIncludeDerived(), false);
        assertEquals(createClasses.get(0).getValue(), "object.container.album.photoAlbum");        

        Container albumArtContainer = containers.get(2);
        assertEquals(albumArtContainer.getId(), "3");
        assertEquals(albumArtContainer.getParentID(), "0");
        assertEquals(albumArtContainer.getChildCount(), new Integer(2));
        assertEquals(albumArtContainer.isRestricted(), false);
        assert albumArtContainer.isSearchable() == null;
        assertEquals(albumArtContainer.getTitle().getValue(), "Album Art");
        upnpClass = DIDLParser.getFirstChild(albumArtContainer, org.teleal.cling.support.contentdirectory.descriptor.upnp.Class.class);
        assertEquals(upnpClass.getValue(), "object.container.storageFolder");
        storagedUsed = DIDLParser.getFirstChild(albumArtContainer, StorageUsed.class);
        assertEquals(storagedUsed.getValue(), new Long(40000));
        writeStatus = DIDLParser.getFirstChild(albumArtContainer, WriteStatus.class);
        assertEquals(writeStatus.getValue(), WriteStatusValue.WRITABLE);
        searchClasses = DIDLParser.getChildren(albumArtContainer, SearchClass.class);
        assertEquals(searchClasses.size(), 1);
        assertEquals(searchClasses.get(0).isIncludeDerived(), true);
        assertEquals(searchClasses.get(0).getValue(), "object.item.imageItem.photo.vendorAlbumArt");
        assertEquals(searchClasses.get(0).getName(), "Vendor Album Art");
        createClasses = DIDLParser.getChildren(albumArtContainer, CreateClass.class);
        assertEquals(createClasses.size(), 1);
        assertEquals(createClasses.get(0).isIncludeDerived(), true);
        assertEquals(createClasses.get(0).getValue(), "object.item.imageItem.photo.vendorAlbumArt");        
    }

    protected void assertSampleFolder(DIDLLite didl) {

        assert DIDLParser.hasContainers(didl);
        List<Container> containers = DIDLParser.getChildren(didl, Container.class);
        assertEquals(containers.size(), 2);

        Container brandNewDayContainer = containers.get(0);
        assertEquals(brandNewDayContainer.getId(), "4");
        assertEquals(brandNewDayContainer.getParentID(), "1");
        assertEquals(brandNewDayContainer.getChildCount(), new Integer(3));
        assertEquals(brandNewDayContainer.isRestricted(), false);
        assert brandNewDayContainer.isSearchable() == null;
        assertEquals(brandNewDayContainer.getTitle().getValue(), "Brand New Day");
        Creator creator = DIDLParser.getFirstChild(brandNewDayContainer, Creator.class);
        assertEquals(creator.getValue(), "Sting");
        org.teleal.cling.support.contentdirectory.descriptor.upnp.Class upnpClass = DIDLParser.getFirstChild(brandNewDayContainer, org.teleal.cling.support.contentdirectory.descriptor.upnp.Class.class);
        assertEquals(upnpClass.getValue(), "object.container.album.musicAlbum");
        List<SearchClass> searchClasses = DIDLParser.getChildren(brandNewDayContainer, SearchClass.class);
        assertEquals(searchClasses.size(), 1);
        assertEquals(searchClasses.get(0).isIncludeDerived(), false);
        assertEquals(searchClasses.get(0).getValue(), "object.item.audioItem.musicTrack");


        Container singlesSoundtrackContainer = containers.get(1);
        assertEquals(singlesSoundtrackContainer.getId(), "5");
        assertEquals(singlesSoundtrackContainer.getParentID(), "1");
        assertEquals(singlesSoundtrackContainer.getChildCount(), new Integer(4));
        assertEquals(singlesSoundtrackContainer.isRestricted(), false);
        assert singlesSoundtrackContainer.isSearchable() == null;
        assertEquals(singlesSoundtrackContainer.getTitle().getValue(), "Singles Soundtrack");
        creator = DIDLParser.getFirstChild(singlesSoundtrackContainer, Creator.class);
        assertEquals(creator.getValue(), "Various Artists");
        upnpClass = DIDLParser.getFirstChild(singlesSoundtrackContainer, org.teleal.cling.support.contentdirectory.descriptor.upnp.Class.class);
        assertEquals(upnpClass.getValue(), "object.container.album.musicAlbum");
        searchClasses = DIDLParser.getChildren(singlesSoundtrackContainer, SearchClass.class);
        assertEquals(searchClasses.size(), 1);
        assertEquals(searchClasses.get(0).isIncludeDerived(), false);
        assertEquals(searchClasses.get(0).getValue(), "object.item.audioItem.musicTrack");
    }

    protected void assertSampleItems(DIDLLite didl) {

        assert !DIDLParser.hasContainers(didl);
        assert DIDLParser.hasItems(didl);
        List<Item> items = DIDLParser.getChildren(didl, Item.class);
        assertEquals(items.size(), 4);

        Item itemOne = items.get(0);
        assertEquals(itemOne.getId(), "6");
        assertEquals(itemOne.getParentID(), "3");
        assert !itemOne.isRestricted();
        assertEquals(itemOne.getTitle().getValue(), "Chloe Dancer");
        Creator creator = DIDLParser.getFirstChild(itemOne, Creator.class);
        assertEquals(creator.getValue(), "Mother Love Bone");
        org.teleal.cling.support.contentdirectory.descriptor.upnp.Class upnpClass = DIDLParser.getFirstChild(itemOne, org.teleal.cling.support.contentdirectory.descriptor.upnp.Class.class);
        assertEquals(upnpClass.getValue(), "object.item.audioItem.musicTrack");
        Res resource = DIDLParser.getFirstChild(itemOne, Res.class);
        assertEquals(resource.getProtocolInfo(), "http-get:*:audio/x-ms-wma:*");
        assertEquals(resource.getSize(), new BigInteger("200000"));
        assertEquals(resource.getValue(), "http://10.0.0.1/getcontent.asp?id=6");

        Item itemTwo = items.get(1);
        assertEquals(itemTwo.getId(), "7");
        assertEquals(itemTwo.getParentID(), "3");
        assert !itemTwo.isRestricted();
        assertEquals(itemTwo.getTitle().getValue(), "Drown");
        creator = DIDLParser.getFirstChild(itemTwo, Creator.class);
        assertEquals(creator.getValue(), "Smashing Pumpkins");
        upnpClass = DIDLParser.getFirstChild(itemTwo, org.teleal.cling.support.contentdirectory.descriptor.upnp.Class.class);
        assertEquals(upnpClass.getValue(), "object.item.audioItem.musicTrack");
        resource = DIDLParser.getFirstChild(itemTwo, Res.class);
        assertEquals(resource.getProtocolInfo(), "http-get:*:audio/mpeg:*");
        assertEquals(resource.getSize(), new BigInteger("140000"));
        assertEquals(resource.getValue(), "http://10.0.0.1/getcontent.asp?id=7");

        Item itemThree = items.get(2);
        assertEquals(itemThree.getId(), "8");
        assertEquals(itemThree.getParentID(), "3");
        assert !itemThree.isRestricted();
        assertEquals(itemThree.getTitle().getValue(), "State Of Love And Trust");
        creator = DIDLParser.getFirstChild(itemThree, Creator.class);
        assert creator == null;
        upnpClass = DIDLParser.getFirstChild(itemThree, org.teleal.cling.support.contentdirectory.descriptor.upnp.Class.class);
        assertEquals(upnpClass.getValue(), "object.item.audioItem.musicTrack");
        resource = DIDLParser.getFirstChild(itemThree, Res.class);
        assertEquals(resource.getProtocolInfo(), "http-get:*:audio/x-ms-wma:*");
        assertEquals(resource.getSize(), new BigInteger("70000"));
        assertEquals(resource.getValue(), "http://10.0.0.1/getcontent.asp?id=8");

        Item itemFour = items.get(3);
        assertEquals(itemFour.getId(), "9");
        assertEquals(itemFour.getParentID(), "3");
        assertEquals(itemFour.getRefID(), "8");
        assert !itemFour.isRestricted();
        assertEquals(itemFour.getTitle().getValue(), "State Of Love And Trust");
        creator = DIDLParser.getFirstChild(itemFour, Creator.class);
        assertEquals(creator.getValue(), "Pearl Jam");

    }

}
