/**
 * The contents of this file are subject to the license and copyright
 * detailed in the LICENSE and NOTICE files at the root of the source
 * tree and available online at
 *
 * http://www.dspace.org/license/
 */
package org.dspace.statistics.content;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.dspace.content.DCDate;
import org.dspace.content.DSpaceObject;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.services.ConfigurationService;
import org.dspace.statistics.Dataset;
import org.dspace.statistics.ObjectCount;
import org.dspace.statistics.SolrLogger;
import org.dspace.statistics.content.filter.StatisticsFilter;
import org.dspace.utils.DSpace;

import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.text.ParseException;
import java.util.*;

/**
 * A workflow data implementation that will query the statistics backend for workflow information
 *
 * @author Kevin Van de Velde (kevin at atmire dot com)
 * @author Ben Bosman (ben at atmire dot com)
 * @author Mark Diggory (markd at atmire dot com)
 */
public class StatisticsDataWorkflow extends StatisticsData {

    private static final Logger log = Logger.getLogger(StatisticsDataWorkflow.class);

    /** Current DSpaceObject for which to generate the statistics. */
    private DSpaceObject currentDso;
    /** Variable used to indicate of how many months an average is required (-1 is inactive) **/
    private int averageMonths = -1;

    public StatisticsDataWorkflow(DSpaceObject dso, int averageMonths) {
        super();
        this.currentDso = dso;
        this.averageMonths = averageMonths;
    }


    @Override
    public Dataset createDataset(Context context) throws SQLException, SolrServerException, IOException, ParseException {
                // Check if we already have one.
        // If we do then give it back.
        if(getDataset() != null)
        {
            return getDataset();
        }

        List<StatisticsFilter> filters = getFilters();
        List<String> defaultFilters = new ArrayList<String>();
        for (StatisticsFilter statisticsFilter : filters) {
            defaultFilters.add(statisticsFilter.toQuery());
        }

        String defaultFilterQuery = StringUtils.join(defaultFilters.iterator(), " AND ");

        String query = getQuery();

        Dataset dataset = new Dataset(0,0);
        List<DatasetGenerator> datasetGenerators = getDatasetGenerators();
        if(0 < datasetGenerators.size()){
            //At the moment we can only have one dataset generator
            DatasetGenerator datasetGenerator = datasetGenerators.get(0);
            if(datasetGenerator instanceof DatasetTypeGenerator){
                DatasetTypeGenerator typeGenerator = (DatasetTypeGenerator) datasetGenerator;
                ObjectCount[] topCounts = SolrLogger.queryFacetField(query, defaultFilterQuery, typeGenerator.getType(), typeGenerator.getMax(), typeGenerator.isIncludeTotal(), null);

                //Retrieve our total field counts
                Map<String, Long> totalFieldCounts = new HashMap<String, Long>();
                if(averageMonths != -1){
                    totalFieldCounts = getTotalFacetCounts(typeGenerator);
                }
                long monthDifference = 1;
                if(getOldestWorkflowItemDate() != null){
                    monthDifference = getMonthsDifference(new Date(), getOldestWorkflowItemDate());
                }

                dataset = new Dataset(topCounts.length, (averageMonths != -1 ? 3 : 2));
                dataset.setColLabel(0, "step");
                dataset.setColLabel(1, "performed");
                if(averageMonths != -1){
                    dataset.setColLabel(2, "average");
                }
                for (int i = 0; i < topCounts.length; i++) {
                    ObjectCount topCount = topCounts[i];
                    dataset.setRowLabel(i, String.valueOf(i + 1));
                    dataset.addValueToMatrix(i, 0, topCount.getValue());
                    dataset.addValueToMatrix(i, 1, topCount.getCount());
                    if(averageMonths != -1){
                        //Calculate the average of one month
                        long monthlyAverage = 0;
                        if(totalFieldCounts.get(topCount.getValue()) != null){
                            monthlyAverage = totalFieldCounts.get(topCount.getValue()) / monthDifference;
                        }
                        //We multiple our average for one month by the number of
                        dataset.addValueToMatrix(i, 2, (monthlyAverage * averageMonths));
                    }

                }
            }
        }

        return dataset;
    }

    /**
     * Returns the query to be used in solr
     * in case of a dso a scopeDso query will be returned otherwise the default *:* query will be used
     * @return the query as a string
     */
    protected String getQuery() {
        String query = "statistics_type:" + SolrLogger.StatisticsType.WORKFLOW.text();
        query += " AND NOT(previousWorkflowStep: SUBMIT)";
        if(currentDso != null){
            if(currentDso.getType() == Constants.COMMUNITY){
                query += " AND owningComm:";

            }else
            if(currentDso.getType() == Constants.COLLECTION){
                query += " AND owningColl:";
            }
            query += currentDso.getID();
        }
        return query;
    }

    private int getMonthsDifference(Date date1, Date date2) {
        int m1 = date1.getYear() * 12 + date1.getMonth();
        int m2 = date2.getYear() * 12 + date2.getMonth();
        return m2 - m1 + 1;
    }


    /**
     * Retrieve the total counts for the facets (total count is same query but none of the filter queries
     * @param typeGenerator the type generator
     * @return as a key the
     * @throws org.apache.solr.client.solrj.SolrServerException
     */
    protected Map<String, Long> getTotalFacetCounts(DatasetTypeGenerator typeGenerator) throws SolrServerException {
        ObjectCount[] objectCounts = SolrLogger.queryFacetField(getQuery(), null, typeGenerator.getType(), -1, false, null);
        Map<String, Long> result = new HashMap<String, Long>();
        for (ObjectCount objectCount : objectCounts) {
            result.put(objectCount.getValue(), objectCount.getCount());
        }
        return result;
    }



    protected Date getOldestWorkflowItemDate() throws SolrServerException {
        ConfigurationService configurationService = new DSpace().getConfigurationService();
        String workflowStartDate = configurationService.getProperty("usage-statistics.workflow-start-date");
        if(workflowStartDate == null){
            //Query our solr for it !
            QueryResponse oldestRecord = SolrLogger.query(getQuery(), null, null, 1, 0, null, null, null, null, "time", true);
            if(0 < oldestRecord.getResults().getNumFound()){
                SolrDocument solrDocument = oldestRecord.getResults().get(0);
                Date oldestDate = (Date) solrDocument.getFieldValue("time");
                //Store the date, we only need to retrieve this once !
                try {
                    //Also store it in the solr-statics configuration file, the reason for this being that the sort query
                    //can be very time consuming & we do not want this delay each time we want to see workflow statistics
                    String solrConfigDir = configurationService.getProperty("dspace.dir") + File.separator + "config"
                            + File.separator + "modules" + File.separator + "usage-statistics.cfg";
                    PropertiesConfiguration config = new PropertiesConfiguration(solrConfigDir);
                    config.setProperty("workflow-start-date", new DCDate(oldestDate));
                    config.save();
                } catch (ConfigurationException e) {
                    log.error("Error while storing workflow start date", e);
                }
                //ALso store it in our local config !
                configurationService.setProperty("usage-statistics.workflow-start-date", new DCDate(oldestDate).toString());

                //Write to file
                return oldestDate;
            }else{
                return null;
            }

        }else{
            return new DCDate(workflowStartDate).toDate();
        }
    }
}
