from __future__ import absolute_import
import time
import os
import atexit
import six
from six.moves import range

try:
    from multiprocessing import Process, Pipe

except ImportError:
    Process = None

import numpy as nm

from sfepy.base.base import sfepy_config_dir, ordered_iteritems
from sfepy.base.base import output, get_default, set_defaults, Output, Struct
from sfepy.base.log_plotter import LogPlotter

_msg_no_live = 'warning: log plot is disabled, install matplotlib and' \
    ' multiprocessing'

def get_logging_conf(conf, log_name='log'):
    """
    Check for a log configuration ('log' attribute by default) in
    `conf`. Supply default values if necessary.

    Parameters
    ----------
    conf : Struct
        The configuration object.
    log_name : str, optional
        The name of the log configuration attribute in `conf`.

    Returns
    -------
    log : dict
        The dictionary {'plot' : <figure_file>, 'text' : <text_log_file>}. One
        or both values can be None.
    """
    log = conf.get(log_name, None)

    default_log = {'text' : None, 'plot' : None}

    if log is None:
        log = default_log

    else:
        set_defaults(log, default_log)

    return log

def name_to_key(name, ii):
    return name + (':%d' % ii)

def read_log(filename):
    """
    Read data saved by :class:`Log` into a text file.

    Parameters
    ----------
    filename : str
        The name of a text log file.

    Returns
    -------
    log : dict
        The log with data names as keys and ``(xs, ys, vlines)`` as values.
    info : dict
        The log plot configuration with subplot numbers as keys.
    """
    log = {}
    info = {}

    fd = open(filename, 'r')

    last_xval = None
    for line in fd:
        if line[0] == '#':
            ls = line.split(':')
            if ls[0] == '# groups':
                n_gr = int(ls[1])
                for ig in range(n_gr):
                    next(fd)
                    line_info = next(fd)
                    xlabel, ylabel, yscales = line_info.split(',')

                    line_names = next(fd)
                    names = line_names.split(':')[1]

                    line_plot_kwargs = next(fd)

                    info[ig] = (xlabel.split(':')[1].strip().strip('"'),
                                ylabel.split(':')[1].strip().strip('"'),
                                yscales.split(':')[1].strip().strip('"'),
                                [name.strip().strip('"')
                                 for name in names.split(',')],
                                eval(line_plot_kwargs[19:].strip().strip('"')
                                     + ','))

            continue

        ls = line.split(':')

        key = ls[0]
        xs, ys, vlines = log.setdefault(key, ([], [], []))

        if (len(ls) == 2) and (last_xval is not None):
            vlines.append(last_xval)

        else:
            try:
                xval, yval = float(ls[1]), float(ls[2])

            except ValueError:
                continue

            xs.append(xval)
            ys.append(yval)

            last_xval = xval

    fd.close()

    for key, (xs, ys, vlines) in six.iteritems(log):
        log[key] = (nm.array(xs), nm.array(ys), nm.array(vlines))

    return log, info

def plot_log(axs, log, info, xticks=None, yticks=None, groups=None):
    """
    Plot log data returned by :func:`read_log()` into a specified figure.

    Parameters
    ----------
    axs : sequence of matplotlib.axes.Axes
        The list of axes for the log data plots.
    log : dict
        The log with data names as keys and ``(xs, ys, vlines)`` as values.
    info : dict
        The log plot configuration with subplot numbers as keys.
    xticks : list of arrays, optional
        The list of x-axis ticks (array or None) for each subplot.
    yticks : list of arrays, optional
        The list of y-axis ticks (array or None) for each subplot.
    groups : list, optional
        The list of data groups subplots. If not given, all groups are plotted.
    """
    import matplotlib.pyplot as plt

    if axs is None:
        fig = plt.figure()

    else:
        fig = None

    if groups is None:
        n_gr = len(info)
        groups = nm.arange(n_gr)

    else:
        n_gr = len(groups)

    n_col = min(5.0, nm.fix(nm.sqrt(n_gr)))
    if int(n_col) == 0:
        n_row = 0

    else:
        n_row = int(nm.ceil(n_gr / n_col))
        n_col = int(n_col)

    if xticks is None:
        xticks = [None] * n_gr

    if yticks is None:
        yticks = [None] * n_gr

    isub = 0
    for ii, (xlabel, ylabel, yscale, names, plot_kwargs) in six.iteritems(info):
        if ii not in groups: continue

        if axs is None:
            ax = fig.add_subplot(n_row, n_col, isub + 1)

        else:
            ax = axs[ii]

        ax.set_yscale(yscale)

        if xlabel:
            ax.set_xlabel(xlabel)

        if ylabel:
            ax.set_ylabel(ylabel)

        for ip, name in enumerate(names):
            xs, ys, vlines = log[name]
            ax.plot(xs, ys, label=name, **plot_kwargs[ip])

            for x in vlines:
                ax.axvline(x, color='k', alpha=0.3)

        if xticks[isub] is not None:
            ax.set_xticks(xticks[isub])

        else:
            ax.locator_params(axis='x', nbins=10)

        if yticks[isub] is not None:
            ax.set_yticks(yticks[isub])

        ax.legend(loc='best')
        isub += 1

    plt.tight_layout(pad=0.5)

class Log(Struct):
    """
    Log data and (optionally) plot them in the second process via
    LogPlotter.
    """
    count = -1

    @staticmethod
    def from_conf(conf, data_names):
        """
        Parameters
        ----------
        data_names : list of lists of str
            The data names grouped by subplots: [[name1, name2, ...], [name3,
            name4, ...], ...], where name<n> are strings to display in
            (sub)plot legends.
        """
        obj = Log(data_names, **conf)