
require 'every.rb'
require 'StartableThread.rb'


class GuiState < MachineState

    attr_accessor   :frame,
                    :back_label, :forw_label, :ok_label,
                    :back_label2,:forw_label2,
                    :back_action,:forw_action,:ok_action,
                    :back_action2,:forw_action2,
                    :title,
                    :top

    def initialize(name,top,title="")
        super(name)
        @top    = top
        @parent = @top.screen
        @frame  = TkFrame.new(@parent)
        @back_action = @forw_action = @ok_action = 
        @back_action2= @forw_action2= proc{}
        @back_label  = @forw_label  = @ok_label =
        @back_label2 = @forw_label2 = ""
        @title  = title
        
        @debug = true
    end
    
    def on_entry()
        super()
        pdbg "Entered GuiState #{@name}: #{@title}"
        @top.title_label.configure('text'=>@title)
        @frame.pack
        
        set_default_labels_and_buttons
    end

    def on_exit()
        # @parent.pack_slaves.each{|x| x.unpack }
        @frame.unpack
        @top.title_label.configure('text'=>'')
        @machine.confirm_timeout.stop
        super()
    end
    
    def set_default_labels_and_buttons
        # Display navigation labels
        @top.back_label .configure('text'=>@back_label)
        @top.back_label2.configure('text'=>@back_label2)
        @top.ok_label   .configure('text'=>@ok_label)
        @top.forw_label .configure('text'=>@forw_label)
        @top.forw_label2.configure('text'=>@forw_label2)

        # Assign actions to navigation buttons
        @top.clear_buttons()
        @top.back_button.configure('command'=>@back_action )
        @top.ok_button  .configure('command'=>@ok_action )
        @top.forw_button.configure('command'=>@forw_action )
    end
    
    def create_confirm_action( evt )
        this = self     # Copy self to a local variable because 
                        # .. locals are remembered in the proc closure
        proc {
            t = this.top
            t.clear_buttons
            t.clear_nav_labels
            this.gen_event("begin_" + evt)
        }
    end
    
    def back_action_confirm( evt )
        @back_action = create_confirm_action( evt )
        @all_events.concat( confirmation_events( evt ))
    end
    
    def forw_action_confirm( evt )
        @forw_action = create_confirm_action( evt )
        @all_events.concat( confirmation_events( evt ))
    end
    
end


class SimpleGuiState < GuiState

    def navigation_labels=(args) 
        raise(ArgumentError, "Navigation label must be String")   unless args.every{|x| x.kind_of? String}
        
        @back_label, @ok_label, @forw_label = *args
    end

    def next_states=(states)
        raise(ArgumentError, "Next state must be GuiState")   unless states.every{|x| x.nil? or x.kind_of? GuiState}
        back,ok,forw = *states
        @all_events = []
        this_state = self
        if not back.nil?
            @back_action = proc { this_state.machine.on_event("transition_to_"+back.name) }
            all_events << ["transition_to_"+back.name, proc{back}]
        end
        if not ok.nil?
            @ok_action   = proc { this_state.machine.on_event("transition_to_"+ok.name) }
            all_events << ["transition_to_"+ok.name, proc{ok}]
        end
        if not forw.nil?
            @forw_action = proc { this_state.machine.on_event("transition_to_"+forw.name) }
            all_events << ["transition_to_"+forw.name, proc{forw}]
        end
    end
end



class GuiMachine < StateMachine

    attr_accessor :confirm_timeout

    def initialize(*args)
        super(*args)
        
        @confirm_timeout = StartableThread.new{|machine,evt|
            sleep 3
             machine.on_event(evt)
        }
    end
end


#
# confirmation_events 
#   Returns confirmation events for a GuiState
#
#

def confirmation_events(evt)
    [
    [ "begin_" + evt, proc {|evt_name,state|
            t = state.top
            t.navigation_labels=['Cancel',"Sure? \nHit OK to Confirm",'Cancel']
            t.navigation_actions = [ 
                proc{state.gen_event( "cancel_" + evt )},
                proc{state.gen_event( evt )},
                proc{state.gen_event( "cancel_" + evt )}  ]
            # Start timeout
            state.machine.confirm_timeout.start(state.machine, "cancel_" + evt )
            nil } ],
    [ "cancel_" + evt, proc {|evt_name,state| 
        state.machine.confirm_timeout.stop
        state.set_default_labels_and_buttons
        nil} ]
    ]
end
