require 'active_support/core_ext'
require 'netzke/core/ruby_ext'
require 'netzke/core/client_code'
require 'netzke/core/stylesheets'
require 'netzke/core/services'
require 'netzke/core/composition'
require 'netzke/core/plugins'
require 'netzke/core/configuration'
require 'netzke/core/state'
require 'netzke/core/embedding'
require 'netzke/core/actions'
require 'netzke/core/session'
require 'netzke/core/core_i18n'
require 'netzke/core/inheritance'
require 'netzke/core/support'

module Netzke
  # The base class for every Netzke component. Its main responsibilities include:
  # * Client class generation and inheritance (using Ext JS class system) which reflects the Ruby class inheritance (see {Netzke::Core::ClientCode})
  # * Nesting and dynamic loading of child components (see {Netzke::Core::Composition})
  # * Ruby-side action declaration (see {Netzke::Actions})
  # * I18n
  # * Client-server communication (see {Netzke::Core::Services})
  # * Session-based persistence (see {Netzke::Core::State})
  #
  # Client-side methods are documented [here](http://api.netzke.org/client/classes/Netzke.Base.html).
  #
  # == Referring to JavaScript configuration methods from Ruby
  #
  # Netzke allows use Ruby symbols for referring to pre-defined pieces of configuration. Let's say for example, that a toolbar needs to nest a control more complex than a button (say, a date field), and a component should still make it possible to make it's presence and position in the toolbar configurable. We can implement it like this:
  #
  #     action :do_something
  #
  #     def configure(c)
  #       super
  #       c.tbar = [:do_something, :date_selector]
  #     end
  #
  # While :do_something here is referring to a usual Netzke action, :date_selector is not declared in actions. If our JavaScript include file contains a method called `dateSelectorConfig`, it will be executed at the moment of configuring `tbar` at client side, and it's result, a config object, will substitute `date_selector`:
  #
  #     {
  #       dateSelectorConfig: function(config){
  #         return {
  #           xtype: 'datefield'
  #         }
  #       }
  #     }
  #
  # This doesn't necessarily have to be used in toolbars, but also in other places in config (i.e. layouts).
  class Base
    include Core::Session
    include Core::State
    include Core::Configuration
    include Core::ClientCode
    include Core::Services
    include Core::Composition
    include Core::Plugins
    include Core::Stylesheets
    include Core::Embedding
    include Core::Actions
    include Core::CoreI18n
    include Core::Inheritance

    # These are set during initialization
    mattr_accessor :session, :controller, :logger

    attr_reader :parent, :name, :item_id, :path

    # Instantiates a component instance. A parent can optionally be provided.
    def initialize(conf = {}, parent = nil)
      @passed_config = conf

      # parent component
      @parent = parent

      # name fo the component used in the +component+ DSL block, and is a part of component's +@path+
      @name = conf[:name] || self.class.name.underscore

      # path down the composition hierarchy (composed of names)
      @path = parent.nil? ? @name : "#{parent.path}__#{@name}"

      # JS id in the scope of the parent component. Auto-generated when using multiple instance loading.
      # Full JS id will be built using these along the +@path+
      @item_id = conf[:item_id] || @name

      # Make +client_config+ accessible in +configure+ before calling +super+
      client_config = Netzke::Support.permit_hash_params(conf.delete(:client_config))
      config.client_config = HashWithIndifferentAccess.new(client_config)

      # Build complete component configuration
      configure(config)

      # Check whether the config is valid (as specified in a custom override)
      validate_config(config)

      normalize_config

      config.deep_freeze
    end
  end
end
