import pandas as pd
import numpy as np

symbol = 'Security 1'
symbol2 = 'Security 2'

price_data = pd.DataFrame(np.cumsum(np.random.randn(150, 2).dot([[0.5, 0.4], [0.4, 1.0]]), axis=0) + 100,
                          columns=[symbol, symbol2],
                          index=pd.date_range(start='01-01-2007', periods=150))

dates_actual = price_data.index.values
prices = price_data[symbol].values

from bqplot import *
from bqplot.interacts import (
    FastIntervalSelector, IndexSelector, BrushIntervalSelector,
    BrushSelector, MultiSelector, LassoSelector,
)

from ipywidgets import ToggleButtons, VBox, HTML

"""
Explanation: Selectors
Index

Introduction
Brush Selectors
FastIntervalSelector
LassoSelector
IndexSelector
MultiSelector
End of explanation
"""


# Define scales for the rest of the notebook
scales = {'x': DateScale(), 'y': LinearScale()}

"""
Explanation: Introduction <a class="anchor" id="introduction"></a>
Selectors are part of the Interaction Layer (link).
They are used to select subparts of Marks, that correspond to different regions on the Figure canvas. Different types of selectors select different types of regions:
- BrushSelector, FastIntervalSelector and MultiSelector select rectangular regions
- IndexSelector selects the elements closest to an abcissa
- LassoSelector selects elements in a region drawn by the user
How they work
bqplot Selectors need to be tied to two other widgets:
- One or several marks. Their selected attribute, a list of data indices, will be set by the Selector instance.
- One (1d selection) or two (2d selection) Scales. These are the scales that the Selector operates on. The Selector's selected attribute will be expressed as values of those scales.
The Selector must then be passed to the desired Figure, as its interaction attribute.
Hopefully this will be clear in the following examples.
End of explanation
"""


# The Mark we want to select subsamples of
scatter = Scatter(x=dates_actual, y=prices, scales=scales, colors=['orange'],
                 selected_style={'opacity': '1'}, unselected_style={'opacity': '0.2'})
# Create the brush selector, passing it its corresponding scale.
# Notice that we do not pass it any marks for now
brushintsel = BrushIntervalSelector(scale=scales['x'])

x_ax = Axis(label='Index', scale=scales['x'])
x_ay = Axis(label=(symbol + ' Price'), scale=scales['y'], orientation='vertical')
# Pass the Selector instance to the Figure
fig = Figure(marks=[scatter], axes=[x_ax, x_ay],
             title='''Brush Interval Selector Example. Click and drag on the Figure to action.''',
             interaction=brushintsel)

# The following text widgets are used to display the `selected` attributes
text_brush = HTML()
text_scatter = HTML()

# This function updates the text, triggered by a change in the selector
def update_brush_text(*args):
    text_brush.value = "The Brush's selected attribute is {}".format(brushintsel.selected)
def update_scatter_text(*args):
    text_scatter.value = "The scatter's selected indices are {}".format(scatter.selected)
brushintsel.observe(update_brush_text, 'selected')
scatter.observe(update_scatter_text, 'selected')

update_brush_text()
update_scatter_text()

# Display
VBox([fig, text_brush, text_scatter])

"""
Explanation: Brush Selectors <a class="anchor" id="brushselectors"></a>
Selects a rectangular region of the Figure.
Usage:

Click and drag to create a new brush
Drag the edge of the brush to change its width
Drag the inside of the brush to translate it
Clicking and dragging outside of the brush deletes it and creates a new one.
End of explanation
"""


brushintsel.marks = [scatter]

"""
Explanation: Linking the brush to the scatter
Passing a mark (or several) to the selector, will link the mark's selected indices to the selector.
End of explanation
"""


def create_figure(selector, **selector_kwargs):
    '''
    Returns a Figure with a Scatter and a Selector.
    
    Arguments
    ---------
    selector: The type of Selector, one of
        {'BrushIntervalSelector', 'BrushSelector', 'FastIntervalSelector', 'IndexSelector', 'LassoSelector'}
    selector_kwargs: Arguments to be passed to the Selector
    '''
    scatter = Scatter(x=dates_actual, y=prices, scales=scales, colors=['orange'],
                      selected_style={'opacity': '1'}, unselected_style={'opacity': '0.2'})
    sel = selector(marks=[scatter], **selector_kwargs)
    
    text_brush = HTML()
    if selector != LassoSelector:
        def update_text(*args):
            text_brush.value = '{}.selected = {}'.format(selector.__name__, sel.selected)
        sel.observe(update_text, 'selected')
        update_text()

    x_ax = Axis(label='Index', scale=scales['x'])
    x_ay = Axis(label=(symbol + ' Price'), scale=scales['y'], orientation='vertical')
    fig = Figure(marks=[scatter], axes=[x_ax, x_ay], title='{} Example'.format(selector.__name__),
                 interaction=sel)
    return VBox([fig, text_brush])

"""
Explanation: From now on we will stop printing out the selected indices, but rather use the selected_style and unselected_style attributes of the Marks to check which elements are selected.
End of explanation
"""


create_figure(BrushIntervalSelector, orientation='vertical', scale=scales['y'])

"""
Explanation: BrushIntervalSelector on the y-axis
The attribute orientation can be set to 'vertical' to select on the y-axis. Be careful to pass the corresponding y-scale.
End of explanation
"""


create_figure(BrushSelector, x_scale=scales['x'], y_scale=scales['y'])

"""
Explanation: 2d BrushSelector
The BrushSelector is 2d, and must be fed 2 scales, x_scale and y_scale.
Note that BrushSelector.selected is now 2x2. It is the coordinates of the lower left-hand and upper right-hand corners of the rectangle.
End of explanation
"""


create_figure(FastIntervalSelector, scale=scales['x'])

"""
Explanation: FastIntervalSelector <a class="anchor" id="fastintervalselector"></a>
The FastIntervalSelector is functionally like a BrushIntervalSelector, but provides a more fluid and rapid interaction.
Usage:

The first click creates the selector.
Moving the mouse up and down widens and narrows the interval width.
Moving the mouse left and right translates the interval left and right.
Subsequent clicks will freeze/unfreeze the interval width
A double-click will freeze both the width and the translation

Experiment and get a feel for it in the example below.
End of explanation
"""


create_figure(LassoSelector)

"""
Explanation: As of the latest version, FastIntervalSelector is only supported for 1d interaction along the x-axis
LassoSelector <a class="anchor" id="lassoselector"></a>
This 2-D selector enables the user to select multiple sets of data points
by drawing lassos on the figure. 
Usage:

Click and drag to draw a new lasso
Click a lasso to select (de-select) it. Mult
Press the 'Delete' button to delete the selected lassos
End of explanation
"""


create_figure(IndexSelector, scale=scales['x'])

"""
Explanation: IndexSelector <a class="anchor" id="indexselector"></a>
This 1-D selector selects a unique value on its scale. The attached Mark's selected element is the closest element to that value.
Usage:

First click creates and activates the selector 
Moving the mouse translates the selector
Subsequent clicks freeze/unfreeze the selector
End of explanation
"""


create_figure(MultiSelector, scale=scales['x'])

"""
Explanation: As of the latest version, IndexSelector is only supported for interaction along the x-axis.
MultiSelector <a class="anchor" id="multiselector"></a>
This 1-D selector is equivalent to multiple brush selectors.
Usage:

The first brush works like a regular brush.
Ctrl + click creates a new brush, which works like the regular brush. 
The active brush has a Green border while all the inactive brushes have a Red border.
Shift + click deactivates the current active brush. Now, click on any inactive brush to make it active.
Ctrl + Alt + Shift + click clears and resets all the brushes.

Each brush has a name (0, 1, 2, ... by default), and the selected attribute is a dict {brush_name: brush_extent}
End of explanation
"""


