/*
 * #%L
 * Alfresco Search Services
 * %%
 * Copyright (C) 2005 - 2020 Alfresco Software Limited
 * %%
 * This file is part of the Alfresco software. 
 * If the software was purchased under a paid Alfresco license, the terms of 
 * the paid license agreement will prevail.  Otherwise, the software is 
 * provided under the following open source license terms:
 * 
 * Alfresco 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 3 of the License, or
 * (at your option) any later version.
 * 
 * Alfresco 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 Alfresco. If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */

package org.alfresco.solr.component;

import java.io.IOException;
import java.util.Collection;
import java.util.Map;

import com.google.common.collect.BiMap;

import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.SimpleOrderedMap;
import org.apache.solr.handler.component.ResponseBuilder;
import org.apache.solr.handler.component.SearchComponent;

/**
 * @author Andy
 *
 */
public class RewriteFacetCountsComponent extends SearchComponent
{

    /* (non-Javadoc)
     * @see org.apache.solr.handler.component.SearchComponent#finishStage(org.apache.solr.handler.component.ResponseBuilder)
     */
    @Override
    public void finishStage(ResponseBuilder rb)
    {
        /// wait until after distributed faceting is done 
        if (!rb.doFacets || rb.stage != ResponseBuilder.STAGE_GET_FIELDS) 
        {
            return;
        }
        else 
        {
            process(rb);
        }
    }

    /* (non-Javadoc)
     * @see org.apache.solr.handler.component.SearchComponent#prepare(org.apache.solr.handler.component.ResponseBuilder)
     */
    @Override
    public void prepare(ResponseBuilder rb) throws IOException
    {
       // Nothing to do
    }

    /* (non-Javadoc)
     * @see org.apache.solr.handler.component.SearchComponent#process(org.apache.solr.handler.component.ResponseBuilder)
     */
    @Override
    public void process(ResponseBuilder rb)
    {
        // rewrite

        rewrite(rb, "_field_mappings_", "facet_counts", "facet_fields");
        rewrite(rb, "_date_mappings_", "facet_counts", "facet_dates");
        rewrite(rb, "_range_mappings_", "facet_counts", "facet_ranges");
        rewrite(rb, "_interval_mappings_", "facet_counts", "facet_intervals");

        rewrite(rb, "_pivot_mappings_", "facet_counts", "facet_pivot");
        rewritePivotFields(rb, "facet_counts", "facet_pivot");

        
        rewrite(rb, "_stats_field_mappings_", "stats", "stats_fields");
        
        copyAnalytics(rb, "facet_counts", "facet_fields");
        
        Map<String, String> mappings = (Map<String, String>) rb.rsp.getValues().get("_stats_field_mappings_");
        if(mappings != null)
        {
            for(String key : mappings.keySet())
            {
                rewrite(rb, "_stats_facet_mappings_", "stats", "stats_fields", key, "facets");
            }
        }
    }
    
    
    /**
     * @param rb
     */
    private void copyAnalytics(ResponseBuilder rb, String ... sections)
    {
        NamedList<Object>  found = (NamedList<Object>) rb.rsp.getValues();
        for(String section : sections)
        {
            found = (NamedList<Object>)found.get(section);
            if(found == null)
            {
                return;
            }
        }

        NamedList<Object>  analytics = (NamedList<Object>) rb.rsp.getValues();
        analytics =  (NamedList<Object>)analytics.get("analytics");
        if(analytics != null)
        {
            for(int i = 0; i < analytics.size(); i++)
            {
                String name = analytics.getName(i);
                Object value = analytics.getVal(i);
                found.add(name, value);
            }
        }
        
        



    }


    /**
     * Update pivot fields in the response to reference ACS properties rather than Solr fields.
     *
     * @param rb The response builder.
     * @param sections The names of the sections in the response to update.
     */
    private void rewritePivotFields(ResponseBuilder rb, String ... sections)
    {
        Map<String, String> mappings = (Map<String, String>) rb.rsp.getValues().get("_pivot_mappings_");
        if(mappings != null)
        {
            NamedList<Object>  found = (NamedList<Object>) rb.rsp.getValues();
            for(String section : sections)
            {
                found = (NamedList<Object>)found.get(section);
                if(found == null)
                {
                    return;
                }
            }
            for(int i = 0; i < found.size(); i++)
            {
                String pivotName = found.getName(i);
                String[] fromParts = pivotName.split(",");
                String mapping = mappings.get(pivotName);
                String[] toParts = mapping != null ? mapping.split(",") : fromParts;
                Collection<NamedList<Object>> current = (Collection<NamedList<Object>>)found.getVal(i);
                processPivot(rb, fromParts, toParts, current, 0);
            }
        }
        
    }
    
    private void processPivot(ResponseBuilder rb, String[] fromParts, String[] toParts, Collection<NamedList<Object>> current, int level)
    {
        for(NamedList<Object> entry : current)
        {
            for(int i = 0; i < entry.size(); i++)
            {
                String name = entry.getName(i);
                if(name.equals("field"))
                {
                    entry.setVal(i, fromParts[level].trim());
                }
                else if(name.equals("pivot"))
                {
                    Collection<NamedList<Object>> pivot = (Collection<NamedList<Object>>)entry.getVal(i);
                    processPivot(rb, fromParts, toParts, pivot, level+1);
                }
                else if(name.equals("ranges"))
                {
                	SimpleOrderedMap ranges = (SimpleOrderedMap)entry.getVal(i);
                	processRanges(rb, ranges);
                }
                else
                {
                    // leave alone
                }
            }
        }
    }

    /**
     * Update the returned ranges to reference ACS properties rather than Solr fields.
     *
     * @param rb The response builder.
     * @param ranges The ranges to update.
     */
    private void processRanges(ResponseBuilder rb, SimpleOrderedMap ranges) {

    	BiMap<String, String> mappings = (BiMap<String, String>) rb.rsp.getValues().get("_range_mappings_");
    	if(mappings != null)
    	{
            updateToACSNaming(mappings, ranges);
        }
    }

	/**
     * Update the returned facet sections to reference ACS properties rather than Solr fields.
     *
     * @param rb the response builder.
     * @param mappingName The name of the mapping definition created in {@link RewriteFacetParametersComponent}.
     * @param sections The names of the sections to be updated.
     */
    private void rewrite(ResponseBuilder rb, String mappingName, String ... sections)
    {
        BiMap<String, String> mappings = (BiMap<String, String>) rb.rsp.getValues().get(mappingName);
        if(mappings != null)
        {
            NamedList<Object>  found = (NamedList<Object>) rb.rsp.getValues();
            for (String section : sections)
            {
                found = (NamedList<Object>) found.get(section);
                if (found == null)
                {
                    return;
                }
            }
            // This found already contains the private buckets
            updateToACSNaming(mappings, found);
        }
    }

    /**
     * Update entries in a list to reference ACS properties rather than Solr fields.
     *
     * @param mappings The mapping from ACS properties to Solr fields.
     * @param propertyList The list to update.
     */
    private void updateToACSNaming(BiMap<String, String> mappings, NamedList<Object> propertyList)
    {
        for (int i = 0; i < propertyList.size(); i++)
        {
            String name = propertyList.getName(i);
            String newName = mappings.inverse().get(name);
            if (newName != null)
            {
                propertyList.setName(i, newName);
            }
        }
    }

    /* (non-Javadoc)
     * @see org.apache.solr.handler.component.SearchComponent#getDescription()
     */
    @Override
    public String getDescription()
    {
        return "RewriteFacetCounts";
    }

    /* (non-Javadoc)
     * @see org.apache.solr.handler.component.SearchComponent#getSource()
     */
    @Override
    public String getSource()
    {
        return "";
    }

}
