from bokeh.client import push_session
from bokeh.document import Document
from bokeh.models import (
    ColumnDataSource, DataRange1d, Plot, LinearAxis, Grid,
    Circle, HoverTool, BoxSelectTool
)
from bokeh.models.widgets import (
    Select, DataTable, TableColumn, StringFormatter, HBox, VBox,
    NumberFormatter, StringEditor, IntEditor, NumberEditor, SelectEditor)
from bokeh.charts import Bar
from bokeh.charts.operations import blend
from bokeh.io import show, output_file

from bokeh.sampledata.autompg import autompg as df

df = df.iloc[:10, :]


class PlotBarsDataTable():
    def __init__(self, df, bar_col_names=['cyl', 'mpg', 'accel']):
        self.df = df
        self.bar_col_names = bar_col_names
        self.document = Document()  # container for all bokeh objects
        # initialize filters and data source for data table
        self.name_filter = None
        self.source = ColumnDataSource()
        self.source.data = ColumnDataSource.from_df(df)  # or initialize with dataframe??
        # create layout
        # add model as the root module of the document ("on_change" callbacks registered)
        self.document.add_root((self.create_layout()))
        # push document to the bokeh server
        self.session = push_session(self.document)

    def create_layout(self):
        df = self.df
        # selector
        names = sorted(df['name'].unique())
        name_select = Select(title='Car names:', value='All', options=['All'] + names)
        name_select.on_change('value', self.on_name_change)

        # data table columns, tablecolumn has width arg!!!
        columns = []
        for col_name in df.columns:
            if col_name == 'name':
                columns += [TableColumn(field=col_name, title=col_name,
                                        editor=SelectEditor(options=names))]
            else:
                columns += [TableColumn(field=col_name, title=col_name)]
        data_table = DataTable(source=self.source, columns=columns, editable=False)

        # bar chart, how to include this?????
        p_bar = Bar(df, label='vars', group='name',
                    values=blend(*self.bar_col_names, name='values', labels_name='vars'),
                    title='mpg cyl accel for car name', color='name', legend=True)
        # tooltips, select tool later!!
        selectors = VBox(children=[name_select])
        plot_panel = HBox(children=[selectors, p_bar])  # order plot left!!??
        self.layout = VBox(children=[plot_panel, data_table])

        return self.layout

    # "on_change" functions
    def on_name_change(self, attr, _, value):
        self.name_filter = None if value == "All" else value
        self.update_data()

    def update_data(self):
        df = self.df
        if self.name_filter:
            df = df[df['name'] == self.name_filter]
        self.source.data = ColumnDataSource.from_df(df)  # deprecated!!
        new_p_bar = Bar(df, label='vars', group='name',
                        values=blend(*self.bar_col_names, name='values', labels_name='vars'),
                        title='mpg cyl accel for car name', color='name', legend=True)
        self.layout.children[0][1] = new_p_bar

    def run(self, do_view=False, poll_interval=0.5):
        if do_view:
            self.session.show()
        self.session.loop_until_closed()

if __name__ == '__main__':
    plot = PlotBarsDataTable(df)
    plot.run(True)
