module Xeroizer
  module Record
    module ModelDefinitionHelper
      
      def self.included(base)
        base.extend(ClassMethods)
        base.send :include, InstanceMethods
      end
      
      module ClassMethods

        # Possible primary keys. At least one of these must exist before attempting to update a record.
        def set_possible_primary_keys(*args)
          args = [args] unless args.is_a?(Array)
          self.possible_primary_keys = args
        end
        
        # Set the actual Xero primary key for this record.
        def set_primary_key(primary_key_name)
          self.primary_key_name = primary_key_name
        end
        
        # Whether this record type's list results contain summary data only.
        #
        # Records like invoices, when returning a list, only show summary data for
        # certain types of associations (like the contact record) and do not return
        # any data for line items.
        #
        # Default: false
        def list_contains_summary_only(status)
          self.summary_only = status
        end
        
        def list_contains_summary_only?
          !!summary_only
        end
              
        # Helper methods used to define the fields this model has.
        def string(field_name, options = {});     define_simple_attribute(field_name, :string, options); end
        def boolean(field_name, options = {});    define_simple_attribute(field_name, :boolean, options); end
        def integer(field_name, options = {});    define_simple_attribute(field_name, :integer, options, 0); end
        def decimal(field_name, options = {});    define_simple_attribute(field_name, :decimal, options, 0.0); end
        def date(field_name, options = {});       define_simple_attribute(field_name, :date, options); end
        def datetime(field_name, options = {});   define_simple_attribute(field_name, :datetime, options); end
        def datetime_utc(field_name, options = {});   define_simple_attribute(field_name, :datetime_utc, options); end
        
        def guid(field_name, options = {})
          # Ensure all automated Id conversions are changed to ID.
          options[:api_name] ||= field_name.to_s.camelize.gsub(/Id/, 'ID')
          define_simple_attribute(field_name, :guid, options)
        end
              
        # Helper method to simplify field definition. 
        # Creates an accessor and reader for the field.
        # Options:
        #   :internal_name => allows the specification of an internal field name differing from the API's field name.
        #   :api_name => allows the API name to be specified if it can't be properly converted from camelize.
        #   :model_name => allows class used for children to be different from it's ndoe name in the XML.
        #   :type => type of field
        #   :skip_writer => skip the writer method
        def define_simple_attribute(field_name, field_type, options, value_if_nil = nil)
          self.fields ||= {}
          
          internal_field_name = options[:internal_name] || field_name
          self.fields[field_name] = options.merge({
            :internal_name  => internal_field_name, 
            :api_name       => options[:api_name] || field_name.to_s.camelize,
            :type           => field_type
          })
          define_method internal_field_name do 
            @attributes[field_name] || value_if_nil
          end
          
          unless options[:skip_writer]
            define_method "#{internal_field_name}=".to_sym do | value | 
              parent.mark_dirty(self) if parent
              @attributes[field_name] = value
            end
          end
        end
        
      end
      
      module InstanceMethods
        
        # Returns the value of the Xero primary key for this record if it exists.
        def id
          self[self.class.primary_key_name]
        end

        # Sets the value of the Xero primary key for this record if it exists.
        def id=(new_id)
          parent.mark_dirty(self) if parent
          self[self.class.primary_key_name] = new_id
        end
        
      end
      
    end
  end
end
