/**
 * Copyright (c) 2008-2009, Aberystwyth University
 *
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions 
 * are met:
 * 
 *  - Redistributions of source code must retain the above 
 *    copyright notice, this list of conditions and the 
 *    following disclaimer.
 *  
 *  - Redistributions in binary form must reproduce the above copyright 
 *    notice, this list of conditions and the following disclaimer in 
 *    the documentation and/or other materials provided with the 
 *    distribution.
 *    
 *  - Neither the name of the Centre for Advanced Software and 
 *    Intelligent Systems (CASIS) nor the names of its 
 *    contributors may be used to endorse or promote products derived 
 *    from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
 * SUCH DAMAGE.
 */
package org.purl.sword.base;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import java.util.Properties;
import nu.xom.Element;
import nu.xom.Elements;

import org.apache.log4j.Logger;
import org.purl.sword.atom.ContentType;
import org.purl.sword.atom.Title;

/**
 * Represents an Atom Publishing Protocol Workspace element. 
 * 
 * @author Neil Taylor
 */
public class Workspace extends XmlElement implements SwordElementInterface
{
   /**
    * The title for the workspace. 
    */
   private Title title; 

   /**
    * A list of collections associated with this workspace. 
    */
   private List<Collection> collections; 

   /**
    * The logger. 
    */
   private static Logger log = Logger.getLogger(Workspace.class);

   /**
    * Local name part of this element.
    */
   @Deprecated
   public static final String ELEMENT_NAME = "workspace";
   
   private static final XmlName XML_NAME = 
           new XmlName(Namespaces.PREFIX_APP, "workspace", Namespaces.NS_APP);
   
   /**
    * Create a new instance of the workspace, with no title. 
    */
   public Workspace( ) 
   {
      super(XML_NAME);
      initialise();
   }

   public static XmlName elementName()
    {
        return XML_NAME;
    }

   /**
    * Create a new instance of the workspace with the specified title. 
    * 
    * @param title The title. 
    */
   public Workspace( String title )
   {
      this();

      setTitle(title);
   }

   /**
    * Initialise the object, ready for use.
    */
   protected void initialise()
   {
       collections = new ArrayList<Collection>();
       title = null; 
   }

   /**
    * Set the title. The type for the title will be set to 
    * <code>ContentType.TEXT</code>
    * 
    * @param title The title. 
    */
   public void setTitle( String title )
   {
      if( this.title == null)
      {
         this.title = new Title();
      }
      this.title.setContent(title);
      this.title.setType(ContentType.TEXT);

   }

   /**
    * Get the content of the Title element. 
    * 
    * @return The title. 
    */
   public String getTitle( )
   {
      if( title == null ) 
      {
         return null;
      }

      return title.getContent(); 
   }

   /**
    * Add a collection to the Workspace. 
    * 
    * @param collection The collection. 
    */
   public void addCollection( Collection collection )
   {
      collections.add(collection);
   }

   /**
    * Get an Iterator over the collections. 
    * 
    * @return An iterator. 
    */
   public Iterator<Collection> collectionIterator( )
   {
      return collections.iterator();
   }

   /**
    * Get a list of the collections
    * 
    * @ return A list.
    */
   public List<Collection> getCollections( )
   {
      return collections;
   }

   /**
    * Marshall the data in this element to an Element. 
    * 
    * @return An element that contains the data in this object. 
    */
   public Element marshall( ) 
   {
      // convert data into XOM elements and return the 'root', i.e. the one 
      // that represents the collection. 
      Element workspace = new Element(xmlName.getQualifiedName(), xmlName.getNamespace());

      if( title != null )
      {
         workspace.appendChild(title.marshall());
      }
      
      for( Collection item : collections )
      {
         workspace.appendChild(item.marshall());
      }

      return workspace;   
   }

   /**
    * Unmarshall the workspace element into the data in this object. 
    * 
    * @throws UnmarshallException If the element does not contain a
    *                             workspace element or if there are problems
    *                             accessing the data. 
    */
   public void unmarshall( Element workspace )
   throws UnmarshallException 
   {
      unmarshall(workspace, null);
   }

   /**
    *
    * @param workspace
    * @param validate
    * @return
    * @throws org.purl.sword.base.UnmarshallException
    */
   public SwordValidationInfo unmarshall( Element workspace, Properties validationProperties )
   throws UnmarshallException
   {
      if( ! isInstanceOf(workspace, xmlName))
      {
         return handleIncorrectElement(workspace, validationProperties);
      }

      ArrayList<SwordValidationInfo> validationItems = new ArrayList<SwordValidationInfo>();

      try
      {
         initialise();

         // FIXME - process the attributes 

         // retrieve all of the sub-elements
         Elements elements = workspace.getChildElements();
         Element element = null;
         int length = elements.size();

         for(int i = 0; i < length; i++ )
         {
            element = elements.get(i);
            if( isInstanceOf(element, Title.elementName() ) )
            {
               if( title == null )
               {
                  title = new Title();
                  validationItems.add(title.unmarshall(element, validationProperties));
               }
               else
               {
                  SwordValidationInfo info =
                          new SwordValidationInfo(Title.elementName(),
                             SwordValidationInfo.DUPLICATE_ELEMENT,
                             SwordValidationInfoType.WARNING);
                  info.setContentDescription(element.getValue());
                  validationItems.add(info);
               }
            }
            else if( isInstanceOf(element, Collection.elementName() ))
            {
               Collection collection = new Collection( );
               validationItems.add(collection.unmarshall(element, validationProperties));
               collections.add(collection); 
            }
            else if( validationProperties != null )
            {
                validationItems.add(new SwordValidationInfo(new XmlName(element),
                        SwordValidationInfo.UNKNOWN_ELEMENT,
                        SwordValidationInfoType.INFO));
            }
         }
      }
      catch( Exception ex )
      {
         log.error("Unable to parse an element in workspace: " + ex.getMessage());
         throw new UnmarshallException("Unable to parse element in workspace.", ex);
      }

      SwordValidationInfo result = null;
      if( validationProperties != null )
      {
          result = validate(validationItems, validationProperties);
      }
      return result; 
   }

   /**
    * 
    * @return A validation object that specifies the status of this object.
    */
   @Override
   public SwordValidationInfo validate(Properties validationContext)
   {
       return validate(null, validationContext);
   }

   /**
    * 
    * @param existing
    * @return
    */
   protected SwordValidationInfo validate(ArrayList<SwordValidationInfo> existing,
           Properties validationContext)
   {
      boolean validateAll = (existing == null );

      SwordValidationInfo result = new SwordValidationInfo(xmlName);

      if( collections == null || collections.size() == 0 )
      {
          result.addValidationInfo(new SwordValidationInfo(Collection.elementName(),
                    SwordValidationInfo.MISSING_ELEMENT_WARNING,
                    SwordValidationInfoType.WARNING ));
      }

      if( validateAll )
      {
          if( title != null )
          {
              result.addValidationInfo(title.validate(validationContext));
          }

          if( collections.size() > 0 )
          {
             Iterator<Collection> iterator = collections.iterator();
             while( iterator.hasNext() )
             {
                 result.addValidationInfo(iterator.next().validate(validationContext));
             }
          }
      }

      result.addUnmarshallValidationInfo(existing, null);
      return result; 
   }


}
