;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; transaction_budget-jps.scm : display running budget balance for envelope accounting 
;; Based heavily on transaction.scm.
;;
;; This report exists to aide in envelope accounting. The standard
;; practice has been to create scheduled transactions that add
;; the budget amount to the expense accounts. However this doesn't
;; let me leverage the other budget reports. So I created this
;; report that is essentially an account register for the
;; expense account with the budget values added in like the
;; scheduled transactions would, except the expense account
;; is still accurate for use with the budget reports.
;;
;; TODO:
;;  - Why might I need to have the for-each over extra currencies in add-subtotal-row
;;  - How to properly disable options when another isn't selected - bug https://bugzilla.gnome.org/show_bug.cgi?id=647805. The bug is fixed in 2.16.18.
;;    Ubuntu 17.10 will have 2.16.17.
;;    - Disable "Notes" when "Memo" isn't selected
;;    - Disable "Use Full Account Name" when "Account Name" isn't selected
;;    - Disable "Use Full Other Account Name" when "Other Account Name" isn't selected
;;    - Disable optname-currency when optname-common-currency isn't seleted
;;
;; Original report by Jon Schewe <jpschewe@mtu.net>
;;
;; This program is free software; you can redistribute it and/or    
;; modify it under the terms of the GNU General Public License as   
;; published by the Free Software Foundation; either version 2 of   
;; the License, or (at your option) any later version.              
;;                                                                  
;; This program is distributed in the hope that it will be useful,  
;; but WITHOUT ANY WARRANTY; without even the implied warranty of   
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    
;; GNU General Public License for more details.                     
;;                                                                  
;; You should have received a copy of the GNU General Public License
;; along with this program; if not, contact:
;;
;; Free Software Foundation           Voice:  +1-617-542-5942
;; 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
;; Boston, MA  02110-1301,  USA       gnu@gnu.org
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define-module (gnucash report standard-reports transaction-budget-jps))

(use-modules (gnucash main)) ;; FIXME: delete after we finish modularizing. - left over from copy of transaction.scm
(use-modules (srfi srfi-1))
(use-modules (gnucash gnc-module))
(use-modules (gnucash gettext))

(use-modules (gnucash printf))

(gnc:module-load "gnucash/report/report-system" 0)

(define-macro (addto! alist element)
  `(set! ,alist (cons ,element ,alist)))

;; Define the strings here to avoid typos and make changes easier.

(define reportname (N_ "Transaction Budget Report"))
(define optname-void-transactions (N_ "Void Transactions"))
(define optname-include-subaccounts (N_ "Include Sub-accounts"))
(define optname-show-splits (N_ "Show splits"))
(define optname-table-export (N_ "Table for Exporting"))
(define optname-common-currency (N_ "Common Currency"))
(define optname-currency (N_ "Report's currency"))
(define optname-budget (N_ "Budget"))
(define def:grand-total-style "grand-total")
(define def:normal-row-style "normal-row")
(define def:alternate-row-style "alternate-row")
(define def:primary-subtotal-style "primary-subheading")



(define (add-subheading-row data table width subheading-style)
  (let ((heading-cell (gnc:make-html-table-cell data)))
    (gnc:html-table-cell-set-colspan! heading-cell width)
    (gnc:html-table-append-row/markup!
     table
     subheading-style
     (list heading-cell))))

(define (add-budget-row table width style date
			budget-value	    ; gnc-monetary
			budget-period-total ; collector
			budget-total	    ; collector
			)
  (gnc:html-table-append-row/markup! table style
				     (list
				      (gnc:make-html-table-cell/markup "date-cell" (gnc-print-date date))
				      (gnc:make-html-table-cell/size/markup 1 (- width 4) "text-cell" "Budget allocation")
				      ;; value
				      (gnc:make-html-table-cell/markup "number-cell" budget-value)
				      (gnc:make-html-table-cell/markup "number-cell" (car (budget-period-total 'format gnc:make-gnc-monetary #f)))
				      (gnc:make-html-table-cell/markup "number-cell" (car (budget-total 'format gnc:make-gnc-monetary #f)))
				      )
				     ))

;; display an account name depending on the options the user has set
(define (account-namestring account show-account-code show-account-name show-account-full-name)
  ;;# on multi-line splits we can get an empty ('()) account
  (if (null? account)
        (_ "Split Transaction")
        (string-append 
           ;; display account code?
           (if show-account-code
                 (string-append (xaccAccountGetCode account) " ")
                 "")
           ;; display account name?
           (if show-account-name
                 ;; display full account name?
                 (if show-account-full-name
                      (gnc-account-get-full-name account)
                      (xaccAccountGetName account))
                 ""))))

;; render an account subheading - column-vector determines what is displayed
(define (render-account-subheading
         account table width subheading-style column-vector)
    (add-subheading-row (gnc:make-html-text
                         (gnc:html-markup-anchor
                           (gnc:account-anchor-text account)
                           (account-namestring account
                                               (used-account-code      column-vector)
                                               #t
                                               (used-account-full-name column-vector))))
                        table width subheading-style))


(define (add-grand-total-row table width subtotal-string subtotal-collector 
			     subtotal-style export?)
  (let ((currency-totals (subtotal-collector
                          'format gnc:make-gnc-monetary #f))
        (blanks (gnc:make-html-table-cell/size 1 (- width 2) #f))
	)
    (gnc:html-table-append-row/markup!
     table
     subtotal-style 
     (if export?
	 (append! (cons (gnc:make-html-table-cell/markup "total-label-cell" subtotal-string)
			(gnc:html-make-empty-cells (- width 2)))
		  (list (gnc:make-html-table-cell/markup 
			 "total-number-cell"
			 (car currency-totals))
			))
	 (list (gnc:make-html-table-cell/size/markup 1 (- width 1) "total-label-cell"
						     subtotal-string)
	       (gnc:make-html-table-cell/markup 
		"total-number-cell"
		(car currency-totals))
	       )))
    
    (for-each (lambda (currency)
    		(gnc:debug "Adding other currencies? " currency)
                (gnc:html-table-append-row/markup! 
                 table
                 subtotal-style
                 (append!
                  (if export?
		      (gnc:html-make-empty-cells (- width 1))
		      (list blanks))
		  (list (gnc:make-html-table-cell/markup
			 "total-number-cell" currency)))))
              (cdr currency-totals))
    ))

(define (add-subtotal-row table width subtotal-string budget-period-total budget-total
                          subtotal-style export?)
  (let ((currency-period-totals (budget-period-total
				 'format gnc:make-gnc-monetary #f))
	(currency-budget-totals (budget-total
				 'format gnc:make-gnc-monetary #f))
        ;;(blanks (gnc:make-html-table-cell/size 1 (- width 2) #f))
	)
    (gnc:html-table-append-row/markup!
     table
     subtotal-style 
     (if export?
	 (append! (cons (gnc:make-html-table-cell/markup "total-label-cell" subtotal-string)
			(gnc:html-make-empty-cells (- width 3)))
		  (list (gnc:make-html-table-cell/markup 
			 "total-number-cell"
			 (car currency-period-totals))
			(gnc:make-html-table-cell/markup 
			 "total-number-cell"
			 (car currency-budget-totals))
			))
	 (list (gnc:make-html-table-cell/size/markup 1 (- width 2) "total-label-cell"
						     subtotal-string)
	       (gnc:make-html-table-cell/markup 
		"total-number-cell"
		(car currency-period-totals))
	       (gnc:make-html-table-cell/markup 
		"total-number-cell"
		(car currency-budget-totals))
	       )))
    
    ;;TODO not sure why this would be needed
    ;;(for-each (lambda (currency)
    ;;		(gnc:debug "Adding other currencies? " currency)
    ;;            (gnc:html-table-append-row/markup! 
    ;;             table
    ;;             subtotal-style
    ;;             (append!
    ;;              (if export?
    ;;               (gnc:html-make-empty-cells (- width 1))
    ;;               (list blanks))
    ;;                     (list (gnc:make-html-table-cell/markup
    ;;                            "total-number-cell" currency)))))
    ;;          (cdr currency-totals))
    ))

(define (total-string str) (string-append (_ "Total For ") str))

(define (render-grand-total
         table width total-collector export?)
  (add-grand-total-row table width
                    (_ "Grand Total")
                    total-collector def:grand-total-style export?))

(define account-types-to-reverse-assoc-list
  (list (cons 'none '())
        (cons 'income-expense
              (list ACCT-TYPE-INCOME ACCT-TYPE-EXPENSE))
        (cons 'credit-accounts
              (list ACCT-TYPE-LIABILITY ACCT-TYPE-PAYABLE ACCT-TYPE-EQUITY
                    ACCT-TYPE-CREDIT ACCT-TYPE-INCOME))))

(define (used-date columns-used)
  (vector-ref columns-used 0))
(define (used-reconciled-date columns-used)
  (vector-ref columns-used 1))
(define (used-num columns-used)
  (vector-ref columns-used 2))
(define (used-description columns-used)
  (vector-ref columns-used 3))
(define (used-account-name columns-used)
  (vector-ref columns-used 4))
(define (used-other-account-name columns-used)
  (vector-ref columns-used 5))	
(define (used-shares columns-used)
  (vector-ref columns-used 6))	
(define (used-price columns-used)
  (vector-ref columns-used 7))	
(define (used-amount-single columns-used)
  (vector-ref columns-used 8))	
(define (used-amount-double-positive columns-used)
  (vector-ref columns-used 9))	
(define (used-amount-double-negative columns-used)
  (vector-ref columns-used 10))	
(define (used-period-balance columns-used)
  (vector-ref columns-used 11))
(define (used-account-full-name columns-used)
  (vector-ref columns-used 12))
(define (used-memo columns-used)
  (vector-ref columns-used 13))
(define (used-account-code columns-used)
  (vector-ref columns-used 14))
(define (used-other-account-code columns-used)
  (vector-ref columns-used 15))
(define (used-other-account-full-name columns-used)
  (vector-ref columns-used 16))
(define (used-notes columns-used)
  (vector-ref columns-used 17))
(define (used-budget-balance columns-used)
  (vector-ref columns-used 18))

;; needs to match the number of possible columns defined
(define columns-used-size 19)

(define (num-columns-required columns-used)  
  (do ((i 0 (+ i 1)) 
       (col-req 0 col-req)) 
      ((>= i columns-used-size) col-req)
    ; If column toggle is true, increase column count. But attention:
    ; some toggles only change the meaning of another toggle. Don't count these modifier toggles
    (if (and (not (= i 12)) ; Skip Account Full Name toggle - modifies Account Name column
             (not (= i 16)) ; Skip Other Account Full Name toggle - modifies Other Account Name column
             (not (= i 17)) ; Skip Note toggle - modifies Memo column
             (vector-ref columns-used i))
      (set! col-req (+ col-req 1)))
    ; Account Code and Account Name share one column so if both were ticked the
    ; the check above would have set up one column too much. The check below
    ; will compensate these again.
    (if (or (and (= i 14) (used-account-code columns-used) (used-account-name columns-used)) ; Account Code and Name
            (and (= i 15) (used-other-account-code columns-used) (used-other-account-name columns-used))) ; Other Account Code and Name
      (set! col-req (- col-req 1)))))

(define (build-column-used options)   
  (define (opt-val section name)
    (gnc:option-value 
     (gnc:lookup-option options section name)))
  (let ((column-list (make-vector columns-used-size #f)))
    (if (opt-val (N_ "Display") (N_ "Date"))
        (vector-set! column-list 0 #t))
    (if (opt-val (N_ "Display") (N_ "Reconciled Date"))
        (vector-set! column-list 1 #t))
    (if (if (gnc:lookup-option options (N_ "Display") (N_ "Num"))
            (opt-val (N_ "Display") (N_ "Num"))
            (opt-val (N_ "Display") (N_ "Num/Action")))
        (vector-set! column-list 2 #t))
    (if (opt-val (N_ "Display") (N_ "Description"))
        (vector-set! column-list 3 #t))
    (if (opt-val (N_ "Display") (N_ "Account Name"))
        (vector-set! column-list 4 #t))
    (if (opt-val (N_ "Display") (N_ "Other Account Name"))
        (vector-set! column-list 5 #t))
    (if (opt-val (N_ "Display") (N_ "Shares"))
        (vector-set! column-list 6 #t))
    (if (opt-val (N_ "Display") (N_ "Price"))
        (vector-set! column-list 7 #t))
    (let ((amount-setting (opt-val (N_ "Display") (N_ "Amount"))))
      (if (eq? amount-setting 'single)
          (vector-set! column-list 8 #t))
      (if (eq? amount-setting 'double)
          (begin (vector-set! column-list 9 #t)
                 (vector-set! column-list 10 #t))))
    (vector-set! column-list 11 #t)
    (if (opt-val (N_ "Display")  (N_ "Use Full Account Name"))
        (vector-set! column-list 12 #t))
    (if (opt-val (N_ "Display") (N_ "Memo"))
        (vector-set! column-list 13 #t))
    (if (opt-val (N_ "Display") (N_ "Account Code"))
        (vector-set! column-list 14 #t))
    (if (opt-val (N_ "Display") (N_ "Other Account Code"))
        (vector-set! column-list 15 #t))
    (if (opt-val (N_ "Display") (N_ "Use Full Other Account Name"))
        (vector-set! column-list 16 #t))
    (if (opt-val (N_ "Display") (N_ "Notes"))
        (vector-set! column-list 17 #t))
    (vector-set! column-list 18 #t) ; always display the overall balance
    column-list))

(define (make-heading-list column-vector options)
  (let ((heading-list '()))
    (if (used-date column-vector)
        (addto! heading-list (_ "Date")))
    (if (used-reconciled-date column-vector)
        (addto! heading-list (_ "Reconciled Date")))
    (if (used-num column-vector)
        (addto! heading-list (if (and (qof-book-use-split-action-for-num-field
                                                        (gnc-get-current-book))
                                      (if (gnc:lookup-option options
                                                    gnc:pagename-display
                                                    (N_ "Trans Number"))
                                          (gnc:option-value 
                                            (gnc:lookup-option options
                                                    gnc:pagename-display
                                                    (N_ "Trans Number")))
                                          #f))
                                 (_ "Num/T-Num")
                                 (_ "Num"))))
    (if (used-description column-vector)
        (addto! heading-list (_ "Description")))
    (if (used-memo column-vector)
        (if (used-notes column-vector)
            (addto! heading-list (string-append (_ "Memo") "/" (_ "Notes")))
            (addto! heading-list (_ "Memo"))))
    (if (or (used-account-name column-vector) (used-account-code column-vector))
        (addto! heading-list (_ "Account")))
    (if (or (used-other-account-name column-vector) (used-other-account-code column-vector))
        (addto! heading-list (_ "Transfer from/to")))
    (if (used-shares column-vector)
        (addto! heading-list (_ "Shares")))
    (if (used-price column-vector)
        (addto! heading-list (_ "Price")))
    (if (used-amount-single column-vector)
        (addto! heading-list (_ "Amount")))
    (if (used-amount-double-positive column-vector)
        (addto! heading-list (_ "Debit")))
    (if (used-amount-double-negative column-vector)
        (addto! heading-list (_ "Credit")))
    (if (used-period-balance column-vector)
        (addto! heading-list (_ "Period Balance")))
    (if (used-budget-balance column-vector)
        (addto! heading-list (_ "Balance")))
    (reverse heading-list)))

(define (should-include-row display-begin-date display-end-date row-date)
  (and (gnc:timepair-le display-begin-date row-date)
       (gnc:timepair-le row-date display-end-date)))

(define (handle-end-of-budget-period table
				     account
				     account-types-to-reverse
				     budget period
				     prev-period-end
				     current-period-end
				     budget-period-total budget-total
				     width export? show-splits
				     display-begin-date display-end-date)

  (let* ((account-type (xaccAccountGetType account))
	 (account-commodity (xaccAccountGetCommodity account))
	 (currency (if (not (null? account))
                       (xaccAccountGetCommodity account)
                       (gnc-default-currency))))

    ;; add subtotal row
    (add-subtotal-row table width
		      (string-append (_ "Total for period ending ")
				     (gnc-print-date prev-period-end))
		      budget-period-total budget-total def:primary-subtotal-style export?)

    ;;(gnc:debug "resetting budget-period-total")
    (budget-period-total 'reset #f #f)

    ;; add budget value to budget-period-total and budget-total
    ;; need to use negative value 
    (let*
	((raw-budget-period-value (gnc-budget-get-account-period-value budget account period))
	 (budget-period-value (if (member account-type account-types-to-reverse) 
				  (gnc-numeric-neg raw-budget-period-value)
				  raw-budget-period-value)))
      (budget-period-total 'add account-commodity budget-period-value)
      (budget-total 'add account-commodity budget-period-value)

      ;; this adds the heading <month> <date> above a group of transactions
      ;;(add-subheading-row
      ;; (gnc:date-get-month-year-string
      ;;  (gnc:timepair->date (gnc-budget-get-period-start-date budget period)))
      ;; table width def:primary-subtotal-style)

      ;; display the budget amount added
      (if (and show-splits (should-include-row display-begin-date display-end-date (gnc-budget-get-period-start-date budget period)))
	  (add-budget-row table width def:primary-subtotal-style
			  (gnc-budget-get-period-start-date budget period)
			  (gnc:make-gnc-monetary currency budget-period-value)
			  budget-period-total
			  budget-total))
			       
      ;;(add-subheading-row (string-append (gnc-print-date (gnc-budget-get-period-start-date budget period))
      ;;					  " - "
      ;;					  (gnc-numeric-to-string budget-period-value))
      ;;			   table width def:primary-subtotal-style)
	    
      )
  
    ))

(define (possibly-add-split-row table account split
				budget period
				budget-period-total budget-total
				column-vector options
				row-style account-types-to-reverse transaction-row?
				width used-columns export?
				display-begin-date display-end-date)

  (define (opt-val section name)
    (gnc:option-value 
     (gnc:lookup-option options section name)))

  (let* ((row-contents '())
         (parent (xaccSplitGetParent split))
         (account-type (xaccAccountGetType account))
         (currency (if (not (null? account))
                       (xaccAccountGetCommodity account)
                       (gnc-default-currency)))
	 (report-currency (if (opt-val gnc:pagename-general optname-common-currency)
			      (opt-val gnc:pagename-general optname-currency)
			      currency))
         (damount (if (gnc:split-voided? split)
		      (xaccSplitVoidFormerAmount split)
		      (xaccSplitGetAmount split)))
	 (trans-date (gnc-transaction-get-date-posted parent))
	 (split-value (gnc:exchange-by-pricedb-nearest
		       (gnc:make-gnc-monetary 
			currency
			(if (member account-type account-types-to-reverse)
			    damount
			    (gnc-numeric-neg damount)))
		       report-currency
		       ;; Use midday as the transaction time so it matches a price
		       ;; on the same day.  Otherwise it uses midnight which will
		       ;; likely match a price on the previous day
		       (timespecCanonicalDayTime trans-date)))
	 (current-period-end (gnc-budget-get-period-end-date budget period))
	 (show-splits (opt-val gnc:pagename-display optname-show-splits)))

    ;; increment the counters, possibly outputting a heading and resetting the period total
    (while (not (gnc:timepair-le trans-date current-period-end))
	   (begin
	     (set! period (+ period 1))
	     (set! current-period-end (gnc-budget-get-period-end-date budget period))
	     (let ((prev-period-end (gnc-budget-get-period-end-date budget (- period 1))))
	       (handle-end-of-budget-period
		table account account-types-to-reverse
		budget period
		prev-period-end
		current-period-end
		budget-period-total budget-total
		width export? show-splits
		display-begin-date display-end-date)
	       )))


    ;; subtract split value from the budget collectors
    (budget-period-total 'add
			 (gnc:gnc-monetary-commodity split-value)
			 (gnc:gnc-monetary-amount split-value))
    (budget-total 'add
		  (gnc:gnc-monetary-commodity split-value)
		  (gnc:gnc-monetary-amount split-value))

    (if (used-date column-vector)
        (addto! row-contents
                (if transaction-row?
                    (gnc:make-html-table-cell/markup "date-cell"
						     (gnc-print-date trans-date))
                    " ")))
    (if (used-reconciled-date column-vector)
        (addto! row-contents
                (gnc:make-html-table-cell/markup "date-cell"
						 (let ((date (gnc-split-get-date-reconciled split)))
						   (if (equal? date (cons 0 0))
						       " "
						       (gnc-print-date date))))))
    (if (used-num column-vector)
        (addto! row-contents
                (if transaction-row?
                    (if (qof-book-use-split-action-for-num-field
			 (gnc-get-current-book))
                        (let* ((num (gnc-get-num-action parent split))
                               (t-num (if (if (gnc:lookup-option options
								 gnc:pagename-display
								 (N_ "Trans Number"))
                                              (opt-val gnc:pagename-display
						       (N_ "Trans Number"))
                                              #f)
                                          (gnc-get-num-action parent #f)
                                          ""))
                               (num-string (if (equal? t-num "")
                                               num
                                               (string-append num "/" t-num))))
			  (gnc:make-html-table-cell/markup "text-cell"
							   num-string))
                        (gnc:make-html-table-cell/markup "text-cell"
							 (gnc-get-num-action parent split)))
                    " ")))

    (if (used-description column-vector)
        (addto! row-contents
                (if transaction-row?
                    (gnc:make-html-table-cell/markup "text-cell"
						     (xaccTransGetDescription parent))
                    " ")))
    
    (if (used-memo column-vector)
        (let ((memo (xaccSplitGetMemo split)))
          (if (and (equal? memo "") (used-notes column-vector))
              (addto! row-contents (xaccTransGetNotes parent))
              (addto! row-contents memo))))
    
    (if (or (used-account-name column-vector) (used-account-code column-vector))
	(addto! row-contents (account-namestring (xaccSplitGetAccount split) ; account may be the parent
						 (used-account-code      column-vector)
						 (used-account-name      column-vector)
						 (used-account-full-name column-vector))))
    
    (if (or (used-other-account-name column-vector) (used-other-account-code column-vector))
	(addto! row-contents (account-namestring (xaccSplitGetAccount
						  (xaccSplitGetOtherSplit split))
						 (used-other-account-code      column-vector)
						 (used-other-account-name      column-vector)
						 (used-other-account-full-name column-vector))))
    
    (if (used-shares column-vector)
        (addto! row-contents (xaccSplitGetAmount split)))
    (if (used-price column-vector)
        (addto! 
         row-contents 
         (gnc:make-gnc-monetary (xaccTransGetCurrency parent)
                                (xaccSplitGetSharePrice split))))
    (if (used-amount-single column-vector)
        (addto! row-contents
                (gnc:make-html-table-cell/markup "number-cell"
                                                 (gnc:html-transaction-anchor parent split-value))))
    (if (used-amount-double-positive column-vector)
        (if (gnc-numeric-positive-p (gnc:gnc-monetary-amount split-value))
            (addto! row-contents
                    (gnc:make-html-table-cell/markup "number-cell"
                                                     (gnc:html-transaction-anchor parent split-value)))
            (addto! row-contents " ")))
    (if (used-amount-double-negative column-vector)
        (if (gnc-numeric-negative-p (gnc:gnc-monetary-amount split-value))
            (addto! row-contents
                    (gnc:make-html-table-cell/markup
                     "number-cell" (gnc:html-transaction-anchor parent (gnc:monetary-neg split-value))))
            (addto! row-contents " ")))
    (if (used-period-balance column-vector)
	(begin
	  (addto! row-contents
		  (gnc:make-html-table-cell/markup
		   "number-cell"
		   (budget-period-total 'getmonetary currency #f)
		   ))))

    (if (used-budget-balance column-vector)
	(begin
	  (addto! row-contents
		  (gnc:make-html-table-cell/markup
		   "number-cell"
		   (budget-total 'getmonetary currency #f)
		   ))))

    (if (and show-splits (should-include-row display-begin-date display-end-date trans-date))
	(gnc:html-table-append-row/markup! table row-style
					   (reverse row-contents)))

    period
    ))


(define (trep-options-generator)
  (define gnc:*transaction-report-options* (gnc:new-options))
  (define (gnc:register-trep-option new-option)
    (gnc:register-option gnc:*transaction-report-options* new-option))
  
  ;; General options
  
  (gnc:options-add-date-interval!
   gnc:*transaction-report-options*
   gnc:pagename-general (N_ "Start Date") (N_ "End Date") "a")
  

  ;; Option to select Budget
  (gnc:register-trep-option (gnc:make-budget-option
			     gnc:pagename-general optname-budget
			     "a" (N_ "Budget to use.")))
  
  (gnc:register-trep-option
   (gnc:make-complex-boolean-option
    gnc:pagename-general optname-common-currency
    "e" (N_ "Convert all transactions into a common currency.") #f
    #f
    (lambda (x) (gnc-option-db-set-option-selectable-by-name
		 gnc:*transaction-report-options*
		 gnc:pagename-general
		 optname-currency
		 x))
    ))

  (gnc:options-add-currency!
   gnc:*transaction-report-options* gnc:pagename-general optname-currency "f")

  (gnc:register-trep-option
   (gnc:make-simple-boolean-option
    gnc:pagename-general optname-table-export
    "g" (N_ "Formats the table suitable for cut & paste exporting with extra cells.") #f))  
  
  ;; Accounts options
  
  ;; account to do report on
  (gnc:register-trep-option
   (gnc:make-account-list-option
    gnc:pagename-accounts (N_ "Account")
    "a" (N_ "Report on this account.")
    ;; select, by default, no accounts! Selecting all accounts will
    ;; always imply an insanely long waiting time upon opening, and it
    ;; is almost never useful. So we instead display the normal error
    ;; message saying "Click here", and the user knows how to
    ;; continue.
    (lambda ()
      '())
    #f #f))

  (gnc:register-trep-option
   (gnc:make-simple-boolean-option
    gnc:pagename-accounts optname-include-subaccounts
    "b" (N_ "Include all descendant accounts in the report. Uses the budget from the selected account.") #f))  
  
  ;;

  (gnc:register-trep-option
   (gnc:make-multichoice-option
    gnc:pagename-accounts optname-void-transactions
    "d" (N_ "How to handle void transactions.")
    'non-void-only
    (list (vector
	   'non-void-only
	   (N_ "Non-void only")
	   (N_ "Show only non-voided transactions."))
	  (vector
	   'void-only
	   (N_ "Void only")
	   (N_ "Show only voided transactions."))
	  (vector 
	   'both
	   (N_ "Both")
	   (N_ "Show both (and include void transactions in totals).")))))

  ;; Display options
  
  (for-each
   (lambda (l)
     (gnc:register-trep-option
      (gnc:make-simple-boolean-option
       gnc:pagename-display (car l) (cadr l) (caddr l) (cadddr l))))
   ;; One list per option here with: option-name, sort-tag,
   ;; help-string, default-value
   (list
    (list (N_ "Date")                         "a"  (N_ "Display the date?") #t)
    (list (N_ "Reconciled Date")              "a2" (N_ "Display the reconciled date?") #f)
    (if (qof-book-use-split-action-for-num-field (gnc-get-current-book))
        (list (N_ "Num/Action")               "b"  (N_ "Display the check number?") #t)
        (list (N_ "Num")                      "b"  (N_ "Display the check number?") #t))
    (list (N_ "Description")                  "c"  (N_ "Display the description?") #t)
    (list (N_ "Account Name")                 "e"  (N_ "Display the account name?") #f)
    (list (N_ "Use Full Account Name")        "f"  (N_ "Display the full account name?") #t)
    (list (N_ "Account Code")                 "g"  (N_ "Display the account code?") #f)
    (list (N_ "Other Account Name")           "h"  (N_ "Display the other account name?\
 (if this is a split transaction, this parameter is guessed).") #f)
    (list (N_ "Use Full Other Account Name")  "i"  (N_ "Display the full account name?") #t)
    (list (N_ "Other Account Code")           "j"  (N_ "Display the other account code?") #f)
    (list (N_ "Shares")                       "k"  (N_ "Display the number of shares?") #f)
    (list (N_ "Price")                        "l"  (N_ "Display the shares price?") #f)
    (list optname-show-splits                 "m"  (N_ "Show all splits, if off only the totals are shown.") #t)
    ;; note the "Amount" multichoice option in between here
    (list (N_ "Totals")                       "o"  (N_ "Display the totals?") #t)))

  (if (qof-book-use-split-action-for-num-field (gnc-get-current-book))
      (gnc:register-trep-option
       (gnc:make-simple-boolean-option
        gnc:pagename-display (N_ "Trans Number")
	"b2" (N_ "Display the trans number?") #f)))

  (gnc:register-trep-option
   (gnc:make-simple-boolean-option
    gnc:pagename-display (N_ "Notes") "d2" (N_ "Display the notes if the memo is unavailable?") #t))
  
  ;; Add an option to display the memo, and disable the notes option
  ;; when memos are not included.
  (gnc:register-trep-option
   (gnc:make-complex-boolean-option
    gnc:pagename-display (N_ "Memo")
    "d"  (N_ "Display the memo?") #t
    #f
    (lambda (x) (gnc-option-db-set-option-selectable-by-name
		 gnc:*transaction-report-options*
		 gnc:pagename-display
		 (N_ "Notes")
		 x))))

  (gnc:register-trep-option
   (gnc:make-multichoice-option
    gnc:pagename-display (N_ "Amount")
    "m" (N_ "Display the amount?")  
    'single
    (list
     (vector 'none (N_ "None") (N_ "No amount display."))
     (vector 'single (N_ "Single") (N_ "Single Column Display."))
     (vector 'double (N_ "Double") (N_ "Two Column Display.")))))
  
  (gnc:register-trep-option
   (gnc:make-multichoice-option
    gnc:pagename-display (N_ "Sign Reverses")
    "p" (N_ "Reverse amount display for certain account types.")
    'credit-accounts
    (list 
     (vector 'none (N_ "None") (N_ "Don't change any displayed amounts."))
     (vector 'income-expense (N_ "Income and Expense")
             (N_ "Reverse amount display for Income and Expense Accounts."))
     (vector 'credit-accounts (N_ "Credit Accounts")
             (N_ "Reverse amount display for Liability, Payable, Equity, \
Credit Card, and Income accounts.")))))


  (gnc:options-set-default-section gnc:*transaction-report-options*
                                   gnc:pagename-general)

  gnc:*transaction-report-options*)


(define (display-date-interval begin end)
  (let ((begin-string (gnc-print-date begin))
        (end-string (gnc-print-date end)))
    (sprintf #f (_ "From %s To %s") begin-string end-string)))

(define (get-primary-subtotal-style options)
  (let ((bgcolor (gnc:lookup-option options 
                                    (N_ "Colors")
                                    (N_ "Primary Subtotals/headings"))))
    (list 'attribute (list "bgcolor" (gnc:color-option->html bgcolor)))))


(define (get-grand-total-style options)
  (let ((bgcolor (gnc:lookup-option options
                                    (N_ "Colors")
                                    (N_ "Grand Total"))))
    (list 'attribute (list "bgcolor" (gnc:color-option->html bgcolor)))))

(define (get-odd-row-style options)
  (let ((bgcolor (gnc:lookup-option options
                                    (N_ "Colors")
                                    (N_ "Split Odd"))))
    (list 'attribute (list "bgcolor" (gnc:color-option->html bgcolor)))))

(define (get-even-row-style options)
  (let ((bgcolor (gnc:lookup-option options
                                    (N_ "Colors")
                                    (N_ "Split Even"))))
    (list 'attribute (list "bgcolor" (gnc:color-option->html bgcolor)))))


;; ;;;;;;;;;;;;;;;;;;;;
;; Here comes the big function that builds the whole table.
(define (make-split-table account
			  splits options
			  budget
			  num-budget-periods
			  display-begin-date
			  display-end-date)
  
 (let ((work-to-do (length splits))
       (work-done 0)
       (used-columns (build-column-used options)))
  (define (get-account-types-to-reverse options)
    (cdr (assq (gnc:option-value 
                (gnc:lookup-option options
                                   (N_ "Display")
                                   (N_ "Sign Reverses")))
               account-types-to-reverse-assoc-list)))
  

  (define (transaction-report-export-p options)
    (gnc:option-value
     (gnc:lookup-option options gnc:pagename-general
       optname-table-export)))

  (define (do-rows-with-subtotals account
				  splits 
                                  table
                                  used-columns
                                  width
                                  odd-row?
                                  export?
                                  account-types-to-reverse
				  budget
				  current-budget-period
				  num-budget-periods
                                  budget-period-total
                                  budget-total
				  display-begin-date
				  display-end-date)

    (gnc:report-percent-done (* 100 (/ work-done work-to-do)))
    (set! work-done (+ 1 work-done))
    (if (null? splits)
        (begin
          (gnc:html-table-append-row/markup!
           table
           def:grand-total-style
           (list
            (gnc:make-html-table-cell/size
             1 width (gnc:make-html-text (gnc:html-markup-hr)))))
	  (if (gnc:option-value (gnc:lookup-option options "Display" "Totals"))
	      (render-grand-total table width budget-total export?)))
	
        (let* ((current (car splits))
               (current-row-style (if odd-row? def:normal-row-style 
				      def:alternate-row-style))
               (rest (cdr splits))
               (next (if (null? rest) #f
                         (car rest)))
               (period (possibly-add-split-row 
			table
			account
			current
			budget
			current-budget-period
			budget-period-total
			budget-total
			used-columns
			options
			current-row-style
			account-types-to-reverse
			#t
			width
			used-columns
			export?
			display-begin-date
			display-end-date)))

	  ;; add budget values for the last periods that don't have any transactions
	  (if (null? rest)
	      (begin
		(set! period (+ period 1)) ; it's the next period that will matter
		(let*
		    ((current-period-end (gnc-budget-get-period-end-date budget period))		     
		     (show-splits (gnc:option-value (gnc:lookup-option options gnc:pagename-display optname-show-splits))))
		  (while (gnc:timepair-le current-period-end display-end-date)
			 (let ((prev-period-end (gnc-budget-get-period-end-date budget (- period 1))))
			   (handle-end-of-budget-period
			    table account account-types-to-reverse
			    budget period
			    prev-period-end
			    current-period-end
			    budget-period-total budget-total
			    width export? show-splits
			    display-begin-date display-end-date)
			   (set! period (+ period 1))
			   (set! current-period-end (gnc-budget-get-period-end-date budget period))
			   ))
		  )))
	  
          (do-rows-with-subtotals account
				  rest 
                                  table 
                                  used-columns
                                  width 
                                  (not odd-row?)
                                  export?
                                  account-types-to-reverse
				  budget
				  period
				  num-budget-periods
                                  budget-period-total
                                  budget-total
				  display-begin-date
				  display-end-date))))

  (let* ((table (gnc:make-html-table))
         (width (num-columns-required used-columns))
	 (export? (transaction-report-export-p options))
         (account-types-to-reverse
          (get-account-types-to-reverse options)))
    
    (gnc:html-table-set-col-headers!
     table
     (make-heading-list used-columns options))
    ;;     (gnc:warn "Splits:" splits)
    (if (not (null? splits))
	(let*
	    ((budget-period-total (gnc:make-commodity-collector))
	     (budget-total (gnc:make-commodity-collector))
	     (period 0)
	     (account-commodity (xaccAccountGetCommodity account))
	     (account-type (xaccAccountGetType account))
	     (raw-budget-period-value (gnc-budget-get-account-period-value budget account period))
	     (budget-period-value (if (member account-type account-types-to-reverse) 
					   (gnc-numeric-neg raw-budget-period-value)
					   raw-budget-period-value))
	     (currency (if (not (null? account))
                       (xaccAccountGetCommodity account)
                       (gnc-default-currency)))
	     (show-splits (gnc:option-value (gnc:lookup-option options gnc:pagename-display optname-show-splits))))

	  ;; put account name at the top, just below the headings
	  (render-account-subheading account table width def:primary-subtotal-style used-columns)

	  ;; initial value in the collectors
	  (budget-period-total 'add account-commodity budget-period-value)
	  (budget-total 'add account-commodity budget-period-value)

	  ;; initial budget amount to display
	  (if (and show-splits (should-include-row display-begin-date display-end-date (gnc-budget-get-period-start-date budget period)))
	      (add-budget-row table width def:primary-subtotal-style
			      (gnc-budget-get-period-start-date budget period)
			      (gnc:make-gnc-monetary currency budget-period-value)
			      budget-period-total
			      budget-total))
	  

	  ;; subheading for the first budget period
	  ;;(add-subheading-row
	  ;; (gnc:date-get-month-year-string
	  ;;  (gnc:timepair->date (gnc-budget-get-period-start-date budget period)))
	  ;; table width def:primary-subtotal-style)
	  
	  (do-rows-with-subtotals account splits table used-columns width
				  #t
				  export?
				  account-types-to-reverse
				  budget
				  period
				  num-budget-periods
				  budget-period-total
				  budget-total
				  display-begin-date
				  display-end-date)))
    
    table)))

;; ;;;;;;;;;;;;;;;;;;;;
;; Here comes the renderer function for this report.
(define (trep-renderer report-obj)

  (define options (gnc:report-options report-obj))

  (define (opt-val section name)
    (gnc:option-value
     (gnc:lookup-option options section name)))

  ;;(define (get-other-account-names account-list)
  ;;  ( map (lambda (acct)  (gnc-account-get-full-name acct)) account-list))


  (gnc:report-starting reportname)
  (let ((document (gnc:make-html-document))
	(selected_account_list (opt-val gnc:pagename-accounts "Account"))
        (display-begin-date (gnc:timepair-start-day-time
                    (gnc:date-option-absolute-time
                     (opt-val gnc:pagename-general "Start Date"))))
        (display-end-date (gnc:timepair-end-day-time
                  (gnc:date-option-absolute-time
                   (opt-val gnc:pagename-general "End Date"))))
	(report-title (opt-val 
                       gnc:pagename-general
                       gnc:optname-reportname))
	(void-status (opt-val gnc:pagename-accounts optname-void-transactions))
        (splits '())
        (query (qof-query-create-for-splits))
	(budget (opt-val gnc:pagename-general optname-budget)))

    ;;(gnc:warn "accts in trep-renderer:" selected_account_list)
    ;;(gnc:warn "Report Account names:" (get-other-account-names selected_account_list))

    (if (and budget (not (null? budget)))

	(if (not (or (null? selected_account_list) (and-map not selected_account_list)))
	    (let*
		((num-budget-periods (gnc-budget-get-num-periods budget))
		 (begindate (gnc-budget-get-period-start-date budget 0))
		 (enddate (gnc-budget-get-period-end-date budget (- num-budget-periods 1)))
		 (selected-account (car selected_account_list))
		 (accounts-to-query (if (opt-val gnc:pagename-accounts optname-include-subaccounts)
					(append selected_account_list (gnc-account-get-descendants selected-account))
					selected_account_list))
		 )
	      (qof-query-set-book query (gnc-get-current-book))
	      ;;(gnc:warn "query is:" query)
	      (xaccQueryAddAccountMatch query accounts-to-query QOF-GUID-MATCH-ANY QOF-QUERY-AND)
	      (xaccQueryAddDateMatchTS
	       query #t begindate #t enddate QOF-QUERY-AND)
	      (qof-query-set-sort-order query
					(list SPLIT-TRANS TRANS-DATE-POSTED)
					'()
					'())

	      (qof-query-set-sort-increasing query #t #t #t)

	      (case void-status
		((non-void-only) 
		 (gnc:query-set-match-non-voids-only! query (gnc-get-current-book)))
		((void-only)
		 (gnc:query-set-match-voids-only! query (gnc-get-current-book)))
		(else #f))

	      (set! splits (qof-query-run query))

	      ;;(gnc:warn "Splits in trep-renderer:" splits)

	      ;;(map (lambda (current)
	      ;;	     (gnc:debug "Current split: " current " date: " (gnc-print-date (gnc-transaction-get-date-posted (xaccSplitGetParent current))))
	      ;;	     )
	      ;; splits)


	      (if (not (null? splits))
		  (let ((table 
			 (make-split-table
			  selected-account
			  splits 
			  options
			  budget
			  num-budget-periods
			  display-begin-date
			  display-end-date
			  )))

		    (gnc:html-document-set-title! document
						  report-title)
		    (gnc:html-document-add-object! 
		     document
		     (gnc:make-html-text
		      (gnc:html-markup-h3 
		       (display-date-interval begindate enddate))))
		    (gnc:html-document-add-object!
		     document 
		     table)
		    (qof-query-destroy query))
		  ;; error condition: no splits found
		  (let ((p (gnc:make-html-text)))
		    (gnc:html-text-append! 
		     p 
		     (gnc:html-markup-h2 
		      (_ "No matching transactions found"))
		     (gnc:html-markup-p
		      (_ "No transactions were found that \
match the time interval and account selection specified \
in the Options panel.")))
		    (gnc:html-document-add-object! document p))))

	    ;; error condition: no accounts specified
	    
	    (gnc:html-document-add-object!
	     document 
	     (gnc:html-make-no-account-warning 
	      report-title (gnc:report-id report-obj))))
	;; error condition: no budget specified
	(gnc:html-document-add-object!
	 document (gnc:html-make-generic-budget-warning reportname)))

    (gnc:report-finished)
    document))

;; Define the report.
(gnc:define-report
 
 'version 1
 
 'name reportname
 'report-guid "3c821c2327594d4faaba07b3a1215669"
 
 'options-generator trep-options-generator
 
 'renderer trep-renderer)
