"""
Plotly implementation of the plotting engine.
"""
from __future__ import print_function, absolute_import

from .engine import PlottingEngine, PlottingFigure, PlottingLayout, filterWithSelections, TiledFigure, LowerTriFigure
import numpy as np
import plotly
from plotly.graph_objs import Scatter, Scatter3d, Layout, Data
from plotly import tools


class PlotlyEngine(PlottingEngine):
    """ PlottingEngine using plotly. """

    def __init__(self):
        PlottingEngine.__init__(self)

    def __str__(self):
        return "<PlotlyEngine>"

    def newFigure(self, title=None, logX=False, logY=False, layout=PlottingLayout(), xtitle=None, ytitle=None):
        """ Returns a figure object."""
        return PlotlyFigure(title=title, layout=layout, xtitle=xtitle, ytitle=ytitle)

    def newStackedFigure(self, title=None, logX=False, logY=False, layout=PlottingLayout()):
        """ Returns a figure object."""
        return PlotlyStackedFigure(title=title, layout=layout)

    def newTiledFigure(self, title=None, rows=None, cols=None):
        return PlotlyTiledFigure(engine=self, rows=rows, cols=cols)

    def newLowerTriFigure(self, title=None, rows=None, cols=None):
        return PlotlyLowerTriFigure(engine=self, rows=rows, cols=cols)


class PlotlyFigure(PlottingFigure):
    """ PlotlyFigure. """

    def __init__(self, title=None, layout=PlottingLayout(), logx=False, logy=False, save_to_pdf=False, xtitle=None, ytitle=None):
        super(PlotlyFigure, self).__init__(title=title, layout=layout, logx=logx, xtitle=xtitle, logy=logy, ytitle=ytitle)

    def getArgsForDataset(self, dataset):
        kwargs = {}
        if 'name' in dataset and dataset['name'] is not None:
            kwargs['name'] = dataset['name']
        else:
            kwargs['showlegend'] = False
        if 'alpha' in dataset and dataset['alpha'] is not None:
            kwargs['opacity'] = dataset['alpha']
        # lines/markers (lines by default)
        if 'mode' in dataset and dataset['mode'] is not None:
            kwargs['mode'] = dataset['mode']
        else:
            kwargs['mode'] = 'lines'
        # override mode via scatter
        if 'scatter' in dataset and dataset['scatter'] == True:
            kwargs['mode'] = 'markers'
        if 'error_y_pos' in dataset and dataset['error_y_pos'] is not None and 'error_y_neg' in dataset and dataset['error_y_neg'] is not None:
            kwargs['error_y'] = dict(
                type='data',
                symmetric=False,
                array=dataset['error_y_pos'],
                arrayminus=dataset['error_y_neg'],
                visible=True,
            )
        return kwargs

    def getScatterGOs(self):
        for dataset in self.getDatasets():
            yield Scatter(
                x = dataset['x'],
                y = dataset['y'],
                **self.getArgsForDataset(dataset)
            )


    def render(self):
        """ Plot the figure. Call this last."""
        traces = list(self.getScatterGOs())

        data = Data(traces)
        plotly.offline.iplot({
            'data': data,
            'layout': self.makeLayout()
        })

    def save(self, filename, format):
        # FIXME: implement
        raise NotImplementedError

    def makeLayout(self):
        kwargs = {}
        if self.title is not None:
            kwargs['title'] = self.title
        if self.logx:
            kwargs['xaxis'] = {
                'type': 'log',
                'autorange': True,
            }
        if self.xtitle:
            if not 'xaxis' in kwargs:
                kwargs['xaxis'] = {}
            kwargs['xaxis']['title'] = self.xtitle
        if self.logy:
            kwargs['yaxis'] = {
                'type': 'log',
                'autorange': True,
            }
        if self.ytitle:
            if not 'yaxis' in kwargs:
                kwargs['yaxis'] = {}
            kwargs['yaxis']['title'] = self.ytitle
        if not self.grid_enabled:
            kwargs['xaxis']['showgrid'] = False
            kwargs['xaxis']['showline'] = False
            kwargs['yaxis']['showgrid'] = False
            kwargs['yaxis']['showline'] = False
        return Layout(**kwargs)


class PlotlyStackedFigure(PlotlyFigure):
    """ Stacked figure."""
    def __init__(self, title=None, layout=PlottingLayout(), logx=False, logy=False):
        super(PlotlyStackedFigure, self).__init__(title=title, layout=layout, logx=logx, logy=logy)
        self.zindex = 0

    def render(self):
        """ Plot the figure. Call this last."""
        traces = []
        for dataset in self.xy_datasets:
            kwargs = {}
            if 'name' in dataset and dataset['name'] is not None:
                kwargs['name'] = dataset['name']
                self.zindex = kwargs['name']
            else:
                kwargs['showlegend'] = False
            zvals = np.full(np.size(dataset['x']), self.zindex)
            traces.append(Scatter3d(
                x = dataset['x'],
                z = dataset['y'],
                y = zvals,
                mode = 'lines',
                **kwargs
            ))
            if not isinstance(self.zindex, str):
                self.zindex += 1

        data = Data(traces)
        plotly.offline.iplot({
            'data': data,
            'layout': self.makeLayout()
        })



class PlotlyTiledFigure(TiledFigure):
    def __init__(self, engine, rows, cols):
        self.rows = rows
        self.rowmarker = 0
        self.cols = cols