/**
 * Copyright (C) 2010 eXo Platform SAS.
 *
 * This 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 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software 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 software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.xcmis.restatom.collections;

import org.apache.abdera.i18n.iri.IRI;
import org.apache.abdera.model.Content;
import org.apache.abdera.model.Document;
import org.apache.abdera.model.Entry;
import org.apache.abdera.model.Feed;
import org.apache.abdera.model.Person;
import org.apache.abdera.protocol.server.RequestContext;
import org.apache.abdera.protocol.server.ResponseContext;
import org.apache.abdera.protocol.server.TargetType;
import org.apache.abdera.protocol.server.context.AbstractResponseContext;
import org.apache.abdera.protocol.server.context.BaseResponseContext;
import org.apache.abdera.protocol.server.context.EmptyResponseContext;
import org.apache.abdera.protocol.server.context.ResponseContextException;
import org.apache.abdera.protocol.server.impl.AbstractEntityCollectionAdapter;
import org.xcmis.restatom.AtomCMIS;
import org.xcmis.restatom.ProviderImpl;
import org.xcmis.spi.Connection;
import org.xcmis.spi.utils.Logger;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author <a href="mailto:andrey.parfonov@exoplatform.com">Andrey Parfonov</a>
 * @version $Id: AbstractCmisCollection.java 2634 2009-08-12 06:26:41Z andrew00x
 *          $
 */
public abstract class AbstractCmisCollection<T> extends AbstractEntityCollectionAdapter<T>
{

   /** The logger. */
   private static final Logger LOG = Logger.getLogger(AbstractCmisCollection.class);

   /** The Constant ANONYMOUS. */
   protected static final String ANONYMOUS = "anonymous";

   /** The Constant SYSTEM. */
   protected static final String SYSTEM = "system";

   /*protected*/private final Connection connection;

   public AbstractCmisCollection(Connection connection)
   {
      this.connection = connection;
   }

   /**
    * {@inheritDoc}
    */
   public void deleteEntry(String resourceName, RequestContext request) throws ResponseContextException
   {
      throw new UnsupportedOperationException();
   }

   /**
    * {@inheritDoc}
    */
   public Object getContent(T entry, RequestContext request) throws ResponseContextException
   {
      return null;
   }

   /**
    * {@inheritDoc}
    */
   public ResponseContext postEntry(RequestContext request)
   {
      throw new UnsupportedOperationException();
   }

   /**
    * {@inheritDoc}
    */
   public T postEntry(String title, IRI id, String summary, Date updated, List<Person> authors, Content content,
      RequestContext request) throws ResponseContextException
   {
      throw new UnsupportedOperationException();
   }

   /**
    * {@inheritDoc}
    */
   public void putEntry(T entry, String title, Date updated, List<Person> authors, String summary, Content content,
      RequestContext request) throws ResponseContextException
   {
      throw new UnsupportedOperationException();
   }

   /**
    * @param id id
    * @param feed feed to which are added links
    * @param atomdocType type of collections. See {@link ProviderImpl}.
    * @param maxItems max items in each response
    * @param skipCount number of skipped results from the begin of set
    * @param total total number items in result set. If total number is unknown
    *        then this parameter must be set as -1.
    * @param hasMore true if has more items in result set false otherwise
    * @param request request context
    */
   protected void addPageLinks(String id, Feed feed, String atomdocType, int maxItems, int skipCount, int total,
      boolean hasMore, RequestContext request)
   {
      Map<String, String> params = new HashMap<String, String>();
      params.put("repoid", getRepositoryId(request));
      params.put("atomdoctype", atomdocType);
      params.put("id", id);
      // First link
      params.put(AtomCMIS.PARAM_SKIP_COUNT, "0");
      params.put(AtomCMIS.PARAM_MAX_ITEMS, //
         Integer.toString((skipCount == 0) ? maxItems //
            : (maxItems < skipCount ? maxItems : skipCount) /* If started not from first page. */));
      feed.addLink(request.absoluteUrlFor("feed", params), AtomCMIS.LINK_FIRST, AtomCMIS.MEDIATYPE_ATOM_FEED, null,
         null, -1);
      // Previous link.
      if (skipCount > 0)
      {
         params.put(AtomCMIS.PARAM_MAX_ITEMS, Integer.toString(maxItems < skipCount ? maxItems : skipCount));
         params.put(AtomCMIS.PARAM_SKIP_COUNT, Integer.toString(maxItems < skipCount ? skipCount - maxItems : 0));
         feed.addLink(request.absoluteUrlFor("feed", params), AtomCMIS.LINK_PREVIOUS, AtomCMIS.MEDIATYPE_ATOM_FEED,
            null, null, -1);
      }
      if (hasMore)
      {
         // Next link.
         params.put(AtomCMIS.PARAM_SKIP_COUNT, Integer.toString(skipCount + maxItems));
         params.put(AtomCMIS.PARAM_MAX_ITEMS, Integer.toString(maxItems));
         // If has more items then provide next link.
         feed.addLink(request.absoluteUrlFor("feed", params), AtomCMIS.LINK_NEXT, AtomCMIS.MEDIATYPE_ATOM_FEED, null,
            null, -1);
         // Total link.
         if (total > 0)
         {
            // If total number result in set is unknown then unable to determine last page link.
            int pages = (total - skipCount) / maxItems;
            int rem = (total - skipCount) % maxItems;
            if (rem == 0)
            {
               skipCount = total - maxItems;
            }
            else if (pages != 0)
            {
               skipCount = skipCount + pages * maxItems;
            }
            params.put(AtomCMIS.PARAM_SKIP_COUNT, Integer.toString(skipCount));
            params.put(AtomCMIS.PARAM_MAX_ITEMS, Integer.toString(maxItems));
            feed.addLink(request.absoluteUrlFor("feed", params), AtomCMIS.LINK_LAST, AtomCMIS.MEDIATYPE_ATOM_FEED,
               null, null, -1);
         }
      }
   }

   /**
    * {@inheritDoc}
    */
   protected ResponseContext buildCreateEntryResponse(String link, Entry entry)
   {
      Document<Entry> doc = entry.getDocument();
      ResponseContext rc = new BaseResponseContext<Document<Entry>>(doc);
      rc.setLocation(link);
      rc.setContentLocation(rc.getLocation().toString());
      //      rc.setEntityTag(ProviderHelper.calculateEntityTag(entry));
      rc.setStatus(201);
      return rc;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   protected ResponseContext buildGetEntryResponse(RequestContext request, Entry entry) throws ResponseContextException
   {
      // The same as in super class but without ETag.
      Document<Entry> doc = entry.getDocument();
      ResponseContext rc = new BaseResponseContext<Document<Entry>>(doc);
      //      rc.setEntityTag(ProviderHelper.calculateEntityTag(entry));
      return rc;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   protected ResponseContext buildGetFeedResponse(Feed feed)
   {
      // The same as in super class but without ETag.
      Document<Feed> document = feed.getDocument();
      AbstractResponseContext rc = new BaseResponseContext<Document<Feed>>(document);
      //      rc.setEntityTag(calculateEntityTag(document.getRoot()));
      return rc;
   }

   /**
    * Creates the error response.
    *
    * @param msg the msg
    * @param status the status
    *
    * @return the response context
    */
   protected ResponseContext createErrorResponse(String msg, int status)
   {
      LOG.error(msg);
      return new EmptyResponseContext(status, msg);
   }

   /**
    * Creates the error response.
    *
    * @param t the t
    * @param status the status
    *
    * @return the response context
    */
   protected ResponseContext createErrorResponse(Throwable t, int status)
   {
      LOG.error(t.getMessage(), t);
      return new EmptyResponseContext(status, t.getMessage());
   }

   /**
    * Create link to object type description.
    *
    * @param id object type id
    * @param request request context
    * @return link to AtomPub Document that describes object type
    */
   protected String getObjectTypeLink(String id, RequestContext request)
   {
      Map<String, String> params = new HashMap<String, String>();
      params.put("repoid", getRepositoryId(request));
      params.put("atomdoctype", "typebyid");
      params.put("id", id);
      String type = request.absoluteUrlFor(TargetType.ENTRY, params);
      return type;
   }

   /**
    * Get id of CMIS repository.
    *
    * @param request RequestContext
    * @return repositoryId string
    */
   protected String getRepositoryId(RequestContext request)
   {
      String id = request.getTarget().getParameter("repoid").replace(" ", "%20");
      return id;
   }

   /**
    * Create link to AtomPub Service Document contains the set of repositories
    * that are available.
    *
    * @param request the request context
    * @return link to AtomPub Service Document
    */
   protected String getServiceLink(RequestContext request)
   {
      Map<String, String> p = new HashMap<String, String>();
      p.put("repoid", getRepositoryId(request));
      String service = request.absoluteUrlFor(TargetType.SERVICE, p);
      return service;
   }

   /**
    * To get Connection for provided repository Id within request.
    *
    * @param request the request context
    * @return the Connection to CMIS storage
    */
   protected Connection getConnection(RequestContext request)
   {
      return connection;
      //return CmisRegistry.getInstance().getConnection(getRepositoryId(request));
   }

   protected boolean getBooleanParameter(RequestContext request, String name, boolean defaultValue)
   {
      String param = request.getParameter(name);
      if (param != null && param.length() > 0)
      {
         return Boolean.parseBoolean(param);
      }
      else
      {
         return defaultValue;
      }
   }

   protected Integer getIntegerParameter(RequestContext request, String name, Integer defaultValue)
      throws ResponseContextException
   {
      Integer result;
      String param = request.getParameter(name);
      if (param != null && param.length() > 0)
      {
         try
         {
            result = new Integer(param);
         }
         catch (NumberFormatException nfe)
         {
            String msg = "Invalid parameter for name '" + name + "' with value: '" + name + "'";
            throw new ResponseContextException(msg, 400);
         }
      }
      else
      {
         result = defaultValue;
      }
      return result;
   }

}
