module CacheAdvance
  class NamedCache

    ENABLED_CHECK_INTERVAL = 60
    
    def initialize(name, params, cache_set, store)
      @name = name.to_s
      @params = params
      @cache_set = cache_set
      @store = store
      @cached_key_list = CachedKeyList.new(@store, "#{@name}/STORED_CACHES", expiration_time)
      @enabled_check_time = Time.now + ENABLED_CHECK_INTERVAL
      @enabled = nil
    end
        
    def value_for(request, options, &block)
      return block.call unless enabled?
      
      key = key_for(request, options[:key])
      
      if (value = read_from_store(key))
        each_plugin { |p| p.send('after_read', @name, key, request, value) if p.respond_to?('after_read') }
        return value
      end
      
      each_plugin { |p| p.send('before_render', @name, key, request) if p.respond_to?('before_render') }
      result = block.call
      each_plugin { |p| p.send('after_render', @name, key, request, result) if p.respond_to?('after_render') }
      each_plugin { |p| p.send('before_write', @name, key, request, result) if p.respond_to?('before_write') }
      write_to_store(key, result)
      each_plugin { |p| p.send('after_write', @name, key, request, result) if p.respond_to?('after_write') }      
      result
    end
        
    def expire_for(type)
      if expiration_types.include?(type)
        expire_all
      end
    end
    
    def expire_all
      delete_all_from_store
    end
        
    def all_cached_keys
      @cached_key_list.all_keys
    end
    
    def expiration_types
      Array(@params[:expiration_types])
    end
    
    def title
      @params[:title] || @name.to_s
    end
    
    def enabled=(state)
      @enabled = !!state
      write_to_store(enabled_key, @enabled, false)
    end
    
    def enabled?
      if @enabled.nil? || Time.now >= @enabled_check_time
        @enabled = [nil, true].include?(read_from_store(enabled_key))
        @enabled_check_time = Time.now + ENABLED_CHECK_INTERVAL
      end
      @enabled
    end
    
    protected
        
    def read_from_store(key)
      @store.get(key)
    end
    
    def write_to_store(key, value, add_to_key_list=true)
      expiration_time ? @store.set(key, value, expiration_time) : @store.set(key, value)
      if add_to_key_list
        @cached_key_list.add_key(key)
      end
    end

    def delete_from_store(key)
      @store.delete(key)
      @cached_key_list.delete_key(key)
    end
        
    def delete_all_from_store
      @cached_key_list.all_keys.each { |key| delete_from_store(key) }
      @cached_key_list.clear
    end
        
    def each_plugin
      @cache_set.plugins.each do |p|
        yield p
      end
    end
    
    def key_for(request, suffix='')
      qualifier_data = qualifiers.map do |q|
        if (qualifier = @cache_set.qualifiers[q])
          (qualifier.call(request) || '').to_s
        end
      end.join('/')
      "#{@name}/#{suffix}/[#{qualifier_data}]"
    end
        
    def enabled_key
      "#{@name}/ENABLED_STATUS"
    end  
      
    def expiration_time
      @params[:expiration_time]
    end
    
    def qualifiers
      Array(@params[:qualifiers])
    end
  end
end