/*
 *  Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  WSO2 Inc. licenses this file to you under the Apache License,
 *  Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 *
 */
package org.wso2.carbon.analytics.data.commons;

import org.wso2.carbon.analytics.data.commons.exception.AnalyticsException;
import org.wso2.carbon.analytics.data.commons.sources.AnalyticsIterator;
import org.wso2.carbon.analytics.data.commons.sources.Record;
import org.wso2.carbon.analytics.data.commons.sources.RecordGroup;
import org.wso2.carbon.analytics.data.commons.service.AnalyticsDataResponse;
import org.wso2.carbon.analytics.data.commons.service.AnalyticsSchema;

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

/**
 * This interface represents the analytics data service operations.
 */
public interface AnalyticsDataService {

    /**
     * Lists all the record stores available in the system.
     *
     * @return The list of record store names
     */
    List<String> listRecordStoreNames();

    /**
     * Creates a table, if not already there, where the columns are not defined here, but can contain any arbitrary number
     * of columns when data is added. The table names are not case sensitive.
     *
     * @param recordStoreName The name of the target record store to store the table at
     * @param tableName       The name of the table to be created
     * @throws AnalyticsException
     */
    void createTable(String recordStoreName, String tableName) throws AnalyticsException;

    /**
     * @param tableName The name of the table to be created
     * @throws AnalyticsException
     * @see AnalyticsDataService#createTable(String, String). The primary record store is used
     * to create the table.
     */
    void createTable(String tableName) throws AnalyticsException;

    /**
     * Creates the table if it does not already exist in the system.
     *
     * @param recordStoreName The name of the target record store to store the table at
     * @param tableName       The name of the table to be created
     * @throws AnalyticsException
     */
    void createTableIfNotExists(String recordStoreName, String tableName) throws AnalyticsException;


    /**
     * Returns the record store name given the table information.
     *
     * @param tableName The table name
     * @return The record store name
     * @throws AnalyticsException
     */
    String getRecordStoreNameByTable(String tableName) throws AnalyticsException;

    /**
     * Sets the schema for the target analytics table, if there is already one assigned, it will be
     * overwritten.
     *
     * @param tableName The table name
     * @param schema    The schema to be applied to the table
     * @throws AnalyticsException
     */
    void setTableSchema(String tableName, AnalyticsSchema schema) throws AnalyticsException;

    /**
     * Retrieves the table schema for the given table.
     *
     * @param tableName The table name
     * @return The schema of the table
     * @throws AnalyticsException
     */
    AnalyticsSchema getTableSchema(String tableName) throws AnalyticsException;

    /**
     * Checks if the specified table with the given category and name exists.
     *
     * @param tableName The table name
     * @return true if the table exists, false otherwise
     * @throws AnalyticsException
     */
    boolean tableExists(String tableName) throws AnalyticsException;

    /**
     * Deletes the table with the given category and name if a table exists already.
     * This will not throw an error if the table is not there.
     *
     * @param tableName The name of the table to be dropped
     * @throws AnalyticsException
     */
    void deleteTable(String tableName) throws AnalyticsException;

    /**
     * Lists all the current tables with the given category.
     *
     * @return The list of table names
     * @throws AnalyticsException
     */
    List<String> listTables() throws AnalyticsException;

    /**
     * Adds a new record to the table. If the record id is mentioned,
     * it will be used to do the insert, or else, it will check the table's schema to check for the existence of
     * primary keys, if there are any, the primary keys will be used to derive the id, or else
     * the insert will be done with a randomly generated id.
     * If the record already exists, it updates the record store with the given records, matches by its record id,
     * this will be a full replace of the record, where the older record is effectively deleted and the new one is
     * added, there will not be a merge of older record's field's with the new one.
     *
     * @param records The list of records to be inserted
     * @throws AnalyticsException
     */
    void put(List<Record> records) throws AnalyticsException;

    /**
     * Retrieves data from a table, with a given range.
     *
     * @param tableName         The name of the table to search on
     * @param numPartitionsHint The best effort number of splits this should return
     * @param columns           The list of columns to required in results, null if all needs to be returned
     * @param timeFrom          The starting time to get records from, inclusive, relatively to epoch,
     *                          Long.MIN_VALUE should signal, this restriction to be disregarded
     * @param timeTo            The ending time to get records to, non-inclusive, relatively to epoch,
     *                          Long.MAX_VALUE should signal, this restriction to be disregarded
     * @param recordsFrom       The paginated index from value, zero based, inclusive
     * @param recordsCount      The paginated records count to be read, -1 for infinity
     * @return The analytics data response
     * @throws AnalyticsException
     */
    AnalyticsDataResponse get(String tableName, int numPartitionsHint, List<String> columns,
                              long timeFrom,
                              long timeTo, int recordsFrom, int recordsCount) throws AnalyticsException;

    /**
     * Retrieves data from a table with given ids.
     *
     * @param tableName         The name of the table to search on
     * @param numPartitionsHint The best effort number of splits this should return
     * @param columns           The list of columns to required in results, null if all needs to be returned
     * @param ids               The list of ids of the records to be read
     * @return The analytics data response
     * @throws AnalyticsException
     */
    AnalyticsDataResponse get(String tableName, int numPartitionsHint, List<String> columns,
                              List<String> ids) throws AnalyticsException;

    /**
     * Retrieves data from a table with the values for all the given primary key value composites.
     *
     * @param tableName         The name of the table to search on
     * @param numPartitionsHint The best effort number of splits this should return
     * @param columns           The list of columns to required in results, null if all needs to be returned
     * @param valuesBatch       A batch of key/values which contains the primary keys values to match the data in the table
     * @return The analytics data response
     * @throws AnalyticsException
     */
    AnalyticsDataResponse getWithKeyValues(String tableName, int numPartitionsHint,
                                           List<String> columns,
                                           List<Map<String, Object>> valuesBatch) throws AnalyticsException;

    /**
     * Reads in the records from a given record group at a given record store, the records will be streamed in.
     *
     * @param recordStoreName The record store name
     * @param recordGroup     The record group which represents the local data set
     * @return An iterator of type {@link Record} in the local record group
     * @throws AnalyticsException
     */
    AnalyticsIterator<Record> readRecords(String recordStoreName, RecordGroup recordGroup) throws AnalyticsException;

    /**
     * Deletes a set of records in the table.
     *
     * @param tableName The name of the table to search on
     * @param timeFrom  The starting time to get records from for deletion
     * @param timeTo    The ending time to get records to for deletion
     * @throws AnalyticsException
     */
    void delete(String tableName, long timeFrom, long timeTo) throws AnalyticsException;

    /**
     * Delete data in a table with given ids.
     *
     * @param tableName The name of the table to search on
     * @param ids       The list of ids of the records to be deleted
     * @throws AnalyticsException
     */
    void delete(String tableName, List<String> ids) throws AnalyticsException;

    /**
     * Destroys and frees any resources taken up by the analytics data service implementation.
     */
    void destroy() throws AnalyticsException;

    /**
     * Invalidates the given table for the current node.
     *
     * @param tableName name of the table
     */
    void invalidateTable(String tableName);

}
