/**
 * Licensed to Apereo under one or more contributor license
 * agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership.
 * Apereo licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file
 * except in compliance with the License.  You may obtain a
 * copy of the License at the following location:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.jasig.portal.layout;

import java.util.Collection;
import java.util.Deque;
import java.util.LinkedList;

import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jasig.portal.portlet.om.IPortletDefinition;
import org.jasig.portal.portlet.om.IPortletDefinitionParameter;
import org.jasig.portal.xml.stream.InjectingXMLEventReader;

/**
 * @author Eric Dalquist
 * @version $Revision$
 */
public class TransientUserLayoutXMLEventReader extends InjectingXMLEventReader {
    private static final XMLEventFactory EVENT_FACTORY = XMLEventFactory.newFactory();

    protected final Log logger = LogFactory.getLog(this.getClass());
    
    private final TransientUserLayoutManagerWrapper userLayoutManager;
    private final String rootFolderId;
    
    
    public TransientUserLayoutXMLEventReader(TransientUserLayoutManagerWrapper userLayoutManager,
            XMLEventReader wrappedReader) {
        super(wrappedReader);
        this.userLayoutManager = userLayoutManager;
        this.rootFolderId = this.userLayoutManager.getRootFolderId();
    }
    
    @Override
    protected Deque<XMLEvent> getAdditionalEvents(XMLEvent event) {
        if (event.isStartElement()) {
            final StartElement startElement = event.asStartElement();
            
            //All following logic requires an ID attribute, ignore any element without one
            final Attribute idAttribute = startElement.getAttributeByName(IUserLayoutManager.ID_ATTR_NAME);
            if (idAttribute == null) {
                return null;
            }
            
            //Handle adding a transient folder to the root element
            if (this.rootFolderId.equals(idAttribute.getValue())) {
                final QName name = startElement.getName();
                final String namespaceURI = name.getNamespaceURI();
                final String prefix = name.getPrefix();
                
                final Deque<XMLEvent> transientEventBuffer = new LinkedList<XMLEvent>();
                
                final Collection<Attribute> transientFolderAttributes = new LinkedList<Attribute>();
                transientFolderAttributes.add(EVENT_FACTORY.createAttribute("ID", TransientUserLayoutManagerWrapper.TRANSIENT_FOLDER_ID));
                transientFolderAttributes.add(EVENT_FACTORY.createAttribute("type", "regular"));
                transientFolderAttributes.add(EVENT_FACTORY.createAttribute("hidden", "true"));
                transientFolderAttributes.add(EVENT_FACTORY.createAttribute("unremovable", "true"));
                transientFolderAttributes.add(EVENT_FACTORY.createAttribute("immutable", "true"));
                transientFolderAttributes.add(EVENT_FACTORY.createAttribute("name", "Transient Folder"));
                
                final StartElement transientFolder = EVENT_FACTORY.createStartElement(prefix, namespaceURI, IUserLayoutManager.FOLDER, transientFolderAttributes.iterator(), null);
                transientEventBuffer.add(transientFolder);

                //append channel element iff subscribeId describes a transient channel, and not a regular layout channel
                final String subscribeId = this.userLayoutManager.getFocusedId();
                if (null != subscribeId && !subscribeId.equals("") && this.userLayoutManager.isTransientChannel(subscribeId)) {
                    IPortletDefinition chanDef = null;
                    try {
                        chanDef = this.userLayoutManager.getChannelDefinition(subscribeId);
                    }
                    catch (Exception e) {
                        this.logger.error("Could not obtain IChannelDefinition for subscribe id: " + subscribeId, e);
                    }
                    
                    if (chanDef != null) {
                        //TODO Move IChannelDefinition/IPortletDefinition -> StAX events code somewhere reusable
                        final Collection<Attribute> channelAttrs = new LinkedList<Attribute>();
                        channelAttrs.add(EVENT_FACTORY.createAttribute("ID", subscribeId));
                        channelAttrs.add(EVENT_FACTORY.createAttribute("typeID", Integer.toString(chanDef.getType().getId())));
                        channelAttrs.add(EVENT_FACTORY.createAttribute("hidden", "false"));
                        channelAttrs.add(EVENT_FACTORY.createAttribute("unremovable", "true"));
                        channelAttrs.add(EVENT_FACTORY.createAttribute("name", chanDef.getName()));
                        channelAttrs.add(EVENT_FACTORY.createAttribute("description", chanDef.getDescription()));
                        channelAttrs.add(EVENT_FACTORY.createAttribute("title", chanDef.getTitle()));
                        channelAttrs.add(EVENT_FACTORY.createAttribute("chanID", chanDef.getPortletDefinitionId().getStringId()));
                        channelAttrs.add(EVENT_FACTORY.createAttribute("fname", chanDef.getFName()));
                        channelAttrs.add(EVENT_FACTORY.createAttribute("timeout", Integer.toString(chanDef.getTimeout())));
                        channelAttrs.add(EVENT_FACTORY.createAttribute("transient", "true"));

                        final StartElement startChannel = EVENT_FACTORY.createStartElement(prefix, namespaceURI, IUserLayoutManager.CHANNEL, channelAttrs.iterator(), null);
                        transientEventBuffer.offer(startChannel);

                        // add channel parameter elements
                        for(final IPortletDefinitionParameter parm : chanDef.getParameters())
                        {
                            final Collection<Attribute> parameterAttrs = new LinkedList<Attribute>();
                            parameterAttrs.add(EVENT_FACTORY.createAttribute("name",parm.getName()));
                            parameterAttrs.add(EVENT_FACTORY.createAttribute("value",parm.getValue()));

                            final StartElement startParameter = EVENT_FACTORY.createStartElement(prefix, namespaceURI, IUserLayoutManager.PARAMETER, parameterAttrs.iterator(), null);
                            transientEventBuffer.offer(startParameter);
                            
                            final EndElement endParameter = EVENT_FACTORY.createEndElement(prefix, namespaceURI, IUserLayoutManager.PARAMETER, null);
                            transientEventBuffer.offer(endParameter);
                        }

                        final EndElement endChannel = EVENT_FACTORY.createEndElement(prefix, namespaceURI, IUserLayoutManager.CHANNEL, null);
                        transientEventBuffer.offer(endChannel);
                    }
                }
                
                final EndElement endFolder = EVENT_FACTORY.createEndElement(prefix, namespaceURI, IUserLayoutManager.FOLDER, null);
                transientEventBuffer.offer(endFolder);
                
                return transientEventBuffer;
            }
        }
        
        return null;
    }


    @Override
    protected XMLEvent getPeekEvent(XMLEvent event) {
        //Not the most efficient way of doing this since the whole deque is built but we only need the first element.
        final Deque<XMLEvent> additionalEvents = this.getAdditionalEvents(event);
        if (additionalEvents != null) {
            return additionalEvents.pop();
        }
        return null;
    }
}
