;; "Entwicklungsumgebung:"
;; - gnucash aus Kommandozeile starten
;; - http://wiki.gnucash.org/wiki/Custom_Reports
;; - http://download.plt-scheme.org/doc/360/html/t-y-scheme/t-y-scheme-Z-H-1.html

;; TODO:
;; - Berechnungen: Anzahl der Nachkommastellen aus Währung

;; ------------------------------------------------------------------
;; Top-level definitions
;; ------------------------------------------------------------------

(define-module (gnucash report prototype))
(use-modules (gnucash gnc-module))
(gnc:module-load "gnucash/report/report-system" 0)

(gnc:module-load "gnucash/html" 0) ;for gnc-build-url

;; ------------------------------------------------------------------
;; Define the Options for this report
;; ------------------------------------------------------------------

(define (options-generator)    
  (let* ((options (gnc:new-options))

    ;; This is just a helper function for making options.
    ;; See gnucash/src/app-utils/options.scm for details.
    (add-option
      (lambda (new-option)
	(gnc:register-option options new-option))))

    (add-option
      (gnc:make-account-list-option
	(N_ "Konten") (N_ "Konten positiv")
	"g" (N_ "Konten die sich positiv auf die Summe auswirken")
	(lambda () '())
	#f #t))

    (add-option
      (gnc:make-account-list-option
	(N_ "Konten") (N_ "Konten negativ")
	"g" (N_ "Konten die sich negativ auf die Summe auswirken")
	(lambda () '())
	#f #t))

    options))

;; ------------------------------------------------------------------
;; Render the HTML document
;; ------------------------------------------------------------------


(define (output-splits splits accounts-positive accounts-negative)
  (let ((table (gnc:make-html-table)))
    (calculate-output table splits accounts-positive accounts-negative (double-to-gnc-numeric 0 100 GNC-RND-ROUND))
    (gnc:html-table-set-col-headers! table (list
      'Datum
      'Beschreibung
      'Konto
      "Betrag (€)"
      "Saldo (€)"
    ))
    table ))

(define (calculate-output table splits accounts-positive accounts-negative previous-sum)
  (if (null? splits)
    '()
    (let* (
      (split (car splits))
      (transaction (xaccSplitGetParent split))
      (account (xaccSplitGetAccount split))
      (sum 
	(if (member account accounts-positive)
	  (gnc-numeric-add previous-sum (xaccSplitGetAmount split) GNC-DENOM-AUTO GNC-RND-ROUND)
	  (gnc-numeric-sub previous-sum (xaccSplitGetAmount split) GNC-DENOM-AUTO GNC-RND-ROUND))))
      (cons
	(gnc:html-table-append-row! table (list
	  (gnc-print-date (gnc-transaction-get-date-posted transaction))
	  (xaccTransGetDescription transaction)
	  (gnc-account-get-full-name account)
	  (xaccSplitGetAmount split)
	  (gnc-numeric-div sum (double-to-gnc-numeric 2 100 GNC-RND-ROUND) 100 GNC-RND-ROUND)
	))
	(calculate-output table (cdr splits) accounts-positive accounts-negative sum)))))

(define (merge-account-splits accounts)
  (if (null? accounts) 
    '()
    (merge-split-lists
      (xaccAccountGetSplitList (car accounts))
      (merge-account-splits (cdr accounts)))))

(define (merge-split-lists L M)         ; assume L and M are sorted lists
  (cond 
    ( (null? L) M) ; if one list is empty, return the other list
    ( (null? M) L)
    (else
      ; determine the transaction dates of the first split in each list
      (let ((date-L (gnc-transaction-get-date-posted(xaccSplitGetParent(car L))))
	    (date-M (gnc-transaction-get-date-posted(xaccSplitGetParent(car M)))))
	; return the smallest element first, then continue recursively with all other elements
	(if (< (car date-L) (car date-M)) 
		(cons (car L) (merge-split-lists (cdr L)M))
		(cons (car M) (merge-split-lists L (cdr M))))))))

  
(define (document-renderer report-obj)
  ;; These are some helper functions for looking up option values.
  (define (get-op section name)
    (gnc:lookup-option (gnc:report-options report-obj) section name))

  (define (op-value section name)
    (gnc:option-value (get-op section name)))


  (let ((accounts-positive (op-value "Konten" "Konten positiv"))
        (accounts-negative (op-value "Konten" "Konten negativ"))
        (document (gnc:make-html-document)))
    (gnc:html-document-set-title! document (_ "Dreikontenmodell"))

      (gnc:html-document-add-object! document
	(if (and(null? accounts-positive) (null? accounts-negative))
	  (gnc:make-html-text
	    (gnc:html-markup-p (_ "You have selected no accounts.")))
	  (output-splits 
	    (merge-split-lists
	      (merge-account-splits accounts-positive)
	      (merge-account-splits accounts-negative))
	    accounts-positive
	    accounts-negative)))

      document))


;; ------------------------------------------------------------------
;; Define the actual report
;; ------------------------------------------------------------------

(gnc:define-report
 'version 1
 'name (N_ "Dreikontenmodell")
 'report-guid "042ee17bacd94d9eb8e442f535f0a905"
 'menu-tip (N_ "Unstable. Used for Testing.")
 'menu-path (list gnc:menuname-utility)
 'options-generator options-generator
 'renderer document-renderer)


 ;; Alte Test-Beispiele:
 
      ;; here's a bullet list of accounts.  We can mark up the
      ;; account name with an <a></a> anchor with a special HREF to
      ;; open a Gnucash register when the link is clicked.  What you
      ;; need to do is pass the HREF "gnc-register:account=My
      ;; Account Name" to html-markup-anchor.  The account name
      ;; passed must be the "full" account name that you get from
      ;; gnc-account-get-full-name.  You should build this url using
      ;; (gnc-build-url ...)
      ;;
      ;; html-markup-anchor takes the link to jump to as its first
      ;; arg and then puts the remaining args in the body of the
      ;; link).
      ;;
      ;; html-markup-ul makes a "<ul>" unnumbered list, and takes as
      ;; its one argument a list of items to put in <li> blocks.
;;       (if (not (null? accounts))
;;           (gnc:html-document-add-object!
;;            document
;;            (gnc:make-html-text
;;             (gnc:html-markup-ul
;;              (map
;;               (lambda (acct)
;;                 (gnc:html-markup-anchor
;;                  (gnc-build-url URL-TYPE-REGISTER
;;                                      (string-append "account="
;;                                                     (gnc-account-get-full-name
;;                                                      acct))
;;                                      "")
;;                  (xaccAccountGetName acct)))
;;               accounts))))
;;           (gnc:html-document-add-object!
;;            document
;;            (gnc:make-html-text
;;             (gnc:html-markup-p (_ "You have selected no accounts.")))))

;;       (gnc:html-document-add-object! document
;;         (gnc:make-html-text
;;           (if (not (null? accounts))
;;             (gnc:html-markup-ul
;;               (map
;;                 (lambda (acct)
;;                   (xaccAccountGetName acct))
;;                 accounts))
;;             (gnc:html-markup-p (_ "You have selected no accounts.")))))


;; Nur das erste Konto anzeigen:
;;       (gnc:html-document-add-object! document
;;         (gnc:make-html-text
;;           (if (not (null? accounts))
;;             (xaccAccountGetName (car accounts))
;;             (gnc:html-markup-p (_ "You have selected no accounts.")))))
            

;; Umsätze des ersten Kontos anzeigen:
;;       (gnc:html-document-add-object! document
;;         (gnc:make-html-text
;;           (if (not (null? accounts))
;; 	    (let ((splits (xaccAccountGetSplitList (car accounts))))
;; 	      (gnc:html-markup-ul
;; 		(map
;; 		  (lambda (split)
;; 		    (xaccSplitGetAmount split)
;; 		  )
;; 		  splits
;; 		)
;; 	      )
;; 	    )
;;             (gnc:html-markup-p (_ "You have selected no accounts.")))))


;; Transaktionsbeschreibungen des ersten Kontos:
;;       (gnc:html-document-add-object! document
;;         (gnc:make-html-text
;;           (if (not (null? accounts))
;; 	    (let ((splits (xaccAccountGetSplitList (car accounts))))
;; 	      (gnc:html-markup-ul
;; 		(map
;; 		  (lambda (split)
;; 		    (xaccTransGetDescription (xaccSplitGetParent split))
;; 		  )
;; 		  splits
;; 		)
;; 	      )
;; 	    )
;;             (gnc:html-markup-p (_ "You have selected no accounts.")))))


;; Kontostand nach der jeweiligen Transaktion:
;;       (gnc:html-document-add-object! document
;;         (gnc:make-html-text
;;           (if (not (null? accounts))
;; 	    (let ((splits (xaccAccountGetSplitList (car accounts))))
;; 	      (gnc:html-markup-ul
;; 		(map
;; 		  (lambda (split)
;; 		    (xaccTransGetAccountBalance (xaccSplitGetParent split) (car accounts))
;; 		  )
;; 		  splits
;; 		)
;; 	      )
;; 	    )
;;             (gnc:html-markup-p (_ "You have selected no accounts.")))))



;; Transaktionsdaten des ersten Kontos:
;;       (gnc:html-document-add-object! document
;;         (gnc:make-html-text
;;           (if (not (null? accounts))
;; 	    (let ((splits (xaccAccountGetSplitList (car accounts))))
;; 	      (gnc:html-markup-ul
;; 		(map
;; 		  (lambda (split)
;; 		    (gnc-print-date
;; 		      (gnc-transaction-get-date-posted (xaccSplitGetParent split))))
;; 		  splits)))
;;             (gnc:html-markup-p (_ "You have selected no accounts.")))))

;;       (gnc:html-document-add-object! document
;;         (gnc:make-html-text
;;           (if (not (null? accounts))
;; 	    (let ((splits (xaccAccountGetSplitList (car accounts))))
;; 	      (gnc:html-markup-ul
;; 		(map
;; 		  (lambda (split)
;; 		    (let ((transaction (xaccSplitGetParent split)))
;; 		      (gnc-print-date
;; 			(gnc-transaction-get-date-posted transaction))))
;; 		  splits)))
;;             (gnc:html-markup-p (_ "You have selected no accounts.")))))
