module CouchSurfer
  module Associations
    module ClassMethods
      def has_many *args
        options = extract_options!(args)
        children = args.first
        if options[:inline]
          name =  ::Extlib::Inflection.camelize(children.to_s.singular)
          cast children, :as => [name]
          define_method children do |*args|
            kinder = self[children.to_s]
            klass = ::Extlib::Inflection.constantize(name)
            if kinder.kind_of?(Array)
              self[children.to_s] =  kinder.map{|c| klass.send(:new, c)} unless kinder.first.kind_of?(klass)
            else
              self[children.to_s] = []
            end
            self[children.to_s]
          end
          return
        end
        if options[:through]
          define_method_for_children(options[:through], options)
          define_method children do
            name = (options[:class_name] || children).to_s.singular
            class_name =  ::Extlib::Inflection.camelize(name)
            klass = ::Extlib::Inflection.constantize(class_name)
            through_items = self.send("#{options[:through]}")
            query ||= {:keys => through_items.map{|child| child.send("#{name}_id")}}
            view_name ||= "by_#{self.class.name.downcase}_id"
            klass.send("all", query)
          end
          return
        end
        define_method_for_children(children, options, options[:class_name])
      end
      
      def belongs_to *args
        options = extract_options!(args)
        parent = args.first
        parent_name = (options[:class_name] || parent).to_s
        define_method parent do
          class_name = ::Extlib::Inflection.camelize(parent_name)
          klass = ::Extlib::Inflection.constantize(class_name)
          parent_id = self["#{parent_name}_id"]
          if parent_id
            klass.send(:get, self["#{parent_name}_id"])
          end
        end

        define_method "#{parent}=".to_sym do |parent_obj|
          self["#{parent_obj.class.name.downcase}_id"] = parent_obj.id
        end
      end
      
      private
      
      def extract_options!(args)
        args.last.is_a?(Hash) ? args.pop : {}
      end
      
      def define_method_for_children(children, options, name = nil)
        class_name =  ::Extlib::Inflection.camelize(name || children.to_s.singular)
        define_method children do
          klass = ::Extlib::Inflection.constantize(class_name)
          if options[:view]
            view_name = options[:view]
          end
          if options[:query].is_a?(Proc)
             query = self.instance_eval(&options[:query])
          end
          view_name ||= "by_#{self.class.name.downcase}_id"
          query ||= {:key => self.id}
          klass.send(view_name, query)
        end
      end
    end
    
    def self.included(receiver)
      receiver.extend         ClassMethods
    end
  end
end