require File.join(File.dirname(__FILE__), 'seamless_database_pool', 'connection_statistics.rb')
require File.join(File.dirname(__FILE__), 'seamless_database_pool', 'controller_filter.rb')
require File.join(File.dirname(__FILE__), 'active_record', 'connection_adapters', 'seamless_database_pool_adapter.rb')
require File.join(File.dirname(__FILE__), 'seamless_database_pool', 'railtie.rb') if defined?(Rails::Railtie)

$LOAD_PATH << File.dirname(__FILE__) unless $LOAD_PATH.include?(File.dirname(__FILE__))

# This module allows setting the read pool connection type. Generally you will use one of
#
#   - use_random_read_connection
#   - use_persistent_read_connection
#   - use_master_connection
#
# Each of these methods can take an optional block. If they are called with a block, they
# will set the read connection type only within the block. Otherwise they will set the default
# read connection type. If none is ever called, the read connection type will be :master.

module SeamlessDatabasePool
    
  # Adapter name to class name map. This exists because there isn't an obvious way to translate things like
  # sqlite3 to SQLite3. The adapters that ship with ActiveRecord are defined here. If you use
  # an adapter that doesn't translate directly to camel case, then add the mapping here in an initializer.
  ADAPTER_TO_CLASS_NAME_MAP = {"sqlite" => "SQLite", "sqlite3" => "SQLite3", "postgresql" => "PostgreSQL"}
  
  READ_CONNECTION_METHODS = [:master, :persistent, :random]

  class << self
    # Call this method to use a random connection from the read pool for every select statement.
    # This method is good if your replication is very fast. Otherwise there is a chance you could
    # get inconsistent results from one request to the next. This can result in mysterious failures
    # if your code selects a value in one statement and then uses in another statement. You can wind
    # up trying to use a value from one server that hasn't been replicated to another one yet.
    # This method is best if you have few processes which generate a lot of queries and you have
    # fast replication.
    def use_random_read_connection
      if block_given?
        set_read_only_connection_type(:random){yield}
      else
        Thread.current[:read_only_connection] = :random
      end
    end
  
    # Call this method to pick a random connection from the read pool and use it for all subsequent
    # select statements. This provides consistency from one select statement to the next. This
    # method should always be called with a block otherwise you can end up with an imbalanced read
    # pool. This method is best if you have lots of processes which have a relatively few select
    # statements or a slow replication mechanism. Generally this is the best method to use for web
    # applications.
    def use_persistent_read_connection
      if block_given?
        set_read_only_connection_type(:persistent){yield}
      else
        Thread.current[:read_only_connection] = {}
      end
    end
  
    # Call this method to use the master connection for all subsequent select statements. This
    # method is most useful when you are doing lots of updates since it guarantees consistency
    # if you do a select immediately after an update or insert.
    #
    # The master connection will also be used for selects inside any transaction blocks. It will
    # also be used if you pass :readonly => false to any ActiveRecord.find method.
    def use_master_connection
      if block_given?
        set_read_only_connection_type(:master){yield}
      else
        Thread.current[:read_only_connection] = :master
      end
    end
  
    # Set the read only connection type to either :master, :random, or :persistent.
    def set_read_only_connection_type(connection_type)
      saved_connection = Thread.current[:read_only_connection]
      retval = nil
      begin
        connection_type = {} if connection_type == :persistent
        Thread.current[:read_only_connection] = connection_type
        retval = yield if block_given?
      ensure
        Thread.current[:read_only_connection] = saved_connection
      end
      return retval
    end
  
    # Get the read only connection type currently in use. Will be one of :master, :random, or :persistent.
    def read_only_connection_type(default = :master)
      connection_type = Thread.current[:read_only_connection] || default
      connection_type = :persistent if connection_type.kind_of?(Hash)
      return connection_type
    end
  
    # Get a read only connection from a connection pool.
    def read_only_connection(pool_connection)
      return pool_connection.master_connection if pool_connection.using_master_connection?
      connection_type = Thread.current[:read_only_connection]
    
      if connection_type.kind_of?(Hash)
        connection = connection_type[pool_connection]
        unless connection
          connection = pool_connection.random_read_connection
          connection_type[pool_connection] = connection
        end
        return connection
      elsif connection_type == :random
        return pool_connection.random_read_connection
      else
        return pool_connection.master_connection
      end
    end
  
    # This method is provided as a way to change the persistent connection when it fails and a new one is substituted.
    def set_persistent_read_connection(pool_connection, read_connection)
      connection_type = Thread.current[:read_only_connection]
      connection_type[pool_connection] = read_connection if connection_type.kind_of?(Hash)
    end
  
    def clear_read_only_connection
      Thread.current[:read_only_connection] = nil
    end
    
    # Get the connection adapter class for an adapter name. The class will be loaded from
    # ActiveRecord::ConnectionAdapters::NameAdapter where Name is the camelized version of the name.
    # If the adapter class does not fit this pattern (i.e. sqlite3 => SQLite3Adapter), then add
    # the mapping to the +ADAPTER_TO_CLASS_NAME_MAP+ Hash.
    def adapter_class_for(name)
      name = name.to_s
      class_name = ADAPTER_TO_CLASS_NAME_MAP[name] || name.camelize
      "ActiveRecord::ConnectionAdapters::#{class_name}Adapter".constantize
    end
    
    # Pull out the master configuration for compatibility with such things as the Rails' rake db:*
    # tasks which only support known adapters.
    def master_database_configuration(database_configs)
      configs = {}
      database_configs.each do |key, values|
        if values['adapter'] == 'seamless_database_pool'
          values['adapter'] = values.delete('pool_adapter')
          values = values.merge(values['master']) if values['master'].is_a?(Hash)
          values.delete('pool_weight')
          values.delete('master')
          values.delete('read_pool')
        end
        configs[key] = values
      end
      configs
    end
  end
end
