package org.hisp.dhis.analytics;



import java.util.List;

import org.hisp.dhis.common.IllegalQueryException;
import org.hisp.dhis.common.MaintenanceModeException;

/**
 * Service interface which provides methods for validating and planning 
 * analytics queries.
 * 
 * @author Lars Helge Overland
 */
public interface QueryPlanner
{
    /**
     * Validates the given query. Throws an IllegalQueryException if the query
     * is not valid with a descriptive message. Returns normally if the query is
     * valid.
     * 
     * @param params the data query parameters.
     * @throws IllegalQueryException if the query is invalid.
     */
    void validate( DataQueryParams params )
        throws IllegalQueryException;
    
    /**
     * Validates whether the given table layout is valid for the given query. 
     * Throws an IllegalQueryException if the query is not valid with a 
     * descriptive message. Returns normally if the query is valid.
     * 
     * @param params the data query parameters.
     * @param columns the column dimension identifiers.
     * @param rows the row dimension identifiers.
     * @throws IllegalQueryException if the query is invalid.
     */
    void validateTableLayout( DataQueryParams params, List<String> columns, List<String> rows )
        throws IllegalQueryException;
    
    /**
     * Checks whether the analytics engine is in maintenance mode.
     * 
     * @throws MaintenanceModeException if analytics engine is in maintenance mode.
     */
    void validateMaintenanceMode()
        throws MaintenanceModeException;
    
    /**
     * Creates a DataQueryGroups object. It is mandatory to group the queries by
     * the following criteria: 1) partition / year 2) organisation  unit level
     * 3) period type 4) aggregation type. The DataQueryGroups contains groups of 
     * queries. The query groups should be run in sequence while the queries within
     * each group should be run in parallel for optimal performance.
     * 
     * If the number of queries produced by this grouping is equal or
     * larger than the number of optimal queries, those queries are returned. If
     * not it will split on the data element dimension, data set dimension and
     * organisation unit dimension, and return immediately after each step if
     * optimal queries are met.
     * 
     * It does not attempt to split on period dimension as splitting on columns 
     * with low cardinality typically does not improve performance.
     * 
     * @param params the data query parameters.
     * @param plannerParams the query planner parameters.
     * @return a DataQueryGroups object.
     */
    DataQueryGroups planQuery( DataQueryParams params, QueryPlannerParams plannerParams  )
        throws IllegalQueryException;

    /**
     * If organisation units appear as dimensions; groups the given query into 
     * sub queries based on the level of the organisation units. Sets the organisation 
     * unit level on each query. If organisation units appear as filter; replaces
     * the organisation unit filter with one filter for each level. Sets the dimension
     * names and filter names respectively.
     * 
     * @param params the data query parameters.
     * @return a list of data query parameters.
     */
    List<DataQueryParams> groupByOrgUnitLevel( DataQueryParams params );
    
    /**
     * Groups the given query into sub queries based on its periods and which 
     * partition it should be executed against. Sets the partition table name on
     * each query. Queries are grouped based on periods if appearing as a 
     * dimension.
     * 
     * @param params the data query parameters.
     * @param plannerParams the query planner parameters.
     * @return a list of data query parameters.
     */
    List<DataQueryParams> groupByPartition( DataQueryParams params, QueryPlannerParams plannerParams );
    
    /**
     * If periods appear as dimensions in the given query; groups the query into 
     * sub queries based on the period type of the periods. Sets the period type 
     * name on each query. If periods appear as filters; replaces the period filter
     * with one filter for each period type. Sets the dimension names and filter
     * names respectively.
     * 
     * @param params the data query parameters.
     * @return a list of data query parameters.
     */
    List<DataQueryParams> groupByPeriodType( DataQueryParams params );    

    /**
     * If periods appear as dimensions in the given query; groups the given query 
     * into sub queries based on start and end dates, i.e. one query per period. 
     * Marks the period dimension as fixed and sets the dimension name to the period
     * ISO name. Sets the start date and end date properties. If periods appear
     * as filters in the given query; sets the start date and end date properties
     * based on the first period and removes the period dimension.
     * 
     * @param params the data query parameters.
     * @return a list of data query parameters.
     */
    List<DataQueryParams> groupByStartEndDate( DataQueryParams params );
}
