module Bowline
  module Desktop
    # This class provides a useful abstraction on the Window class.
    # It's the usual way of interfacing with your application's windows, 
    # and all of Bowline's windows normally inherit from it.
    # 
    # You'll need to call the setup! method, before calling using this class.
    # If the window is deallocated (i.e. closed), you'll need to call it again.
    # It's worth not calling the setup method before you need it, since it'll
    # increase the amount of CPU your application uses.
    # 
    # By default, windows are hidden. You'll need to show your custom windows explicitly 
    # before any user interaction using the show method. You can still load HTML/JavaScript
    # while the window is hidden.
    # 
    # If you don't want to show the window until it's properly loaded, you can use this pattern:
    #  MyWindow.on_load { MyWindow.show }
    #  MyWindow.file = :test
    #
    # Any undefined methods are delegated to the Window class.
    # See that class for the full windowing API.
    class WindowManager
      extend Bridge::ClassMethods
      js_expose
      
      extend Bowline::Watcher::Base
      watch :on_load
      
      class << self
        # An array of all the application's windows
        def windows
          Bowline::Desktop::WindowManager.subclasses.map(&:constantize)
        end
        
        # An array of all the application's windows that are currently shown
        def shown_windows
          windows.select(&:shown?)
        end
        
        # An array of all the application's allocated windows
        def allocated_windows
          windows.select(&:allocated?)
        end
        
        # Methods for subclasses:
        
        def window #:nodoc:
          @window
        end
        
        def allocated?
          !deallocated?
        end
        
        def deallocated?
          !@window || @window.deallocated?
        end
        
        # Call this method to allocate a new window.
        # You'll need to do this before using it, 
        # or after it has been closed.
        def setup!
          return unless Desktop.enabled?
          return if allocated?
          if self.name == "MainWindow"
            @window = MainWindow.get
          else
            @window = Window.new
          end
          # Has to be an instance variable since
          # it is getting GCed (even if I mark it).
          @script_callback = Proc.new {|str|
            Bowline::Desktop::Bridge.call(self, str)
          }
          @window.script_callback = @script_callback
          true
        end
        
        # Evaluate JavaScript in this window. Pass
        # a block to capture the result. 
        # Example:
        #   eval("Bowline.msgs") {|res| puts res }
        def eval(*args, &block)
          JS.eval(window, *args, &block)
        end
        
        # Window Proxy instance. Use this to evaluate JavaScript
        # in this window, but in an object orientated way.
        # Example:
        #   page.yourFunc.call
        # 
        # See Bowline::Desktop::Proxy for full usage.
        def page
          Proxy.new(self)
        end
        
        def bowline
          page.Bowline
        end
        
        # Returns true if the both the HTML and JavaScript in
        # this window have loaded. Use the on_load event to know
        # when this window has loaded.
        def loaded?
          @loaded
        end
        
        ##
        # :singleton-method: on_load(method = nil, &block)
        # A Watcher event method that gets called when this window loads.
        # Example:
        #   on_load { puts "Window loaded!" }
        
        def loaded! # :nodoc:
          @loaded = true
          watcher.call(:on_load)
          true
        end
        
        # Delegate most methods to Window
        def method_missing(sym, *args) #:nodoc:
          if window && window.respond_to?(sym)
            return window.send(sym, *args)
          end
          # Window won't be around if Bowline::Desktop isn't enabled
          Bowline::Desktop.enabled? ? super : nil
        end
      end
    end
  end
end