from kivy.adapters.listadapter import ListAdapter
from kivy.adapters.models import SelectableDataItem
from kivy.uix.gridlayout import GridLayout
from kivy.uix.listview import ListView, ListItemButton

from fixtures import fruit_categories, fruit_data_list_of_dicts

from fruit_detail_view import FruitDetailView

# This is an expansion on the "master-detail" example to illustrate
# cascading from the selection of one list view to another. In this
# example the lists are restricted to single selection. The list on the
# left is a simple list. The list in the middle is specialized for
# observing the selection in the first, and using that item as the key
# into a dict providing its own list items. The view on the right is
# the same as the DetailView in the master-detail example.

# A custom adapter is needed here, because we must transform the selected
# fruit category into the list of fruits for that category.


class FruitsListAdapter(ListAdapter):

    def fruit_category_changed(self, fruit_categories_adapter, *args):
        if len(fruit_categories_adapter.selection) == 0:
            self.data = []
            return

        category = \
                fruit_categories[fruit_categories_adapter.selection[0].text]

        # We are responsible with resetting the data. In this example, we are
        # using lists of instances of the classes defined below, CategoryItem
        # and FruitItem. We assume that the names of the fruits are unique,
        # so we look up items by name.
        #
        self.data = \
            [f for f in fruit_data_items if f.name in category['fruits']]

        # Also, see the examples that use dict records.


# FruitsListAdapter subclasses ListAdapter, which has SelectionSupport mixed
# in. SelectionSupport requires that data items handle selection operations.
# This means that we can't have simple strings as data items, nor can we have
# items that don't comply with SelectionSupport needs. It is not difficult to
# make your own data items, however, because you can define custom data item
# classes that subclass SelectableDataItem:
#
class CategoryItem(SelectableDataItem):
    def __init__(self, name='', fruits=None, is_selected=False, **kwargs):
        super(CategoryItem, self).__init__(is_selected=is_selected, **kwargs)
        self.name = name
        self.fruits = fruits if fruits is not None else []
        self.is_selected = is_selected


class FruitItem(SelectableDataItem):
    def __init__(self, name='', data=None, is_selected=False, **kwargs):
        self.serving_size = kwargs.pop('Serving Size', '')
        super(FruitItem, self).__init__(is_selected=is_selected, **kwargs)
        self.name = name
        self.data = data if data is not None else []
        self.is_selected = is_selected


# To instantiate CategoryItem and FruitItem instances, we use the dictionary-
# style fixtures data in fruit_data (See import above), which is
# also used by other list examples. The double asterisk usage here is for
# setting arguments from a dict in calls to instantiate the custom data item
# classes defined above.

# fruit_categories is a dict of dicts.
category_data_items = \
    [CategoryItem(**fruit_categories[c]) for c in sorted(fruit_categories)]

# fruit_data_list_of_dicts is a list of dicts, already sorted.
fruit_data_items = \
    [FruitItem(**fruit_dict) for fruit_dict in fruit_data_list_of_dicts]

# We end up with two normal lists of objects, to be used for two list views
# defined below.


class CascadingView(GridLayout):
    '''Implementation of a master-detail style view, with a scrollable list
    of fruit categories on the left, a list of fruits for the selected
    category in the middle, and a detail view on the right.

    This example uses :class:`ListAdapter`. See an equivalent treatment that
    uses :class:`DictAdapter` in list_cascade_dict.py.
    '''

    def __init__(self, **kwargs):
        kwargs['cols'] = 3
        super(CascadingView, self).__init__(**kwargs)

        list_item_args_converter = \
                lambda row_index, selectable: {'text': selectable.name,
                                               'size_hint_y': None,
                                               'height': 25}

        # Add a fruit categories list on the left. We use ListAdapter, for
        # which we set the data argument to the list of CategoryItem
        # instances from above. The args_converter only pulls the name
        # property from these instances, adding also size_hint_y and height.
        # selection_mode is single, because this list will "drive" the second
        # list defined below. allow_empty_selection is False, because we
        # always want a selected category, so that the second list will be
        # populated. Finally, we instruct ListAdapter to build list item views
        # using the provided cls, ListItemButton.
        #
        fruit_categories_list_adapter = \
            ListAdapter(data=category_data_items,
                        args_converter=list_item_args_converter,
                        selection_mode='single',
                        allow_empty_selection=False,
                        cls=ListItemButton)

        fruit_categories_list_view = \
                ListView(adapter=fruit_categories_list_adapter,
                         size_hint=(.2, 1.0))

        self.add_widget(fruit_categories_list_view)

        # Fruits, for a given category, are in a list in the middle, which
        # uses FruitsListsAdapter, defined above. FruitsListAdapter has a
        # fruit_changed() method that updates the data list. The binding
        # to the fruit_categories_list_adapter is set up after
        # instantiation of the fruit_list_adapter.
        #
        first_category_fruits = \
            fruit_categories[list(fruit_categories.keys())[0]]['fruits']

        first_category_fruit_data_items = \
            [f for f in fruit_data_items if f.name in first_category_fruits]

        fruits_list_adapter = \
                FruitsListAdapter(data=first_category_fruit_data_items,
                                  args_converter=list_item_args_converter,
                                  selection_mode='single',
                                  allow_empty_selection=False,
                                  cls=ListItemButton)

        fruit_categories_list_adapter.bind(
            on_selection_change=fruits_list_adapter.fruit_category_changed)

        fruits_list_view = \
                ListView(adapter=fruits_list_adapter, size_hint=(.2, 1.0))

        self.add_widget(fruits_list_view)

        # Detail view, for a given fruit, on the right:
        #
        detail_view = FruitDetailView(
                fruit_name=fruits_list_adapter.selection[0].text,
                size_hint=(.6, 1.0))

        fruits_list_adapter.bind(
                on_selection_change=detail_view.fruit_changed)
        self.add_widget(detail_view)


if __name__ == '__main__':

    from kivy.base import runTouchApp

    runTouchApp(CascadingView(width=800))
