module MassiveRecord
  module ORM
    module IdFactory

      #
      # A factory class for unique IDs for any given tables.
      #
      # Usage:
      #   AtomicIncrementation.next_for(:cars) # => 1
      #   AtomicIncrementation.next_for(:cars) # => 2
      #   AtomicIncrementation.next_for(AClassRespondingToTableName)         # => 1
      #   AtomicIncrementation.next_for("a_class_responding_to_table_names") # => 2
      #
      #
      # Storage:
      #   Stored in id_factories table, under column family named tables.
      #   Field name equals to tables it has generated ids for, and it's
      #   values is integers (if the adapter supports it).
      #
      class AtomicIncrementation < Table
        include IdFactory

        COLUMN_FAMILY_FOR_TABLES = :tables
        ID = "id_factory"

        set_table_name "id_factories"

        column_family COLUMN_FAMILY_FOR_TABLES do
          autoload_fields :type => :integer
        end

        #
        # Returns the factory, singleton class.
        # It will be a reloaded version each time instance
        # is retrieved, or else it will fetch self from the
        # database, or if all other fails return a new of self.
        #
        def self.instance
          if table_exists?
            begin
              if @instance
                @instance.reload # If, for some reason, the record has been removed. Will be rescued and set to nil
              else
                @instance = find(ID)
              end
            rescue RecordNotFound
              @instance = nil
            end
          end

          @instance = new unless @instance
          @instance
        end



        def id
          ID
        end

        private

        #
        # Method which actually does the increment work for
        # a given table name as string
        #
        def next_id(options = {}) 
          options.assert_valid_keys(:table)
          table_name = options.delete :table

          create_field_or_ensure_type_integer_for(table_name)
          atomic_increment!(table_name)
        end




        def create_field_or_ensure_type_integer_for(table_name)
          if has_field_for? table_name
            ensure_type_integer_for(table_name)
          else
            create_field_for(table_name)
          end
        end


        #
        # Creates a field for a table name which is new
        # Feels a bit hackish, hooking in and doing some of what the
        # autoload-functionality of column_family block above does too.
        # But at least, we can "dynamicly" assign new attributes to this object.
        #
        def create_field_for(table_name)
          add_field_to_column_family COLUMN_FAMILY_FOR_TABLES, table_name, :integer, :default => 0
        end
        
        #
        # Just makes sure that definition of a field is set to integer.
        # This is needed as the autoload functionlaity sets all types to strings.
        #
        def ensure_type_integer_for(table_name)
          column_family_for_tables.field_by_name(table_name).type = :integer
          self[table_name] = 0 if self[table_name].blank?
        end


        def has_field_for?(table_name)
          respond_to? table_name
        end

        def column_family_for_tables
          @column_family_for_tables ||= column_families.family_by_name(COLUMN_FAMILY_FOR_TABLES)
        end
      end
    end
  end
end
