# -*- coding: utf-8 -*-
from __future__ import division
from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals

import time, numpy as np
import matplotlib.pyplot as plt


class VoltMeter(object):
    '''Bar graph and history displays of effective voltage of input samples'''

    def __init__(self, OscConfDict):
        '''Args:   Wtime: waiting time between updates
        conf: Configuration of channels
        '''
        # collect relevant configuration parameters
        self.Npoints = 120  # number of points for history
        self.bwidth = 0.5  # width of bars
        self.NChan = OscConfDict['NChannels']

        self.ChanLim = []
        CRanges = OscConfDict['CRanges']
        COffsets = OscConfDict['ChanOffsets']
        for i in range(self.NChan):
            # Channel Limits for effective voltage
            self.ChanLim.append((0.0, CRanges[i] - COffsets[i]))
        # Channel Limits for average voltage
        #  self.ChanLim.append( (-CRanges[i]-COffsets[i],
        #                        CRanges[i]-COffsets[i]) )

        self.ChanNams = OscConfDict['Channels']
        self.ChanColors = OscConfDict['ChanColors']

        # data structures needed throughout the class
        self.ix = np.linspace(-self.Npoints + 1, 0, self.Npoints)  # history plot
        self.ind = self.bwidth + np.arange(self.NChan)  # bar position for voltages
        #
        self.V = np.empty(self.NChan)
        self.stdV = np.empty(self.NChan)
        self.Vhist = np.zeros([self.NChan, self.Npoints])
        self.stdVhist = np.zeros([self.NChan, self.Npoints])

        # set up a figure to plot actual voltage and samplings from Picoscope
        fig = plt.figure("Voltmeter", figsize=(4.0, 5.3))
        fig.subplots_adjust(left=0.2, bottom=0.08, right=0.8, top=0.95, wspace=None, hspace=0.25)
        axes = []
        # history plot
        axes.append(plt.subplot2grid((6, 1), (4, 0), rowspan=2))
        if self.NChan > 1:
            axes.append(axes[0].twinx())
        # for effective Voltage
        for i, C in enumerate(self.ChanNams):
            if i > 1:
                break  # works for a maximum of 2 Channels only
            axes[i].set_ylim(*self.ChanLim[i])
            axes[i].set_ylabel('Chan ' + C + ' (Veff)', color=self.ChanColors[i])
            axes[i].grid(True, color=self.ChanColors[i], linestyle='--', alpha=0.3)
        axes[0].set_xlabel('History')
        # barchart
        axes.append(plt.subplot2grid((6, 1), (1, 0), rowspan=3))
        axbar1 = axes[-1]
        axbar1.set_frame_on(False)
        if self.NChan > 1:
            axbar2 = axbar1.twinx()
            axbar2.set_frame_on(False)
        axbar1.get_xaxis().set_visible(False)
        axbar1.set_xlim(0.0, self.NChan)
        axbar1.axvline(0, color=self.ChanColors[0])
        if self.NChan > 1:
            axbar1.axvline(self.NChan, color=self.ChanColors[1])
        axbar1.set_ylim(*self.ChanLim[0])
        axbar1.axhline(0.0, color='k', linestyle='-', lw=2, alpha=0.5)
        axbar1.set_ylabel('Chan A (Veff)', color=self.ChanColors[0])
        if self.NChan > 1:
            axbar2.set_ylim(*self.ChanLim[1])
            axbar2.set_ylabel('Chan B (Veff)', color=self.ChanColors[1])
        # Voltage in Text format
        axes.append(plt.subplot2grid((6, 1), (0, 0)))
        axtxt = axes[-1]
        axtxt.set_frame_on(False)
        axtxt.get_xaxis().set_visible(False)
        axtxt.get_yaxis().set_visible(False)
        axtxt.set_title('Picoscope as Voltmeter', size='xx-large')

        self.fig = fig
        self.axes = axes
        self.axbar1 = axbar1
        if self.NChan > 1:
            self.axbar2 = axbar2

    # -- end def grVMeterIni

    def init(self):
        # initialize objects to be animated

        # a bar graph for the actual voltages
        #    self.bgraph = self.axes[0].bar(ind, np.zeros(self.NChan), self.bwidth,
        #                           align='center', color='grey', alpha=0.5)
        (self.bgraph1,) = self.axbar1.bar(
            self.ind[0], 0.0, self.bwidth, align='center', color=self.ChanColors[0], alpha=0.5
        )
        if self.NChan > 1:
            (self.bgraph2,) = self.axbar2.bar(
                self.ind[1], 0.0, self.bwidth, align='center', color=self.ChanColors[1], alpha=0.5
            )
        # history graphs
        self.graphs = ()
        for i, C in enumerate(self.ChanNams):
            if i > 1:
                break  # max. of 2 channels
            (g,) = self.axes[i].plot(self.ix, np.zeros(self.Npoints), color=self.ChanColors[i])
            self.graphs += (g,)
        self.animtxt = self.axes[-1].text(
            0.01, 0.05, ' ', transform=self.axes[-1].transAxes, size='large', color='darkblue'
        )

        self.t0 = time.time()  # remember start time

        if self.NChan > 1:
            return (self.bgraph1,) + (self.bgraph2,) + self.graphs + (self.animtxt,)
        else:
            # -- end VoltMeter.init()
            return (self.bgraph1,) + self.graphs + (self.animtxt,)

    def __call__(self, evt):
        n, evNr, evTime, evData = evt
        if n == 0:
            return self.init()

        k = n % self.Npoints
        txt_t = 'Time  %.1fs' % (evTime)
        txt = []
        for i, C in enumerate(self.ChanNams):
            if i > 1:
                break  # works for 2 channels only
            self.V[i] = np.sqrt(np.inner(evData[i], evData[i]) / len(evData[i]))
            self.Vhist[i, k] = self.V[i]
            self.stdV[i] = evData[i].std()
            self.stdVhist[i, k] = self.stdV[i]
            # update history graph
            if n > 1:  # !!! fix to avoid permanent display of first object in blit mode
                self.graphs[i].set_data(
                    self.ix, np.concatenate((self.Vhist[i, k + 1 :], self.Vhist[i, : k + 1]), axis=0)
                )
            else:
                self.graphs[i].set_data(self.ix, np.zeros(self.Npoints))
            txt.append('  %s:   %.3gV +/-%.2gV' % (C, self.Vhist[i, k], self.stdVhist[i, k]))
        # update bar chart
        #      for r, v in zip(bgraph, V):
        #          r.set_height(v)
        if n > 1:  # !!! fix to avoid permanent display of first object in blit mode
            self.bgraph1.set_height(self.V[0])
            if self.NChan > 1:
                self.bgraph2.set_height(self.V[1])
        else:
            self.bgraph1.set_height(0.0)
            if self.NChan > 1:
                self.bgraph2.set_height(0.0)
        if self.NChan > 1:
            self.animtxt.set_text(txt_t + '\n' + txt[0] + '\n' + txt[1])
        else:
            self.animtxt.set_text(txt_t + '\n' + txt[0])
        #
        if self.NChan > 1:
            return (self.bgraph1,) + (self.bgraph2,) + self.graphs + (self.animtxt,)
        else:
            return (self.bgraph1,) + self.graphs + (self.animtxt,)


# - -end def Voltmeter.__call__
# -end class VoltMeter
