import sys

from kivy.animation import Animation
from kivy.config import Config
from kivy.lang import Builder
from kivy.metrics import dp
from kivy.properties import *
from kivy.uix.actionbar import ActionBar, ActionItem, ActionPrevious
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.checkbox import CheckBox
from kivy.uix.dropdown import DropDown
from kivy.uix.spinner import Spinner
from kivy.uix.togglebutton import ToggleButton
from kivy.uix.widget import Widget
from kivy.utils import platform as PLATFORM

from material_ui.flatui.flatui import *
from material_ui.flatui.labels import *
from material_ui.flatui.popups import *
from material_ui.navigation.form import Form

from pkg_resources import resource_filename

#KV Files
alphapng = resource_filename( __name__, 'alpha.png' )
path = resource_filename( __name__, 'control.kv' )
Builder.load_file( path )

MOBILE = PLATFORM in ( 'android', 'ios' )

class EmptyNavigationStack( Exception ) :
    '''
    Raised whenever you pop with an empty navigation stack.
    '''
    def __init__( self ) :
        super( EmptyNavigationStack, self ).__init__(
            'Cannot pop view, navigation stack is empty'
        )

class DidPopLastViewException( Exception ) :
    '''
    Raised whenever you pop the last view in the stack.
    '''
    def __init__( self ) :
        super( DidPopLastViewException, self ).__init__(
            'Navigation stack is now empty'
        )

class NavigationController( BoxLayout ) :
    '''
    Custom layout you can use to manage navigation in your app.
    This is inspired by iOS navigation system, but much easier...

    You should put a NavigationController as root widget for your app,
    internally uses a stack to manage navigation ( accessible by using the 'stack' property ).

    You can pop\push views by using 'pop' and 'push' methods.
    '''

    root_widget = ObjectProperty( None )
    '''
    The current widget is stored here.
    '''

    stack = ListProperty( [] )
    '''
    Stack used to navigate between various screens\widgets.
    '''

    background_color = ListProperty( [ .93, .93, .93, 1 ] )
    '''
    Solid background color.
    '''

    animation_duracy = NumericProperty( 0 )#.25 )
    '''
    Push & pop animation duracy, default 0.25 seconds.
    '''

    push_mode = OptionProperty( 'right', options=['left','right'] )
    '''
    Left or right, will push/pop views from/to the given direction.
    '''

    disable_widget = BooleanProperty( False ) 
    '''
    If true, root widget will be disabled during animations.
    This will hide animations!
    '''

    #Navigation bar

    title = StringProperty( 'Navigation control!' )
    '''
    Navigation bar title.
    '''    
    
    nav_height = NumericProperty( dp(56) )
    '''
    Navigation bar height.
    '''
    
    nav_color = ListProperty( [ .1, .11, .11, 1] )
    '''
    Navigation bar color.
    '''

    shadow_alpha = NumericProperty( .065 )
    '''
    Alpha channel for navigation bar shadow.
    '''

    font_name = StringProperty( None )
    '''
    Navigation bar font name.
    '''

    font_size = NumericProperty( dp(22) )
    '''
    Navigation bar font size.
    '''

    text_color = ListProperty( [1,1,1,1] )
    '''
    Navigation bar text color.
    '''

    floating_panel = ObjectProperty( None )
    '''
    FloatingLayout you can use for any pourpose.
    '''

    floating_action = ObjectProperty( None )
    '''
    FloatingAction managed by the controller.
    Press 'Menu' key to fire the on-press event.
    '''

    splash_image = StringProperty( alphapng )
    '''
    Image used as splash screen.
    '''

    #Private stuffs...
    _push_cache = ListProperty( [] )
    _actionprev = ObjectProperty( None )
    _actiontext = ObjectProperty( None )
    _content    = ObjectProperty( None )
    _width      = NumericProperty( float(Config.get('graphics','width')) )

    def __init__( self, **kargs ) :
        super( NavigationController, self ).__init__( **kargs )
        self._keyboard_show = False
        self._keyboard_just_show = False
        self._has_root = False
        self._last_args = {'title':'', 'animation':None}
        self._animation = None
        self._bind_keyboard()        
        self.push( Form( shared_navigation_controller=self ) )

    def pop( self, *args ) :
        '''
        Use this to go back to the last view.
        Will eventually throw EmptyNavigationStack.
        '''

        if self._animation is None :
            if len( self.stack ) > 1 :
                if len( self.stack ) > 2 : 
                    try : 
                        self.root_widget.on_pop( self )
                    except : pass
                    self._save_temp_view( 0, self.root_widget )
                    self._run_pop_animation()
                else :
                    raise DidPopLastViewException()
            else :
                raise EmptyNavigationStack()
                        
    def push( self, view, **kargs ) :
        '''
        Will append the last view to the list and show the new one.
        Keyword arguments :
            title
                Navigation bar title, default ''.
        '''
    
        if self._animation is None :
            if not 'title' in kargs.keys() : kargs['title'] = ''
            self._last_kargs = kargs
            x = -1 if self.push_mode == 'left' else 1
            self._save_temp_view( x, view )
            self._run_push_animation()

#================================================================================
# Private stuff of various use...
#================================================================================

    def _bind_keyboard(self) :
        EventLoop.window.bind( on_keyboard=self._on_keyboard_show )
        EventLoop.window.bind( on_key_down=self._on_keyboard_down )

    def _on_keyboard_show( self, *args ) :
        self._keyboard_show = True

    def _on_keyboard_down( self, window, key, *args ) :
    
        if key == 319 : # Menu
            if self.floating_action :
                self.floating_action.on_press()
                return True

        if key == 27 : # Escape
            if self._keyboard_show : 
                self._keyboard_show = False
                EventLoop.window.release_all_keyboards()
            else : 
                self.pop()
            return True
        return False

    def _run_push_animation( self ) :
        try : 
            self._temp_view.disabled = self.disable_widget 

            if self._has_root :
                self._animation = Animation( x=0, duration=self.animation_duracy )
                self._animation.bind( on_complete=self._push_temp_view )
                self._animation.start( self._temp_view ) 
            else : 
                self._push_temp_view()
        except : pass #Exception as e : print(e)

    def _run_pop_animation( self ) :
        try : 
            self._temp_view.disabled = self.disable_widget 
            x = self._temp_view.width * ( -1 if self.push_mode == 'left' else 1 )
            self._animation = Animation( x=x, duration=self.animation_duracy )
            self._animation.bind( on_complete=self._pop_temp_view )
            self._animation.start( self._temp_view ) 
        except : pass #Exception as e : print(e)

    def _push_temp_view( self, *args ) :
        self._temp_view.disabled = False
                
        if self._has_root :            
            self.content.remove_widget( self.root_widget )

        self.stack.append( [ self.root_widget, self._last_kargs ] )
        self.root_widget = self._temp_view
        self._clear_temp_view()
        self.content.add_widget( self.root_widget )
        self._has_root = True
        self._update_nav()
        self._animation = None
        try : 
            self.root_widget.on_push( self )
        except : pass

    def _pop_temp_view( self, *args ) :
        self._temp_view.disabled = False
        self.content.remove_widget( self.root_widget )
        self.root_widget, self._last_kargs = self.stack.pop()

        if len(self.stack) > 1 : 
            self._last_kargs = self.stack[-1][1]        

        self.content.add_widget( self.root_widget )
        self._update_nav()
        self._animation = None

    def _clear_temp_view( self, *args ) :
        try : 
            self.floating_panel.remove_widget( self._temp_view )
        except : pass
        self._temp_view = None  

    def _save_temp_view( self, p, view ) :
        self._temp_view = view
        try : 
            self._temp_view.pos = [ self._width*p, 0 ]
            self.floating_panel.add_widget( self._temp_view )
        except : pass

    def _update_nav( self ) :
        self.title = self._last_kargs['title']
        has_previous = len( self.stack ) > 2 
        self.actionprev.text = ' < ' if has_previous else ''
        self.actionprev.disabled = not has_previous