from __future__ import division

import scipy as sp
import quantities as pq

from guiqwt.builder import make
from guiqwt.baseplot import BasePlot
from guiqwt.plot import BaseCurveWidget

from ..progress_indicator import ProgressIndicator
from .. import conversions
from .. import SpykeException
from dialog import PlotDialog
import helper


@helper.needs_qt
def signals(signals, events=None, epochs=None, spike_trains=None,
            spikes=None, show_waveforms=True, use_subplots=True,
            subplot_names=True, time_unit=pq.s, y_unit=None, progress=None):
    """ Create a plot from a list of analog signals.

    :param list signals: The list of :class:`neo.core.AnalogSignal` objects
        to plot.
    :param sequence events: A list of Event objects to be included in the
        plot.
    :param sequence epochs: A list of Epoch objects to be included in the
        plot.
    :param list spike_trains: A list of :class:`neo.core.SpikeTrain` objects
        to be included in the plot. The ``unit`` property (if it exists) is
        used for color and legend entries.
    :param list spikes: A list :class:`neo.core.Spike` objects to be included
        in the plot. The ``unit`` property (if it exists) is used for color
        and legend entries.
    :param bool show_waveforms: Determines if spikes from
        :class:`neo.core.Spike` and :class:`neo.core.SpikeTrain` objects are
        shown as waveforms (if available) or vertical lines.
    :param bool use_subplots: Determines if a separate subplot for is created
        each signal.
    :param bool subplot_names: Only valid if ``use_subplots`` is True.
        Determines if signal (or channel) names are shown for subplots.
    :param Quantity time_unit: The unit of the x axis.
    :param progress: Set this parameter to report progress.
    :type progress: :class:`spykeutils.progress_indicator.ProgressIndicator`
    """
    if not signals:
        raise SpykeException(
            'Cannot create signal plot: No signal data provided!')
    if not progress:
        progress = ProgressIndicator()

    # Plot title
    win_title = 'Analog Signal'
    if len(set((s.recordingchannel for s in signals))) == 1:
        if signals[0].recordingchannel and signals[0].recordingchannel.name:
            win_title += ' | Recording Channel: %s' %\
                         signals[0].recordingchannel.name
    if len(set((s.segment for s in signals))) == 1:
        if signals[0].segment and signals[0].segment.name:
            win_title += ' | Segment: %s' % signals[0].segment.name
    win = PlotDialog(toolbar=True, wintitle=win_title)

    if events is None:
        events = []
    if epochs is None:
        epochs = []
    if spike_trains is None:
        spike_trains = []
    if spikes is None:
        spikes = []

    if show_waveforms:
        for st in spike_trains:
            if st.waveforms is not None:
                spikes.extend(conversions.spike_train_to_spikes(st))
        spike_trains = []
    else:
        unit_spikes = {}
        for s in spikes:
            unit_spikes.setdefault(s.unit, []).append(s)
        for sps in unit_spikes.itervalues():
            spike_trains.append(conversions.spikes_to_spike_train(sps, False))
        spikes = []

    channels = range(len(signals))

    channel_indices = []
    for s in signals:
        if not s.recordingchannel:
            channel_indices.append(-1)
        else:
            channel_indices.append(s.recordingchannel.index)

    # Heuristic: If multiple channels have the same index, use channel order
    # as index for spike waveforms
    nonindices = max(0, channel_indices.count(-1) - 1)
    if len(set(channel_indices)) != len(channel_indices) - nonindices:
        channel_indices = range(len(signals))

    progress.set_ticks((len(spike_trains) + len(spikes) + 1) * len(channels))

    offset = 0 * signals[0].units
    if use_subplots:
        plot = None
        for c in channels:
            pW = BaseCurveWidget(win)
            plot = pW.plot

            if subplot_names:
                if signals[c].name:
                    win.set_plot_title(plot, signals[c].name)
                elif signals[c].recordingchannel:
                    if signals[c].recordingchannel.name:
                        win.set_plot_title(
                            plot, signals[c].recordingchannel.name)

            sample = (1 / signals[c].sampling_rate).simplified
            x = (sp.arange(signals[c].shape[0])) * sample + signals[c].t_start
            x.units = time_unit

            helper.add_epochs(plot, epochs, x.units)
            if y_unit is not None:
                plot.add_item(make.curve(x, signals[c].rescale(y_unit)))
            else:
                plot.add_item(make.curve(x, signals[c]))
            helper.add_events(plot, events, x.units)

            _add_spike_waveforms(
                plot, spikes, x.units, channel_indices[c], offset, progress)

            for train in spike_trains:
                color = helper.get_object_color(train.unit)
                helper.add_spikes(plot, train, color, units=x.units)
                progress.step()

            win.add_plot_widget(pW, c)
            plot.set_axis_unit(
                BasePlot.Y_LEFT, signals[c].dimensionality.string)
            progress.step()

        plot.set_axis_title(BasePlot.X_BOTTOM, 'Time')
        plot.set_axis_unit(BasePlot.X_BOTTOM, x.dimensionality.string)
    else:
        channels.reverse()

        pW = BaseCurveWidget(win)
        plot = pW.plot

        helper.add_epochs(plot, epochs, time_unit)

        # Find plot y offset
        max_offset = 0 * signals[0].units
        for i, c in enumerate(channels[1:], 1):
            cur_offset = signals[channels[i - 1]].max() - signals[c].min()
            if cur_offset > max_offset:
                max_offset = cur_offset

        offset -= signals[channels[0]].min()

        for c in channels:
            sample = (1 / signals[c].sampling_rate).simplified
            x = (sp.arange(signals[c].shape[0])) * sample + signals[c].t_start
            x.units = time_unit

            if y_unit is not None:
                plot.add_item(
                    make.curve(x, (signals[c] + offset).rescale(y_unit)))
            else:
                plot.add_item(make.curve(x, signals[c] + offset))
            _add_spike_waveforms(
                plot, spikes, x.units, channel_indices[c], offset, progress)
            offset += max_offset
            progress.step()

        helper.add_events(plot, events, x.units)

        for train in spike_trains:
            color = helper.get_object_color(train.unit)
            helper.add_spikes(plot, train, color, units=x.units)
            progress.step()

        win.add_plot_widget(pW, 0)

        plot.set_axis_title(BasePlot.X_BOTTOM, 'Time')
        plot.set_axis_unit(BasePlot.X_BOTTOM, x.dimensionality.string)
        plot.set_axis_unit(BasePlot.Y_LEFT, signals[0].dimensionality.string)

    win.add_custom_curve_tools()

    units = set([s.unit for s in spike_trains])
    units = units.union([s.unit for s in spikes])

    progress.done()

    helper.make_window_legend(win, units, False)
    win.show()

    if use_subplots:
        win.add_x_synchronization_option(True, channels)
        win.add_y_synchronization_option(False, channels)

    return win


def _add_spike_waveforms(plot, spikes, x_units, channel, offset, progress):
    for spike in spikes:
        if spike.waveform is None or not spike.sampling_rate:
            continue
        if channel < 0 or spike.waveform.shape[1] <= channel:
            continue

        color = helper.get_object_color(spike.unit)
        if spike.left_sweep:
            lsweep = spike.left_sweep
        else:
            lsweep = 0.0 * pq.ms
        start = (spike.time - lsweep).rescale(x_units)
        stop = (spike.waveform.shape[0] / spike.sampling_rate +
                spike.time - lsweep).rescale(x_units)
        spike_x = sp.linspace(
            start, stop, spike.waveform.shape[0], endpoint=False) * x_units

        plot.add_item(
            make.curve(spike_x, spike.waveform[:, channel] + offset,
                       color=color, linewidth=2))
        progress.step()
