require 'active_record/associations'

module ActiveRecord

  module Persistence
    module ClassMethods

        alias_method :instantiate_without_save_original, :instantiate

        def instantiate(attributes, column_types = {})
          record = instantiate_without_save_original(attributes, column_types)
          record.save_original
          record
        end
    end
  end

  module Associations

    module ManyToManyTables
    end

    class CollectionProxy       
      
      def schedule_merge(record)        
        proxy_association.reflection.options
        macro = proxy_association.reflection.macro
        if(proxy_association.is_a?(ActiveRecord::Associations::HasManyThroughAssociation))
          handle_has_many_through_schedule_merge(record)
          return
        end  
        if(macro == :has_many)
          handle_has_many_schedule_merge(record)
          return
        end      
      end

      def handle_has_many_schedule_merge(record)
        pk = proxy_association.reflection.options[:primary_key] || proxy_association.owner.class.primary_key
        fk = proxy_association.reflection.options[:foreign_key] || "#{proxy_association.owner.class.to_s.underscore.downcase}_id"
        if(pk.is_a?(Array))
          #puts "is a array"
          pk.each do |pk_item|
            pk_item_val = proxy_association.owner[pk_item.to_s]
            record.send("#{pk_item.to_s}=",pk_item_val)
          end
        else
          pk_val = proxy_association.owner[pk.to_s]        
          record.send("#{fk}=",pk_val) 
        end
        
        record.schedule_merge
        self << record
      end

      def parent_reflection
        proxy_association.reflection.parent_reflection[1]
      end

      def get_m_t_m_table_name
        parent_reflection.options[:join_table] || [proxy_association.owner.class.table_name,proxy_association.klass.table_name].sort.join('_')
      end

      def parent_pk
        parent_reflection.options[:primary_key] || proxy_association.owner.class.primary_key
      end

      def parent_association_pk
        parent_reflection.options[:association_primary_key] || "#{proxy_association.owner.class.to_s.underscore.downcase}_id"
      end

      def child_pk
        parent_reflection.options[:foreign_key] || proxy_association.klass.primary_key
      end

      def child_association_pk
        parent_reflection.options[:association_foreign_key] || "#{proxy_association.klass.to_s.underscore.downcase}_id"
      end
      
      #TODO remove
      alias_method :count_without_merges, :count
      def count
        if defined?(@internal_new_count)
          count_without_merges + ( @internal_new_count.to_i)
        else
          count_without_merges
        end
      end
      
      def handle_has_many_through_schedule_merge(record)        
     # TODO AK doesn't work well 
        self << record 
        
=begin
        join_model = Class.new(ActiveRecord::Base) do
          class << self;           
            attr_accessor :table_info
          end
          def self.table_name
            table_info.table_name
          end
        end
        join_model.table_info = OpenStruct.new(:table_name => get_m_t_m_table_name)        
        unless(ManyToManyTables.const_defined?(get_m_t_m_table_name.camelize))
          ManyToManyTables.const_set get_m_t_m_table_name.camelize,join_model
        end
        #puts "RP DEBUG child_pk: #{child_pk} parent_pk: #{parent_pk}"
        #puts "RP DEBUG child_association_pk: #{child_association_pk} parent_association_pk: #{parent_association_pk}"
        #puts "RP DEBUG table_name: #{get_m_t_m_table_name}"
        c = ManyToManyTables.const_get get_m_t_m_table_name.camelize
        record.schedule_merge
        obj = c.new
        puts "AK: #{obj.class.name}"
        puts "AK: #{obj.class.name}.#{child_association_pk} = #{proxy_association.owner.class.name}.#{parent_pk} #{proxy_association.owner.send(parent_pk)}" 
        puts "AK: #{obj.class.name}.#{child_association_pk} = #{record.class.name}.#{parent_association_pk} #{record.send(parent_association_pk)}" 
        obj.send("#{parent_association_pk}=", proxy_association.owner.send(parent_pk))
        obj.send("#{child_association_pk}=" , record.send(parent_association_pk))
        obj.schedule_merge
        pp obj
        @internal_new_count ||= 0 
        @internal_new_count += 1
=end
      end

    end

    class JoinDependency # :nodoc:

        def instantiate_each(row, &block)
          if row
            primary_id = join_base.record_id(row)
            unless @base_records_hash[primary_id]
              if @base_records_in_order.size > 0
                yield @base_records_in_order.first
                # Die Theorie ist hier ein primary_key der Haupttabelle
                # ist verarbeitet und nun kann der Satz entsorgt werden.
                @base_records_in_order.pop
                # instatiate_each nicht das gesamte Ergebnis durchsucht,
                # wird @base_record_hash nur fuer den Gruppenwechsel
                # verwendet.
                # Bei einem neuen primary_key wird der Hash geleert.
                @base_records_hash = {}
                # record cache leeren
                # join_base.cached_record = {}
                @joins.each { |j| j.cached_record = {} }
              end
              #RP TODO check if instantiate call is correct with an empty hash or where to get the 2nd parameter
              @base_records_in_order << (@base_records_hash[primary_id] = join_base.instantiate(row,{}))
            end
            construct(@base_records_hash[primary_id], @associations, join_associations.dup, row)
          else
            yield @base_records_in_order.first
          end
        end

      class JoinBase 
        attr_accessor :cached_record
        def extract_record(row,column_names_with_alias)
          # if the :select option is set, only the selected field should be extracted
          # column_names_with_alias.inject({}){|record, (cn, an)| record[cn] = row[an] if row.has_key?(an); record}
          record = {}
          column_names_with_alias.each { |(cn, an)| record[cn] = row[an] if row.key?(an) }
          record
        end
        alias_method :instantiate_without_save_original, :instantiate        
        def instantiate(row, aliases)          
          instantiate_without_save_original(row, aliases)
        end
      end
    end
  end
end