module StorageRoom
  # A collection is used to define the structure of a data set. 
  class Collection < Model      
    key :name
    key :entry_type
    key :primary_field_identifier
    
    many :fields
    many :webhook_definitions
    
    
    class << self                  
      def index_path # :nodoc:
        "#{Resource.base_uri}/collections"
      end
      
      def show_path(collection_id) # :nodoc:
        "#{index_path}/#{collection_id}"
      end
            
      def json_name # :nodoc:
        'collection'
      end
    end
    
    # The class name of the collection's entries, can be overridden with a mapping
    def entry_class_name
      ensure_loaded { StorageRoom.entry_class_for_name(entry_type)}
    end
    
    # The class for the collection's entries
    def entry_class
      require_initialized_entry_class do 
        self.entry_class_name.constantize
      end
    end
    
    # Load all the entries of a collection
    def entries
      require_initialized_entry_class do 
        Array.load(self[:@entries_url])
      end
    end
    
    # Load all deleted entries of a collection
    def deleted_entries
      Array.load(self[:@deleted_entries_url])
    end
    
    # The field with a specific identifier
    def field(identifier)
      ensure_loaded do
        fields.each do |f|
          return f if f.identifier == identifier
        end
      end
      
      nil
    end
    
    # All fields of type AssociationField
    def association_fields
      fields.select{|f| f.is_a?(AssociationField)}
    end
    
    # Load all Collections that are related to the current one through AssociationFields
    def load_associated_collections
      array = association_fields

      if array.map{|f| f.collection_loaded?}.include?(false)
        StorageRoom.log("Fetching associated collections for '#{name}'")
        array.each{|f| f.collection}
      end
    end

    
    protected   
      def initialize_from_response_data
        super
        require_initialized_entry_class
      end
     
      def initialize_entry_class      
        name = self.entry_class_name

        klass = if Object.is_constant_defined?(name.to_sym)  
          name.constantize.tap{|k| raise "Class '#{name}' must inherit from StorageRoom::Entry" unless k.ancestors.include?(Entry)}
        else
          klass = Class.new(Entry) 
          Object.const_set(name, klass)
          klass
        end

        klass.collection = self
        
        fields.each do |field|
          field.add_to_entry_class(klass)
        end
      
        true
      end
    
      def deconstruct_entry_class
        if Object.is_const_defined?(self.entry_class_name.to_sym)
          Object.send(:remove_const, self.entry_class_name) 
        end

        true
      end

      def recreate_entry_class
        self.deconstruct_entry_class
        self.initialize_entry_class
      end

      def require_initialized_entry_class
        ensure_loaded do
          self.initialize_entry_class
          yield if block_given?
        end
      end
  end
end