=begin
  gettext/active_record.rb - GetText for ActiveRecord

  Copyright (C) 2006  Masao Mutoh

  You may redistribute it and/or modify it under the same
  license terms as Ruby.

  $Id: active_record.rb,v 1.9 2006/09/10 15:08:32 mutoh Exp $
=end
require 'gettext'
require 'active_record'

module ActiveRecord #:nodoc:
  class Migration
    extend GetText
    include GetText
  end
  
  module ConnectionAdapters #:nodoc:
    # An abstract definition of a column in a table.
    class Column
      attr_accessor :table_class
      alias :human_name_witout_localized :human_name 

      def human_name
        table_class.human_attribute_name(@name)
      end
    end
  end

  module Validations # :nodoc:
    def self.real_included(base)
      base.extend ClassMethods
      base.class_eval{
	include GetText
	def gettext(str)  #:nodoc:
	  _(str)
	end
	def self.human_attribute_name(attribute_key_name) #:nodoc:
	  s_("#{self}|#{attribute_key_name.humanize}")
	end

	def self.human_attribute_table_name_for_error(table_name) #:nodoc:
	  _(table_name.gsub(/_/, " "))
	end
      }
    end
if ActiveRecord::VERSION::STRING >= "1.14.5"
    def self.included(base) # :nodoc:
      super
      real_included(base)
    end
else
    def self.append_features(base) # :nodoc:
      super
      real_included(base)
    end
end

    module ClassMethods #:nodoc:
      @@custom_error_messages_d = {}
      # Very ugly but...
      def validates_length_of(*attrs)  #:nodoc:
	if attrs.last.is_a?(Hash)
	  msg = attrs.last[:message] || attrs.last[:too_long] || attrs.last[:too_short] || attrs.last[:wrong_length]
	  if msg
	    @@custom_error_messages_d[msg] = /\A#{Regexp.escape(msg).sub(/%d/, '(\d+)')}\Z/ 
	  end
	end
	validates_size_of(*attrs)
      end
      def custom_error_messages_d  #:nodoc:
	@@custom_error_messages_d
      end
    end
    def custom_error_messages_d  #:nodoc:
      self.class.custom_error_messages_d
    end
  end
  
  class Base
    include GetText
    include Validations

    @@gettext_untranslate = Hash.new(false)
    @@gettext_untranslate_columns = {}

    # Untranslate all of the tablename/fieldnames in this model class.
    def self.untranslate_all
      @@gettext_untranslate[self] = true
    end

    # Returns true if "untranslate_all" is called. Otherwise false.
    def self.untranslate_all?
      @@gettext_untranslate[self]
    end

    # Sets the untranslate columns.
    # (e.g.) untranslate :foo, :bar, :baz
    def self.untranslate(*w)
      ary = @@gettext_untranslate_columns[self] || []
      ary += w.collect{|v| v.to_s}
      @@gettext_untranslate_columns[self] = ary
    end

    # Returns true if the column is set "untranslate".
    # (e.g.) untranslate? :foo
    def self.untranslate?(columnname)
      ary = @@gettext_untranslate_columns[self] || []
      ary.include?(columnname)
    end

    def self.untranslate_data #:nodoc:
      [@@gettext_untranslate[self], @@gettext_untranslate_columns[self] || []]
    end

    def self.columns
      unless defined? @columns
	@columns = nil 
      end
      unless @columns
	@columns = connection.columns(table_name, "#{name} Columns")
	@columns.each {|column| 
	  column.table_class = self
	  column.primary = column.name == primary_key
	}
      end
      @columns
    end
    
    # call-seq:
    # set_error_message_title(msg)
    #
    # ((*Deprecated*)) 
    # Use ActionView::Helpers::ActiveRecordHelper::L10n.set_error_message_title
    # instead.
    #
    # Sets a your own title of error message dialog.
    # * msg: [single_msg, plural_msg]. Usually you need to call this with Nn_().
    # * Returns: [single_msg, plural_msg]
    def self.set_error_message_title(msg, plural_msg = nil)
      ActionView::Helpers::ActiveRecordHelper::L10n.set_error_message_title(msg, plural_msg)
    end

    # call-seq:
    # set_error_message_explanation(msg)
    #
    # ((*Deprecated*)) 
    # Use ActionView::Helpers::ActiveRecordHelper::L10n.set_error_message_explanation
    # instead.
    #
    # Sets a your own explanation of the error message dialog.
    # * msg: [single_msg, plural_msg]. Usually you need to call this with Nn_().
    # * Returns: [single_msg, plural_msg]
    def self.set_error_message_explanation(msg, plural_msg = nil)
      ActionView::Helpers::ActiveRecordHelper::L10n.set_error_message_explanation(msg, plural_msg)
    end
  end
  
  # activerecord-1.14.3/lib/active_record/validations.rb
  class Errors #:nodoc:
    include GetText
    alias initialize_without_locale initialize #:nodoc:
    alias full_messages_without_localized full_messages #:nodoc:

    def initialize(base) # :nodoc:
      initialize_without_locale(base)
      bindtextdomain("rails")
    end

    def @@default_error_messages.[]=(id, msg) #:nodoc:
      @@default_error_messages.update({id => msg})
      if [:message, :too_long, :too_short, :wrong_length].include?(id)
	@@default_error_messages_d[msg] = /\A#{Regexp.escape(msg).sub(/%d/, '(\d+)')}\Z/ 
      end
    end    

    # You need to define this here, because this values will be updated by application.
    default_error_messages.update(
				  :inclusion => N_("%{fn} is not included in the list"),
				  :exclusion => N_("%{fn} is reserved"),
				  :invalid => N_("%{fn} is invalid"),
				  :confirmation => N_("%{fn} doesn't match confirmation"),
				  :accepted  => N_("%{fn} must be accepted"),
				  :empty => N_("%{fn} can't be empty"),
				  :blank => N_("%{fn} can't be blank"),
				  :too_long => N_("%{fn} is too long (maximum is %d characters)"),  
				  :too_short => N_("%{fn} is too short (minimum is %d characters)"), 
				  :wrong_length => N_("%{fn} is the wrong length (should be %d characters)"),
				  :taken => N_("%{fn} has already been taken"),
				  :not_a_number => N_("%{fn} is not a number")
				  )
    @@default_error_messages_d = {
      default_error_messages[:too_long] => /#{Regexp.escape(default_error_messages[:too_long]).sub(/%d/, '(\d+)')}/,
      default_error_messages[:too_short] => /#{Regexp.escape(default_error_messages[:too_short]).sub(/%d/, '(\d+)')}/,
      default_error_messages[:wrong_length] => /#{Regexp.escape(default_error_messages[:wrong_length]).sub(/%d/, '(\d+)')}/,
    }
    cattr_accessor :default_error_messages_d

    def localize_error_messages(append_field = true) # :nodoc:
      # e.g.) foo field: "%{fn} foo" => "Foo foo", "foo" => "Foo foo". 
      errors = {}
      @errors.each_key do |attr|
        @errors[attr].each do |msg|
          next if msg.nil?
          custom_msg = nil
          #Ugly but... :-<
          @@default_error_messages_d.dup.merge(@base.custom_error_messages_d).each do |key, regexp|
            if regexp =~ msg
              custom_msg = @base.gettext(key)
              custom_msg = _(msg) if custom_msg == msg 
	      custom_msg = _(custom_msg) % $1.to_i
	      break
            end
          end

          unless custom_msg
            custom_msg = @base.gettext(msg)
            custom_msg = _(msg) if custom_msg == msg 
          end
          if attr == "base"
            full_message = custom_msg
          elsif /%\{fn\}/ =~ custom_msg
            full_message = custom_msg % {:fn => @base.class.human_attribute_name(attr)}
          elsif append_field
            full_message = @base.class.human_attribute_name(attr) + " " + custom_msg
          else
            full_message = custom_msg
          end
	  errors[attr] ||= []
	  errors[attr] << full_message
        end
      end
      errors
    end

    # Returns error messages.
    # * Returns nil, if no errors are associated with the specified attribute.
    # * Returns the error message, if one error is associated with the specified attribute.
    # * Returns an array of error messages, if more than one error is associated with the specified attribute.
    # And for GetText,
    # * If the error messages include %{fn}, it returns formatted text such as "foo %{fn}" => "foo Field"
    # * else, the error messages are prepended the field name such as "foo" => "foo" (Same as default behavior).
    # Note that this behaviour is different from full_messages.
    def on(attribute)
      # e.g.) foo field: "%{fn} foo" => "Foo foo", "foo" => "foo". 
      errors = localize_error_messages(false)
      if errors[attribute.to_s].nil?
        nil
      elsif errors[attribute.to_s].length == 1
        errors[attribute.to_s].first
      else
        errors[attribute.to_s]
      end
    end

    # Returns all the full error messages in an array.
    # * If the error messages include %{fn}, it returns formatted text such as "foo %{fn}" => "foo Field"
    # * else, the error messages are prepended the field name such as "foo" => "Field foo" (Same as default behavior).
    # As L10n, first one is recommanded because the order of subject,verb and others are not same in languages.
    def full_messages
      full_messages = []
      errors = localize_error_messages
      errors.each_key do |attr|
        errors[attr].each do |msg|
	  next if msg.nil?
	  full_messages << msg
	end
      end
      full_messages
    end
  end
end