from typing import Callable

from amino import List, __, _

from lenses import lens

from ribosome.record import field, list_field, bool_field

from myo.record import Record
from myo.ui.tmux.session import Session, VimSession
from myo.ui.tmux.window import VimWindow, Window
from myo.ui.tmux.layout import VimLayout, Layout
from myo.ui.tmux.pane import VimPane
from myo.util import Ident
from myo.components.tmux.watcher import Watcher

_is_vim_session = lambda a: isinstance(a, VimSession)
_is_vim_window = lambda a: isinstance(a, VimWindow)
_is_vim_layout = lambda a: isinstance(a, VimLayout)
_is_vim_pane = lambda a: isinstance(a, VimPane)


class TmuxState(Record):
    initialized = bool_field()
    sessions = list_field()
    instance_id = field(str, initial='', factory=lambda a: a if a else List.random_string(5))
    watcher = field(Watcher)

    @property
    def _str_extra(self):
        return self.sessions

    def session_lens(self, pred: Callable[[Session], bool]):
        return self.attr_lens_pred(_.sessions, pred) / __.bind(self)

    @property
    def vim_session(self):
        return self.sessions.find(_is_vim_session).to_either('no vim session')

    @property
    def vim_session_lens(self):
        return (self.sessions.find_lens_pred(_is_vim_session) /
                lens(self).sessions.add_lens)

    def window_lens(self, pred: Callable[[Window], bool]):
        sub = __.attr_lens_pred(_.windows, pred)
        return (
            (self.attr_lens(_.sessions, sub) / __.bind(self))
            .to_either('no such window: {}'.format(pred))
        )

    def window_lens_ident(self, ident: Ident):
        return self.window_lens(__.has_ident(ident))

    @property
    def vim_window(self):
        return (
            (self.vim_session // __.windows.find(_is_vim_window))
            .to_either('no vim window')
        )

    @property
    def vim_window_lens(self):
        return self.window_lens(_is_vim_window)

    def layout_lens(self, pred: Callable[[Layout], bool]):
        lay_sub = __.root.layout_lens(pred).map(lens().root.add_lens)
        win_sub = __.attr_lens(_.windows, lay_sub)
        return self.attr_lens(_.sessions, win_sub) / __.bind(self)

    def layout_lens_ident(self, ident: Ident):
        return self.layout_lens(__.has_ident(ident))

    @property
    def vim_layout(self):
        return self.vim_window // __.root.layouts.find(_is_vim_layout)

    @property
    def vim_layout_lens(self):
        return self.layout_lens(_is_vim_layout)

    def pane_lens(self, pred: Callable[[Layout], bool]):
        lay_sub = __.root.pane_lens(pred).map(lens().root.add_lens)
        win_sub = __.attr_lens(_.windows, lay_sub)
        return self.attr_lens(_.sessions, win_sub) / __.bind(self)

    def pane_lens_ident(self, ident: Ident):
        return self.pane_lens(__.has_ident(ident))

    @property
    def vim_pane(self):
        return self.vim_layout / _.panes / _.head

    @property
    def all_windows(self):
        return self.sessions // _.windows

    def window(self, ident: Ident):
        return self.all_windows.find(__.has_ident(ident))

    @property
    def all_panes(self):
        return self.all_windows // _.root.all_panes

    def pane(self, ident):
        return self.all_panes.find(__.has_ident(ident))

    @property
    def possibly_open_panes(self):
        return (
            self.all_panes
            .filter(_.id.present)
            .filter(lambda a: not _is_vim_pane(a))
        )

__all__ = ('TmuxState',)
