require 'massive_record/orm/persistence/operations/table_operation_helpers'

module MassiveRecord
  module ORM
    module Persistence
      module Operations
        class AtomicOperation
          include Operations, TableOperationHelpers

          def execute
            raise NotNumericalFieldError unless record.attributes_schema[attr_name].type == :integer

            ensure_that_we_have_table_and_column_families!
            ensure_proper_binary_integer_representation(attr_name)
            record[attr_name] = row_for_record.send("atomic_#{operation}", record.attributes_schema[attr_name].unique_name, by)
          ensure
            record.instance_variable_set(:@new_record, false)
          end


          private

          #
          # To cope with the problem which arises when you ask to
          # do atomic incrementation / decrementation of an attribute and that attribute
          # has a string representation of a number, like "1", instead of
          # the binary representation, like "\x00\x00\x00\x00\x00\x00\x00\x01".
          #
          # We then need to re-write that string representation into
          # hex representation. Now, if you are on a completely new
          # database and have never used MassiveRecord before we should not
          # need to do this at all; numbers are now stored as hex, but for
          # backward compatibility we are doing this.
          #
          # Now, there is a risk of doing this; if two calls are made to
          # atomic_increment! or atomic_decrement! on a record where it's value is a string
          # representation this operation might be compromised. Therefor
          # you need to enable this feature.
          #
          def ensure_proper_binary_integer_representation(attr_name)
            return if !klass.backward_compatibility_integers_might_be_persisted_as_strings || record.new_record?

            field = record.attributes_schema[attr_name]

            if raw_value = klass.table.get(record.id, field.column_family.name, field.name)
              store_record_to_database('update', [attr_name]) if raw_value =~ /\A\d*\Z/
            end
          end


          def operation
            options[:operation] or raise "Missing option :operation"
          end

          def attr_name
            @attr_name ||= if options[:attr_name].present?
                             options[:attr_name].to_s
                           else
                             raise "Missing option :attr_name"
                           end
          end

          def by
            options[:by] or raise "Missing option :by"
          end
        end
      end
    end
  end
end
        
