from botstory import matchers
from botstory.ast import callable, forking
from botstory.ast.story_context import reducers

import json
import logging

logger = logging.getLogger(__name__)


class StoryContext:
    def __init__(self, message, library):
        self.library = library
        self.message = message
        self.waiting_for = None

    def is_empty_stack(self):
        return len(self.stack()) == 0

    def could_scope_out(self):
        """
        could bubble up from current scope

        :return:
        """
        return self.waiting_for and \
               not isinstance(self.waiting_for, callable.EndOfStory)

    def current_step(self):
        return self.stack_tail()['step']

    def is_waiting_for_input(self):
        """
        could make one step further
        :return:
        """
        return self.waiting_for and \
               not isinstance(self.waiting_for, forking.SwitchOnValue)

    def compiled_story(self):
        if self.is_empty_stack():
            return self.library.get_global_story(self.message)
        else:
            return self.library.get_story_by_topic(self.stack_tail()['topic'], stack=self.stack()[:-1])

    def stack(self):
        return self.message['session']['stack']

    def stack_tail(self):
        return self.stack()[-1]

    def is_end_of_story(self):
        return self.current_step() >= len(self.compiled_story().story_line)

    def does_it_match_any_story(self):
        return self.compiled_story() is not None

    def is_tail_of_story(self):
        return self.current_step() >= len(self.compiled_story().story_line) - 1

    def has_child_story(self):
        return self.get_child_story() is not None

    def get_current_story_part(self):
        return self.compiled_story().story_line[self.current_step()]

    def get_child_story(self):
        """
        try child story that match message and get scope of it
        :return:
        """
        stack_tail = self.stack_tail()
        story_part = self.compiled_story().story_line[stack_tail['step']]

        if not hasattr(story_part, 'get_child_by_validation_result'):
            return None

        if isinstance(self.waiting_for, forking.SwitchOnValue):
            return story_part.get_child_by_validation_result(self.waiting_for.value)

        if stack_tail['data'] is not None:
            validator = matchers.deserialize(stack_tail['data'])
            validation_result = validator.validate(self.message)
            return story_part.get_child_by_validation_result(validation_result)

        return None

    def user(self):
        return self.message['user']

    def to_json(self):
        return {
            'message': self.message,
            'waiting_for': str(self.waiting_for),
        }

    def __repr__(self):
        return json.dumps(self.to_json())


__all__ = [reducers, StoryContext]

@@ -351,67 +351,244 @@
 def 
-is_empty_stack(self):%0A        return len(self.stack()) == 0
+compiled_story(self):%0A        if self.is_empty_stack():%0A            return self.library.get_global_story(self.message)%0A        else:%0A            return self.library.get_story_by_topic(self.stack_tail()%5B'topic'%5D, stack=self.stack()%5B:-1%5D)
 %0A%0A  
@@ -891,580 +891,138 @@
 def 
-is_waiting_for_input(self):%0A        %22%22%22%0A        could make one step further%0A        :return:%0A        %22%22%22%0A        return self.waiting_for and %5C%0A               not isinstance(self.waiting_for, forking.SwitchOnValue)%0A%0A    def compiled_story(self):%0A        if self.is_empty_stack():%0A            return self.library.get_global_story(self.message)%0A        else:%0A            return self.library.get_story_by_topic(self.stack_tail()%5B'topic'%5D, stack=self.stack()%5B:-1%5D)%0A%0A    def stack(self):%0A        return self.message%5B'session'%5D%5B'stack'%5D%0A%0A    def stack_tail(self):%0A        return 
+does_it_match_any_story(self):%0A        return self.compiled_story() is not None%0A%0A    def is_empty_stack(self):%0A        return len(
 self
@@ -1033,12 +1033,14 @@
 ck()
-%5B-1%5D
+) == 0
 %0A%0A  
@@ -1149,97 +1149,8 @@
 e)%0A%0A
-    def does_it_match_any_story(self):%0A        return self.compiled_story() is not None%0A%0A
     
@@ -1270,188 +1270,221 @@
 def 
-has_child_story(self):%0A        return self.get_child_story() is not None%0A%0A    def get_current_story_part(self):%0A        return self.compiled_story().story_line%5Bself.current_step()%5D
+is_waiting_for_input(self):%0A        %22%22%22%0A        could make one step further%0A        :return:%0A        %22%22%22%0A        return self.waiting_for and %5C%0A               not isinstance(self.waiting_for, forking.SwitchOnValue)
 %0A%0A  
@@ -2262,54 +2262,316 @@
 def 
-user(self):%0A        return self.message%5B'user'
+get_current_story_part(self):%0A        return self.compiled_story().story_line%5Bself.current_step()%5D%0A%0A    def has_child_story(self):%0A        return self.get_child_story() is not None%0A%0A    def stack(self):%0A        return self.message%5B'session'%5D%5B'stack'%5D%0A%0A    def stack_tail(self):%0A        return self.stack()%5B-1
 %5D%0A%0A 
@@ -2707,16 +2707,73 @@
      %7D%0A%0A
+    def user(self):%0A        return self.message%5B'user'%5D%0A%0A
     def 

put story context methods in a order