(define-syntax define-record-procedures*
  
  (let ()
    (define (filter-map proc l)
      (if (null? l)
	  '()
	  (let ((result (proc (car l))))
	    (if result
		(cons result (filter-map proc (cdr l)))
		(filter-map proc (cdr l))))))
    
    
    (define (syntax-member? thing stuff)
      (cond
       ((null? stuff) #f)
       ((free-identifier=? thing (car stuff)) #t)
       (else (syntax-member? thing (cdr stuff)))))

    (define (map-with-index proc list)
      (let loop ((i 0) (list list) (rev-result '()))
	(if (null? list)
	    (reverse rev-result)
	    (loop (+ 1 i)
		  (cdr list)
		  (cons (proc i (car list)) rev-result)))))

    (lambda (x)
      (syntax-case x ()
	((_ ?stx
	    ?type-name
	    ?mutable?
	    ?signature-constructor-name
	    ?constructor
	    ?predicate
	    (?field-spec ...))

	 (with-syntax
	     ((number-of-fields (length (syntax->list (syntax (?field-spec ...)))))
	      ((accessor ...)
	       (map (lambda (field-spec)
		      (syntax-case field-spec ()
			((accessor mutator) (syntax accessor))
			(accessor (syntax accessor))))
		    (syntax->list (syntax (?field-spec ...)))))
	      ((mutator ...)
	       (map (lambda (field-spec dummy-mutator)
		      (syntax-case field-spec ()
			((accessor mutator) (syntax mutator))
			(accessor dummy-mutator)))
		    (syntax->list (syntax (?field-spec ...)))
		    (generate-temporaries (syntax (?field-spec ...))))))
	   (with-syntax
	       (((accessor-proc ...)
		 (map-with-index
		  (lambda (i accessor)
		    (with-syntax ((i i)
				  (tag accessor))
		      (syntax-property (syntax/loc
					accessor
					(lambda (s)
					  (when (not (raw-predicate s))
					    (raise
					     (make-exn:fail:contract
					      (string->immutable-string
					       (format "~a: Argument kein ~a: ~e" 
						       'tag '?type-name s))
					      (current-continuation-marks))))
					  (raw-generic-access s i)))
				       'inferred-name
				       (syntax-e accessor))))
		  (syntax->list #'(accessor ...))))
		((our-accessor ...) (generate-temporaries #'(accessor ...)))
		((mutator-proc ...)
		 (map-with-index
		  (lambda (i mutator)
		    (with-syntax ((i i)
				  (tag mutator))
		      (syntax-property (syntax/loc
					mutator
					(lambda (s v)
					  (when (not (raw-predicate s))
					    (raise
					     (make-exn:fail:contract
					      (string->immutable-string
					       (format "~a: Argument kein ~a: ~e" 
						       'tag '?type-name s))
					      (current-continuation-marks))))
					  (raw-generic-mutate s i v)))
				       'inferred-name
				       (syntax-e mutator))))
		  (syntax->list #'(mutator ...))))
		(constructor-proc
		 (syntax-property #'(lambda (accessor ...)
				      (raw-constructor accessor ... #f))
				  'inferred-name
				  (syntax-e #'?constructor)))
		(predicate-proc
		 (syntax-property #'(lambda (thing)
				     (raw-predicate thing))
				  'inferred-name
				  (syntax-e #'?predicate)))
		((raw-accessor-proc ...)
		 (map-with-index (lambda (i _)
				   #`(lambda (r)
				       (raw-generic-access r #,i)))
				 (syntax->list #'(?field-spec ...))))
		((raw-mutator-proc ...)
		 (map-with-index (lambda (i _)
				   #`(lambda (r val)
				       (raw-generic-mutate r #,i val)))
				 (syntax->list #'(?field-spec ...))))

		(record-equal? #`(lambda (r1 r2 equal?)
				   (and #,@(map-with-index (lambda (i field-spec)
							     #`(equal? (raw-generic-access r1 #,i)
								       (raw-generic-access r2 #,i)))
							   (syntax->list #'(?field-spec ...)))))))

				 
	     (with-syntax
		 ((struct-type-defs
		   #'(define-values (type-descriptor
				     raw-constructor
				     raw-predicate
				     raw-generic-access
				     raw-generic-mutate)
		       (make-struct-type
			'?type-name #f (+ 1 number-of-fields) 0
			#f
			(list
			 (cons prop:print-convert-constructor-name
			       '?constructor)
			 (cons prop:custom-write
			       (lambda (r port write?)
				 (custom-write-record '?type-name 
						      (access-record-fields r raw-generic-access number-of-fields)
						      port write?)))
			 (cons prop:print-converter
			       (lambda (r recur)
				 (list '?constructor
				       (recur (raw-accessor-proc r)) ...)))
			 (cons prop:equal+hash
			       (list record-equal?
				     (make-equal-hash (lambda (r i) (raw-generic-access r i)) number-of-fields) 
				     (make-equal2-hash (lambda (r i) (raw-generic-access r i)) number-of-fields)))
			 (cons prop:lazy-wrap
			       (make-lazy-wrap-info constructor-proc
						    (list raw-accessor-proc ...)
						    (list raw-mutator-proc ...)
						    (lambda (r)
						      (raw-generic-access r number-of-fields))
						    (lambda (r val)
						      (raw-generic-mutate r number-of-fields val)))))
			(make-inspector))))
		  (constructor-def #'(define ?constructor constructor-proc))
		  (predicate-def #'(define-values (?predicate real-predicate)
				     (values predicate-proc predicate-proc)))
		  (accessor-defs #'(define-values (accessor ... our-accessor ...)
				     (values accessor-proc ... accessor-proc ...)))
		  (mutator-defs #'(define-values (mutator ...) (values mutator-proc ...)))
		  (signature-def
		   (with-syntax (((?param ...) (generate-temporaries #'(?field-spec ...))))
		     (with-syntax (((component-signature ...)
				    (map (lambda (accessor param)
					   (with-syntax ((?accessor accessor)
							 (?param param))
					     #'(at ?param (property ?accessor ?param))))
					 (syntax->list #'(our-accessor ...))
					 (syntax->list #'(?param ...)))))
		       (with-syntax ((base-signature
				      (stepper-syntax-property
				       #'(define ?type-name 
					   (signature ?type-name (predicate real-predicate)))
				       'stepper-skip-completely 
				       #t))
				     (constructor-signature
				      (stepper-syntax-property
				       (if (syntax->datum #'?mutable?)
					   ;; no lazy signatures
					   #'(define (?signature-constructor-name ?param ...)
					       (signature
						(combined (at ?type-name (predicate real-predicate))
							  component-signature ...)))
					   ;; lazy signatures
					   #'(define (?signature-constructor-name ?param ...)
					       (let* ((sigs (list ?param ...))
						      (sig
						       (make-lazy-wrap-signature '?type-name #t
										 type-descriptor raw-predicate
										 sigs
										 #'?type-name)))
						 (let ((arbs (map signature-arbitrary sigs)))
						   (when (andmap values arbs)
						     (set-signature-arbitrary! 
						      sig
						      (apply arbitrary-record
							     ?constructor
							     (list raw-accessor-proc ...)
							     arbs))))
						 sig)))
				       'stepper-skip-completely
				       #t)))
			 #'(begin
			     ;; we use real-predicate to avoid infinite recursion if a signature
			     ;; for ?type-name using ?predicate is inadvertently defined
			     base-signature
			     constructor-signature))))))
	       ;; again, with properties
	       (with-syntax ((struct-type-defs
			      (stepper-syntax-property
			       (syntax/loc x struct-type-defs) 'stepper-black-box-expr #'?stx))
			     (constructor-def
			      (stepper-syntax-property #'constructor-def 'stepper-skip-completely #t))
			     (predicate-def
			      (stepper-syntax-property #'predicate-def 'stepper-skip-completely #t))
			     (accessor-defs
			      (stepper-syntax-property #'accessor-defs 'stepper-skip-completely #t))
			     (mutator-defs
			      (stepper-syntax-property #'mutator-defs 'stepper-skip-completely #t)))
		 #'(begin
		     signature-def
		     ;; the signature might be used in the definitions, hence this ordering
		     struct-type-defs predicate-def constructor-def accessor-defs mutator-defs))))))
      ((_ ?type-name
	  ?signature-constructor-name
	  ?constructor
	  ?predicate
	  rest)
       (raise-syntax-error 
	#f 
	"Der vierte Operand ist illegal" (syntax rest)))
      ((_ ?type-name
	  ?signature-constructor-name
	  ?constructor
	  ?predicate
	  rest1 rest2 ... (?field-spec ...))
       (raise-syntax-error 
	#f 
	"Vor den Selektoren/Mutatoren steht eine Form zuviel" #'rest1))
      ((_ ?type-name
	  ?signature-constructor-name
	  ?constructor
	  ?predicate
	  rest1 rest2 ...)
       (raise-syntax-error 
	#f 
	"Zu viele Operanden für define-record-procedures*" x))
      ((_ arg1 ...)
       (raise-syntax-error 
	#f 
	"Zu wenige Operanden für define-record-procedures*" x))))))

(define (access-record-fields rec acc count)
  (let recur ((i 0))
    (if (= i count)
	'()
	(cons (acc rec i)
	      (recur (+ i 1))))))

(define (make-equal-hash generic-access field-count)
  (lambda (r recur)
    (let loop ((i 0)
	       (factor 1)
	       (hash 0))
      (if (= i field-count)
	  hash
	  (loop (+ 1 i)
		(* factor 33)
		(+ hash (* factor (recur (generic-access r i)))))))))

(define (make-equal2-hash generic-access field-count)
  (lambda (r recur)
    (let loop ((i 0)
	       (factor 1)
	       (hash 0))
      (if (= i field-count)
	  hash
	  (loop (+ 1 i)
		(* factor 33)
		(+ hash (* factor 
			   (recur (generic-access r (- field-count i 1))))))))))

#|
(define-record-procedures :pare kons pare? (kar kdr))
(kons 1 (kons 2 (kons 3 (kons 5 (kons 6 (kons 7 (kons 8 "asdjkfdshfdsjkf")))))))

prints as:

#<record:pare 1
              #<record:pare 2
                            #<record:pare 3
                                          #<record:pare 5
                                                        #<record:pare 6
                                                                      #<record:pare 7 #<record:pare 8 "asdjkfdshfdsjkf">>>>>>>

|#

(define (custom-write-record name field-values port write?)
  (let ((pp? (and (pretty-printing)
		  (number? (pretty-print-columns)))))

    (write-string "#<" port)
    (write-string "record" port)
    (let ((name (symbol->string name)))
      (when (not (and (positive? (string-length name))
		    (char=? #\: (string-ref name 0))))
	  (write-char #\: port))
      (write-string name port))

    (let-values (((ref-line ref-column ref-pos)
		  (if pp?
		      (port-next-location port)
		      (values 0 -1 0)))) ; to compensate for space
      (for-each
       (if pp?
	   (lambda (field-value)
	     (let* ((max-column (- (pretty-print-columns) 1)) ; > terminator
		    (tentative
		     (make-tentative-pretty-print-output-port
		      port
		      max-column
		      void)))
	       (display " " tentative)
	       ((if write? write display) field-value tentative)
	       (let-values (((line column pos) (port-next-location tentative)))
		 (if (< column max-column)
		     (tentative-pretty-print-port-transfer tentative port)
		     (begin
		       (tentative-pretty-print-port-cancel tentative)
		       (let ((count (pretty-print-newline port max-column)))
			 (write-string (make-string (max 0 (- (+ ref-column 1) count)) #\space) 
				       port)
			 ((if write? write display) field-value port)))))))
	   (lambda (field-value)
	       (display " " port)
	       ((if write? write display) field-value port)))
       field-values)
      
      (write-string ">" port))))

;; (define-record-procedures :pare kons pare? (kar kdr))

(define-syntax define-record-procedures
  (lambda (x)
    (syntax-case x ()
      ((_ ?type-name
          ?constructor
          ?predicate
          (accessor  ...))

       (begin
         (check-for-id!
          (syntax ?type-name)
          "Typ-Name ist kein Bezeichner")
         
         (check-for-id!
          (syntax ?constructor)
          "Konstruktor ist kein Bezeichner")
         
         (check-for-id!
          (syntax ?predicate)
          "Prädikat ist kein Bezeichner")
         
         (check-for-id-list! 
          (syntax->list (syntax (accessor ...)))
          "Selektor ist kein Bezeichner")

         (with-syntax ((?stx x)
		       ((dummy-mutator ...)
                        (generate-temporaries (syntax (accessor ...)))))
           (syntax
            (define-record-procedures* ?stx ?type-name #f
	      dummy-signature-constructor-name
              ?constructor
              ?predicate
              ((accessor dummy-mutator) ...))))))

       ((_ ?type-name
           ?constructor
           ?predicate
           rest)
        (raise-syntax-error 
         #f 
         "Der vierte Operand ist keine Liste von Selektoren" (syntax rest)))
       ((_ ?type-name
           ?constructor
           ?predicate
           rest1 rest2 ... (accessor ...))
        (raise-syntax-error 
         #f 
         "Vor den Selektoren steht eine Form zuviel" #'rest1))
       ((_ ?type-name
           ?constructor
           ?predicate
           rest1 rest2 ...)
        (raise-syntax-error 
         #f 
         "Zu viele Operanden für define-record-procedures" x))
       ((_ arg1 ...)
        (raise-syntax-error 
         #f 
         "Zu wenige Operanden für define-record-procedures" x))
      )))

(define-syntax define-record-procedures-parametric
  (lambda (x)
    (syntax-case x ()
      ((_ ?type-name
	  ?signature-constructor-name
          ?constructor
          ?predicate
          (accessor  ...))


       (begin
	 (check-for-id!
	  (syntax ?type-name)
	  "Record-Name ist kein Bezeichner")

	 (check-for-id!
	  (syntax ?signature-constructor-name)
	  "Signaturkonstruktor-Name ist kein Bezeichner")

         (check-for-id!
          (syntax ?constructor)
          "Konstruktor ist kein Bezeichner")

	 (check-for-id!
	  (syntax ?predicate)
	  "Prädikat ist kein Bezeichner")
         
	 (check-for-id-list! 
	  (syntax->list (syntax (accessor ...)))
	  "Selektor ist kein Bezeichner")

	 (with-syntax ((?stx x)
		       ((dummy-mutator ...)
			(generate-temporaries (syntax (accessor ...)))))
	   (syntax
	    (define-record-procedures* ?stx ?type-name #f ?signature-constructor-name
	      ?constructor
	      ?predicate
	      ((accessor dummy-mutator) ...))))))

      ((_ ?type-name
	  ?signature-constructor-name
	  ?constructor
	  ?predicate
	  rest)
       (raise-syntax-error 
	#f 
	"Der vierte Operand ist keine Liste von Selektoren" (syntax rest)))
      ((_ ?type-name
	  ?signature-constructor-name
	  ?constructor
	  ?predicate
	  rest1 rest2 ...)
       (raise-syntax-error 
	#f 
	"Zu viele Operanden für define-record-procedures-parametric" x))
      ((_ arg1 ...)
       (raise-syntax-error 
	#f 
	"Zu wenige Operanden für define-record-procedures-parametric" x))
      )))

;; (define-record-procedures-2 :pare kons pare? ((kar set-kar!) kdr))

(define-syntax define-record-procedures-2
  (lambda (x)
    (syntax-case x ()
      ((_ ?type-name
	  ?constructor
	  ?predicate
	  (?field-spec ...))

       (begin
	 (check-for-id!
	  (syntax ?type-name)
	  "Record-Name ist kein Bezeichner")
         
	 (check-for-id!
	  (syntax ?constructor)
	  "Konstruktor ist kein Bezeichner")
         
	 (check-for-id!
	  (syntax ?predicate)
	  "Prädikat ist kein Bezeichner")
	 
	 (for-each (lambda (field-spec)
		     (syntax-case field-spec ()
		       ((accessor mutator)
			(check-for-id! (syntax accessor)
				       "Selektor ist kein Bezeichner")
			(check-for-id! (syntax mutator)
				       "Mutator ist kein Bezeichner"))
		       (accessor
			(check-for-id! (syntax accessor)
				       "Selektor ist kein Bezeichner"))))
		   (syntax->list (syntax (?field-spec ...))))

	 (with-syntax ((?stx x))
	   #'(define-record-procedures* ?stx ?type-name #t
	       dummy-signature-constructor-name
	       ?constructor
	       ?predicate
	       (?field-spec ...)))))
      ((_ ?type-name
	  ?constructor
	  ?predicate
	  rest)
       (raise-syntax-error 
	#f 
	"Der vierte Operand ist illegal" (syntax rest)))
      ((_ ?type-name
	  ?constructor
	  ?predicate
	  rest1 rest2 ...)
       (raise-syntax-error 
	#f 
	"Zu viele Operanden für define-record-procedures-2" x))
      ((_ arg1 ...)
       (raise-syntax-error 
	#f 
	"Zu wenige Operanden für define-record-procedures-2" x)))))

(define-syntax define-record-procedures-parametric-2
  (lambda (x)
    (syntax-case x ()
      ((_ ?type-name
	  ?signature-constructor-name
	  ?constructor
	  ?predicate
	  (?field-spec ...))

       (begin
	 (check-for-id!
	  (syntax ?type-name)
	  "Record-Name ist kein Bezeichner")

	 (check-for-id!
	  (syntax ?signature-constructor-name)
	  "Signaturkonstruktor-Name ist kein Bezeichner")

	 (check-for-id!
	  (syntax ?constructor)
	  "Konstruktor ist kein Bezeichner")
         
	 (check-for-id!
	  (syntax ?predicate)
	  "Prädikat ist kein Bezeichner")
	 
	 (for-each (lambda (field-spec)
		     (syntax-case field-spec ()
		       ((accessor mutator)
			(check-for-id! (syntax accessor)
				       "Selektor ist kein Bezeichner")
			(check-for-id! (syntax mutator)
				       "Mutator ist kein Bezeichner"))
		       (accessor
			(check-for-id! (syntax accessor)
				       "Selektor ist kein Bezeichner"))))
		   (syntax->list (syntax (?field-spec ...))))

	 (with-syntax ((?stx x))
	   #'(define-record-procedures* ?stx ?type-name #t ?signature-constructor-name
	       ?constructor
	       ?predicate
	       (?field-spec ...)))))
      ((_ ?type-name
	  ?signature-constructor-name
	  ?constructor
	  ?predicate
	  rest)
       (raise-syntax-error 
	#f 
	"Der vierte Operand ist illegal" (syntax rest)))
      ((_ ?type-name
	  ?signature-constructor-name
	  ?constructor
	  ?predicate
	  rest1 rest2 ...)
       (raise-syntax-error 
	#f 
	"Zu viele Operanden für define-record-procedures-parametric-2" x))
      ((_ arg1 ...)
       (raise-syntax-error 
	#f 
	"Zu wenige Operanden für define-record-procedures-parametric-2" x)))))


