module SimpleModel
  # require all that active support we know and love
  require 'active_support/core_ext/array/extract_options'
  require 'active_support/core_ext/object/blank'
  
  module Attributes
    include ExtendCore

    #Set attribute values to those supplied at initialization
    def initialize(*attrs)
      set_attributes(attrs.extract_options!)
    end

    # Place to store set attributes and their values
    def attributes
      @attributes ||= {}
      @attributes
    end

    def set_attributes(attrs)
      attrs.each do |attr|
        self.send("#{attr[0].to_sym}=",attr[1])
      end
    end
    alias :update_attributes :set_attributes

    def self.included(base)
      base.extend(ClassMethods)
    end
 
    module ClassMethods

      #creates setter and getter datatype special attribute
      def has_attributes(*attrs)
        options = attrs.extract_options!
        attrs.each do |attr|

          attr_reader attr
          define_reader_with_options(attr,options)
          define_method("#{attr.to_s}=") do |val|
            instance_variable_set("@#{attr}", val)
            attributes[attr] = val
            val

          end
        end
      end

      # Creates setter and getter methods for boolean attributes
      def has_booleans(*attrs)
        options = attrs.extract_options!
        attrs.each do |attr|

          attr_reader attr
          define_reader_with_options(attr,options)  
          define_method("#{attr.to_s}=") do |val|
            instance_variable_set("@#{attr}", val.to_s.to_b)
            attributes[attr] = val
            val
          end

          define_method ("#{attr.to_s}?") do
            send("#{attr.to_s}".to_sym).to_s.to_b
          end
        end
      end
     alias :has_boolean :has_booleans

      # Creates setter and getter methods for integer attributes
      def has_ints(*attrs)
        options = attrs.extract_options!
        attrs.each do |attr|
          attr_reader attr
          define_reader_with_options(attr,options)

          define_method("#{attr.to_s}=") do |val|             
            instance_variable_set("@#{attr}", val.to_i)
            attributes[attr] = val
            val

          end
        end
      end
      alias :has_int :has_ints

      # Creates setter and getter methods for currency attributes
      # attributes are cast to BigDecimal and rounded to nearest cent
      # #Warning, rounding occurs on all sets, so if you need to keep higher prescsion
      # use has_decimals
      def has_currency(*attrs)
        options = attrs.extract_options!
        attrs.each do |attr|
          attr_reader attr
          define_reader_with_options(attr,options)
          define_method("#{attr.to_s}=") do |val|
            instance_variable_set("@#{attr}", val.to_s.to_currency)
            attributes[attr] = val
            val
          end
        end
      end

      def has_decimals(*attrs)
        options = attrs.extract_options!
        attrs.each do |attr|
          attr_reader attr
          define_reader_with_options(attr,options)
          define_method("#{attr.to_s}=") do |val|
            instance_variable_set("@#{attr}", BigDecimal("#{val}"))
            attributes[attr] = val
            val
          end
        end
      end
      alias :has_decimal :has_decimals

      # Creates setter and getter methods for float attributes
      def has_floats(*attrs)
        options = attrs.extract_options!
        attrs.each do |attr|

          attr_reader attr
          define_reader_with_options(attr,options)

          define_method("#{attr.to_s}=") do |val|
            instance_variable_set("@#{attr}", val.to_f)
            attributes[attr] = val
            val

          end
        end
      end
      alias :has_float :has_floats
      # Creates setter and getter methods for date attributes
      def has_dates(*attrs)
        options = attrs.extract_options!
        attrs.each do |attr|

          attr_reader attr
          define_reader_with_options(attr,options)
          define_method("#{attr.to_s}=") do |val|   
            val = val.to_date unless val.nil?
            instance_variable_set("@#{attr}", val )
            attributes[attr] = val
            val

          end
        end
      end
      alias :has_date :has_dates
      # Creates setter and getter methods for time attributes
      def has_times(*attrs)
        options = attrs.extract_options!
        attrs.each do |attr|
          attr_reader attr
          define_reader_with_options(attr,options)
          define_method("#{attr.to_s}=") do |val|
            val = val.to_time unless val.nil?
            instance_variable_set("@#{attr}", val)
            attributes[attr] = val
            val
          end
        end
      end

      def fetch_alias_name(attr)
        alias_name = (attr.to_s << "_old=").to_sym
        self.module_eval("alias #{alias_name} #{attr}")

        alias_name
      end

      # Defines a reader method that returns a default value if current value
      # is nil, if :default is present in the options hash
      def define_reader_with_options(attr,options)
        unless options[:default].blank?
          define_method (attr.to_s) do
            default = options[:default].is_a?(Symbol) ? self.send(options[:default]) : options[:default]
            val = instance_variable_get("@#{attr.to_s}")    
            val = default if val.nil?
            val
          end
        end
      end
    end
  end
end