module Globalize
  module Model
    class AttributeStash < Hash
      def contains?(locale, attr_name)
        locale = locale.to_sym
        self[locale] ||= {}
        self[locale].has_key? attr_name        
      end
      
      def read(locale, attr_name)
        locale = locale.to_sym
        self[locale] ||= {}
        self[locale][attr_name]
      end
      
      def write(locale, attr_name, value)
        locale = locale.to_sym
        self[locale] ||= {}
        self[locale][attr_name] = value
      end
    end
    
    class Adapter
      def initialize(record)
        @record = record
        
        # TODO what exactly are the roles of cache and stash
        @cache = AttributeStash.new
        @stash = AttributeStash.new
      end
      
      def fetch(locale, attr_name)
        # locale = I18n.locale
        is_cached = @cache.contains?(locale, attr_name)
        is_cached ? @cache.read(locale, attr_name) : begin
          value = fetch_attribute locale, attr_name
          @cache.write locale, attr_name, value if value && value.locale == locale
          value
        end
      end
      
      def stash(locale, attr_name, value)
        @stash.write locale, attr_name, value
        @cache.write locale, attr_name, value
      end
      
      def update_translations!
        @stash.each do |locale, attrs|
          translation = @record.globalize_translations.find_or_initialize_by_locale(locale.to_s)
          attrs.each{|attr_name, value| translation[attr_name] = value }
          translation.save!
        end
        @stash.clear
      end
      
      # Clears the cache
      def clear
        @cache.clear
        @stash.clear
      end
      
      private
      
      def fetch_attribute(locale, attr_name)
        fallbacks = I18n.fallbacks[locale].map{|tag| tag.to_s}.map(&:to_sym)
        
        # If the translations were included with 
        # :include => globalize_translations
        # there is no need to query them again.
        unless @record.globalize_translations.loaded?
          translations = @record.globalize_translations.by_locales(fallbacks) 
        else
          translations = @record.globalize_translations
        end
        result, requested_locale = nil, locale

        # Walk through the fallbacks, starting with the current locale itself, and moving
        # to the next best choice, until we find a match.
        # Check the @globalize_set_translations cache first to see if we've just changed the 
        # attribute and not saved yet.
        fallbacks.each do |fallback|
          # TODO should we be checking stash or just cache?
          result = @stash.read(fallback, attr_name) || begin
            translation = translations.detect {|tr| tr.locale == fallback }
            translation && translation.send(attr_name)
          end
          if result
            locale = fallback
            break
          end
        end
        result && Translation::Attribute.new(result, :locale => locale, :requested_locale => requested_locale)
      end
    end
  end
end
