/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/store/org/apache/slide/store/tamino/datastore/search/XResourceImpl.java,v 1.5 2004/09/15 14:58:25 pnever Exp $
 * $Revision: 1.5 $
 * $Date: 2004/09/15 14:58:25 $
 *
 * ====================================================================
 *
 * Copyright 1999-2004 The Apache Software Foundation.
 *
 * Licensed 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
 *
 *     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.apache.slide.store.tamino.datastore.search;

import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.slide.common.SlideException;
import org.apache.slide.content.NodeProperty;
import org.apache.slide.search.CompareHint;
import org.apache.slide.search.SearchException;
import org.apache.slide.search.basic.ComparableResource;
import org.apache.slide.search.basic.ComparableResourceImpl;
import org.apache.slide.search.basic.IBasicQuery;
import org.apache.slide.store.tamino.common.XGlobals;
import org.apache.slide.store.tamino.datastore.XContentId;
import org.apache.slide.structure.ObjectNode;
import org.apache.slide.util.XMLValue;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import org.jdom.xpath.XPath;

/**
 * Represents one result set object. This implementation deals with the xdav
 * specific properties of a requested Resource (xPath).
 *
 * @author <a href="mailto:martin.wallmer@softwareag.com">Martin Wallmer</a>
 * @version $Revision: 1.5 $
 */
public class XResourceImpl extends ComparableResourceImpl {

    //    private XMLOutputter outputter = new XMLOutputter ("  ", true, "UTF-8");

    private Namespace inoNamespace =
        Namespace.getNamespace (XGlobals.TAMINO_TSD_2_NAMESPACE_PREFIX,
                                XGlobals.TAMINO_TSD_2_NAMESPACE_URI);

    private IContentQueryFilter contentQueryFilter;

    /**
     * Constructs a RequestedResource.
     *
     * @param objectNode        the ObjectNode for this resource
     *
     * @throws BadQueryException
     */
    public XResourceImpl (ObjectNode objectNode, IBasicQuery query)
        throws SlideException {
        this (objectNode, query, null);
    }

    /**
     * Constructor
     *
     * @param    objectNode          an ObjectNode
     * @param    query               an IBasicQuery
     * @param    filter              an IContentQueryFilter
     *
     * @throws   SlideException
     *
     */
    public XResourceImpl (ObjectNode objectNode,
                          IBasicQuery query,
                          IContentQueryFilter filter)
        throws SlideException {
        super (objectNode,
               query.getSearchToken().getSlideToken(),
               query.getSearchToken().getContentHelper(),
               query.getScope(),
               query.getPropertyProvider());

        XRequestedPropertiesImpl reqProp =
            (XRequestedPropertiesImpl)query.requestedProperties();

        contentQueryFilter = filter;

        if (reqProp.hasXPathProperties()) {
            Iterator it = reqProp.xPathIterator();

            NodeProperty nodeProp = getProperty (XGlobals.CONTENT_ID, XGlobals.TAMINO_NAMESPACE_URI);

            if (nodeProp != null) {
                XContentId contId = new XContentId ((String)nodeProp.getValue());
                getXpathProperty(it, contId);
            }
        }
    }

    /**
     * step thru all requested xpath expressions of this query
     *
     * @param    it                  an Iterator over all reqXPathProperties
     * @param    contId              the contentID of this resource
     *
     * @throws   SearchException
     * @throws   IllegalArgumentException
     *
     */
    private void getXpathProperty (Iterator it, XContentId contId)
        throws SearchException, IllegalArgumentException {
        while (it.hasNext()) {
            Element resultWrapper = null;
            XPathProperty xProp = (XPathProperty)it.next();
            XPathContentQuery contentQuery = xProp.getXPathContentQuery();

            if (contentQueryFilter != null)
                contentQuery.setFilter (contentQueryFilter);

            boolean isUnwrap = xProp.isUnwrap();

            XMLValue xmlValue = new XMLValue();

            String queryName = contentQuery.getQueryName().getName();
            String queryNameSpace = contentQuery.getQueryName().getNamespace();

            Element e = contentQuery.getResult (contId);
            if (e != null) {
                resultWrapper = contentQuery.getResult (contId);
            }

            if (isUnwrap) {
                if (resultWrapper != null) {
                    addResultElements (xmlValue, resultWrapper);
                }
            }
            else {
                Element xqlQuery  = new Element (XLiterals.XQL, "xql",
                                                 XLiterals.XQL_NAMESPACE);

                if (resultWrapper == null)
                    resultWrapper = new Element("result");

                resultWrapper.setName ("result");
                resultWrapper.setNamespace
                    (Namespace.getNamespace ("xql", XLiterals.XQL_NAMESPACE));

                xqlQuery.addContent (contentQuery.getXPathString());

                xmlValue.add (xqlQuery);
                xmlValue.add (resultWrapper);
            }

            NodeProperty resultProperty = new NodeProperty
                (queryName, xmlValue, queryNameSpace);

            revisionDescriptor.setProperty (resultProperty);
        }
    }

    /**
     * Method addResultElements
     *
     * @param    xmlValue            a  XMLValue
     * @param    resultWrapper       an Element
     *
     */
    private void addResultElements (XMLValue xmlValue,
                                    Element resultWrapper) {

        Iterator it = resultWrapper.getChildren ().iterator();
        while (it.hasNext()) {
            Element e = (Element)it.next();
            Element result = (Element)e.clone();
            result.removeAttribute ("id", inoNamespace);
            xmlValue.add (result);
        }
    }
    
    /**
     * compares two RequestedResources according to OrderByHint. NULL values are
     * always considered as lessThan. (see [DASL] 5.6). Called in orderBy context
     * May only return 0 if the URIs are equal.
     * If both properties are XML valued and this value was created by an xpath
     * expression, do the specialised handling, else call the super method.
     *
     * @param    otherResource       a  RequestedResource
     * @param    hint                an OrderByHint
     *
     * @return   an int
     *
     */
    public int compareTo (ComparableResource otherResource, CompareHint hint) {
        int result = 0;

        if (getInternalHref().equals (otherResource.getInternalHref()))
            return 0;
        
        Comparable otherValue = (Comparable)otherResource.getThisValue (hint.getPropName(), hint.getPropNamespace());
        Comparable thisValue  = (Comparable) getThisValue (hint.getPropName(), hint.getPropNamespace());
        
        if ((hint instanceof XCompareHint) && (((XCompareHint)hint).getSortBy() != null) &&
                (otherValue instanceof XMLValue) && (thisValue instanceof XMLValue)) {
            if (thisValue != null && otherValue != null) {
                result = compareXpathDrivenValues((XMLValue)thisValue, (XMLValue)otherValue, (XCompareHint)hint);
            }
            else if (thisValue == null)
                result = -1;
                
            else if (otherValue == null)
                result = 1;
        }
        else {
            return super.compareTo(otherResource, hint);
    
        }

        if (hint.isAscending() == false)
            result = result * -1;
        
        return result;
    }
    
    public int compareXpathDrivenValues(XMLValue thisResource, XMLValue otherResource, XCompareHint hint) {
        int result = 0;
        
        try {
//          System.out.println("\n====================================");
            List thisList = getTaminoValue(thisResource, hint);
//          System.out.println("====================================");
            List otherList = getTaminoValue(otherResource, hint);
//          System.out.println("\n====================================");
    
            int thisSize  = thisList.size();
            int otherSize = otherList.size();
    
            // compare based on the size of the lists
            if (thisSize != otherSize) {
                return new Integer(thisSize).compareTo(new Integer(otherSize));
            }
            
            
            for( int i=0; i < thisSize; i++ ) {
                String thisString = (String)thisList.get(i);
                String thatString = (String)otherList.get(i);
    
                if (!thisString.equals(thatString)) {
                    //System.out.println(thisString + getOperator(thisString.compareTo(thatString)) + thatString );
                    return thisString.compareTo(thatString);
                }
            }
            
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    
    return result;

    }
    
    private String getOperator(int value) {
        if (value < 0) {
            return " < ";
        } else if (value > 0) {
            return " > ";
        } else{
            return " = ";
        }
    }
    
    
    
    


    /**
     * @param thisResource
     * @param hint
     * @return
     * @throws JDOMException
     */
    private static final List EMPTY_LIST = new ArrayList();
    private List getTaminoValue(XMLValue thisResource, XCompareHint hint) throws JDOMException {
        
        int theSize = thisResource.getList().size();
//      System.out.println("Element size = " + theSize);
        if (theSize == 2) {
//          System.out.println("Element \n" + getElementAsString((Element)(thisResource.getList().get(0))));
//          System.out.println("Element \n" + getElementAsString((Element)(thisResource.getList().get(1))));
            List thisList  = XPath.newInstance("string(//"+hint.getSortBy()+")").selectNodes(getElementAsElement((Element)(thisResource.getList().get(1))));
//          System.out.println("Result = " + thisList.get(0));
            return thisList;
        } else if (theSize == 1) {
//          System.out.println("Element \n" + getElementAsString((Element)(thisResource.getList().get(0))));
            List thisList  = XPath.newInstance("string(//"+hint.getSortBy()+")").selectNodes(getElementAsElement((Element)(thisResource.getList().get(0))));
//          System.out.println("Result = " + thisList.get(0));
            return thisList;
        } else {
            return EMPTY_LIST;
        }
    }

    /**
     * @throws IOException
     */
    private String getElementAsString(Element e)  {
        StringWriter writer = new StringWriter();

        XMLOutputter o2 = new XMLOutputter(Format.getRawFormat());
        try {
            o2.output( e, writer );
        }
        catch (Exception exception) {
            exception.printStackTrace();
        }
        
        return writer.toString();

        }
    
    private Element getElementAsElement(Element e)  {
        return getStringAsElement(getElementAsString(e));
        }

private Element getStringAsElement(String s)  {
    Element e = null;
    try {
        SAXBuilder b = new SAXBuilder();
        Document d = b.build( new StringReader(s) );
        e = d.getRootElement();
        
    } catch (Exception ex) {
        ex.printStackTrace();
    }

    return e;
    }
}


