import logging
import numpy as np
import pandas
import progressbar

import hcnn.common.labels as labels
instrument_map = labels.InstrumentClassMap()

logger = logging.getLogger(__name__)


def predict_one(dfrecord, model, slicer_fx, t_len):
    """Return an evaluation object/dict after evaluating
    a single model using a loaded model.

    This method runs the model.predict over every set of
    frames generated by slicer_fx, and returns
    the class with the maximum

    Parameters
    ----------
    dfrecord : pandas.DataFrame
        pandas.Series containing the record to evaluate.

    model : models.NetworkManager

    slicer_fx : function
        Function that extracts featuers fr eaach frame
        from the feature file.

    t_len : int

    Returns
    ------
    results : pandas.Series
        All the results stored as a pandas.Series
    """
    # Get predictions for every timestep in the file.
    target = instrument_map.get_index(dfrecord["instrument"])

    predictions = []
    losses = []
    # Technically, this should only ever sample one frame,
    #  but we use the for loop to handle StopIterations smoothly.
    for frames in slicer_fx(dfrecord, t_len=t_len,
                            shuffle=False, auto_restart=False,
                            add_noise=False):
        predictions += [model.predict(frames).argmax()]
        loss, acc = model.evaluate(frames)
        losses.append(loss)

    y_pred = predictions[0] if len(predictions) else None
    loss = losses[0] if len(losses) else None

    # Return both of these as a dataframe.
    return pandas.Series(
        data=[loss, y_pred, target],
        index=['loss', "y_pred", "y_true"],
        name=dfrecord.name)


def predict_many(test_df, model, slicer_fx, t_len, show_progress=False):
    """Run evaluation on the files in a dataframe.

    Parameters
    ----------
    test_df : pandas.DataFrame
        DataFrame pointing to the features files and targets to evaluate.

    model : models.NetworkManager

    slicer_fx : function
        Function that extracts featuers fr eaach frame
        from the feature file.

    t_len : int

    Returns
    -------
    results_df : pandas.DataFrame
        DataFrame containing the results from for each file,
        where the index of the original file is maintained, but
        the dataframe now contains the columns:
            * max_likelihood
            * vote
            * target
    """
    results = []
    if show_progress:
        i = 0
        progress = progressbar.ProgressBar(max_value=len(test_df))

    try:
        for index, row in test_df.iterrows():
            results += [predict_one(row, model, slicer_fx, t_len)]

            if show_progress:
                progress.update(i)
                i += 1
    except KeyboardInterrupt:
        logger.error("Evaluation process interrupted; {} of {} evaluated."
                     .format(len(results), len(test_df)))
        logger.error("Recommend you start this process over to evaluate "
                     "them all.")

    return pandas.DataFrame(results)
