/*******************************************************************************
 * Copyright (c) 2013, 2015 Oracle. All rights reserved.
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
 * which accompanies this distribution.
 * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * Contributors:
 *     gonural - initial implementation
 *     2014-09-01-2.6.0 Dmitry Kornilov
 *       - JPARS 2.0 related changes
 ******************************************************************************/
package org.eclipse.persistence.jpa.rs.features.paging;

import java.util.List;
import java.util.Map;

import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import javax.xml.bind.JAXBElement;

import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.internal.jpa.rs.metadata.model.ItemLinks;
import org.eclipse.persistence.internal.queries.ReportItem;
import org.eclipse.persistence.internal.weaving.PersistenceWeavedRest;
import org.eclipse.persistence.jpa.rs.PersistenceContext;
import org.eclipse.persistence.jpa.rs.QueryParameters;
import org.eclipse.persistence.jpa.rs.features.FeatureResponseBuilderImpl;
import org.eclipse.persistence.jpa.rs.features.ItemLinksBuilder;
import org.eclipse.persistence.jpa.rs.util.HrefHelper;
import org.eclipse.persistence.jpa.rs.util.IdHelper;
import org.eclipse.persistence.jpa.rs.util.list.PageableCollection;
import org.eclipse.persistence.jpa.rs.util.list.ReadAllQueryResultCollection;
import org.eclipse.persistence.jpa.rs.util.list.ReportQueryResultCollection;
import org.eclipse.persistence.jpa.rs.util.list.ReportQueryResultListItem;

/**
 * FeatureResponseBuilder implementation used for pageable collections. Used in JPARS 2.0.
 *
 * @author gonural
 * @since EclipseLink 2.6.0.
 */
public class PagingResponseBuilder extends FeatureResponseBuilderImpl {

    /**
     * {@inheritDoc}
     */
    @Override
    public Object buildReadAllQueryResponse(PersistenceContext context, Map<String, Object> queryParams, List<Object> items, UriInfo uriInfo) {
        ReadAllQueryResultCollection response = new ReadAllQueryResultCollection();
        for (Object item : items) {
            response.addItem(populatePagedReadAllQueryItemLinks(context, item));
        }

        return populatePagedCollectionLinks(queryParams, uriInfo, response);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Object buildReportQueryResponse(PersistenceContext context, Map<String, Object> queryParams, List<Object[]> results, List<ReportItem> items, UriInfo uriInfo) {
        return populatePagedReportQueryCollectionLinks(queryParams, results, items, uriInfo);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Object buildAttributeResponse(PersistenceContext context, Map<String, Object> queryParams, String attribute, Object results, UriInfo uriInfo) {
        if (results instanceof List) {
            ReadAllQueryResultCollection response = new ReadAllQueryResultCollection();
            response.setItems((List<Object>) results);
            return populatePagedCollectionLinks(queryParams, uriInfo, response);
        }
        return results;
    }

    private Object populatePagedReadAllQueryItemLinks(PersistenceContext context, Object result) {
        // populate links for the entity
        ClassDescriptor descriptor = context.getJAXBDescriptorForClass(result.getClass());
        if ((result instanceof PersistenceWeavedRest) && (descriptor != null)) {
            final PersistenceWeavedRest entity = (PersistenceWeavedRest) result;
            final String href = HrefHelper.buildEntityHref(context, descriptor.getAlias(), IdHelper.stringifyId(result, descriptor.getAlias(), context));

            final ItemLinks itemLinks = (new ItemLinksBuilder())
                    .addSelf(href)
                    .addCanonical(href)
                    .build();

            entity._persistence_setLinks(itemLinks);
            return entity;
        }
        return result;
    }

    private PageableCollection populatePagedCollectionLinks(Map<String, Object> queryParams, UriInfo uriInfo, PageableCollection resultCollection) {
        // populate links for entire response
        final ItemLinksBuilder itemLinksBuilder = new ItemLinksBuilder();

        final int limit = Integer.parseInt((String) queryParams.get(QueryParameters.JPARS_PAGING_LIMIT));
        final int offset = Integer.parseInt((String) queryParams.get(QueryParameters.JPARS_PAGING_OFFSET));

        final UriBuilder uriBuilder = UriBuilder.fromUri(uriInfo.getRequestUri());

        if (resultCollection.getItems() != null) {
            final int actualCount = resultCollection.getItems().size();
            if (actualCount > limit) {
                // Remove the last item from collection. It was artificially added to indicate there are more records or not.
                resultCollection.getItems().remove(actualCount - 1);
                resultCollection.setCount(actualCount - 1);

                // next link
                // The uri might have other query/matrix parameters, just replace the limit and offset
                // for next and prev links and leave the rest untouched
                uriBuilder.replaceQueryParam(QueryParameters.JPARS_PAGING_OFFSET, String.valueOf(limit + offset));
                itemLinksBuilder.addNext(uriBuilder.build().toString());
                resultCollection.setHasMore(true);
            } else {
                resultCollection.setHasMore(false);
                resultCollection.setCount(actualCount);
            }
        } else {
            resultCollection.setCount(0);
        }

        if (offset != 0) {
            if (offset > limit) {
                uriBuilder.replaceQueryParam(QueryParameters.JPARS_PAGING_OFFSET, String.valueOf(offset - limit));
            } else {
                uriBuilder.replaceQueryParam(QueryParameters.JPARS_PAGING_OFFSET, "0");
            }

            if (resultCollection.getItems() != null && !resultCollection.getItems().isEmpty()) {
                itemLinksBuilder.addPrev(uriBuilder.build().toString());
            }
        }

        itemLinksBuilder.addSelf(uriInfo.getRequestUri().toString());

        resultCollection.setLinks(itemLinksBuilder.build().getLinks());
        resultCollection.setOffset(offset);
        resultCollection.setLimit(limit);

        return resultCollection;
    }

    /**
     * Populate paged report query collection links.
     *
     * @param queryParams the query params
     * @param results the results
     * @param reportItems the report items
     * @param uriInfo the uri info
     * @return the pageable collection
     */
    @SuppressWarnings({ "rawtypes" })
    private PageableCollection populatePagedReportQueryCollectionLinks(Map<String, Object> queryParams, List<Object[]> results, List<ReportItem> reportItems, UriInfo uriInfo) {
        ReportQueryResultCollection response = new ReportQueryResultCollection();
        for (Object result : results) {
            ReportQueryResultListItem queryResultListItem = new ReportQueryResultListItem();
            List<JAXBElement> jaxbFields = createShellJAXBElementList(reportItems, result);
            // We don't have a way of determining self links for the report query responses
            // so, no links array will be inserted into individual items in the response
            queryResultListItem.setFields(jaxbFields);
            response.addItem(queryResultListItem);
        }

        response.setCount(results.size());
        return populatePagedCollectionLinks(queryParams, uriInfo, response);
    }
}
