(* Dumping of account statements to HTML files. *)

open Account
open Unix

let fixme_rexp = Str.regexp "^.*FIXME.*$"
let next_checkpoint = ref (-1, -1, -1)
let txn_total = ref 0

let find_next_checkpoint which_day (year, month, day) =
  if which_day = -1 then (year, month, day)
  else
  begin
    if month = 12 then (year + 1, 1, which_day)
    else (year, month + 1, which_day)
  end

let date_on_or_after (y, m, d) (y', m', d') =
  y > y' || (y == y' && (m > m' || (m == m' && d >= d')))

(* Emit HTML for a single transaction. *)
let rec print_transaction channel acct context cur_total checkpoint_day txn =
  incr txn_total;
  let css_class =
    if Str.string_match fixme_rexp txn.description 0 then
      "fixme"
    else
      "entry"
  in
  let _ =
    if !next_checkpoint = (-1, -1, -1)
    then next_checkpoint :=
           find_next_checkpoint checkpoint_day (txn.year, txn.month, txn.day)
  in
  let real_name = Accountdbase.lookup_creditor txn.creditor context in
  let date_str = Printf.sprintf "%d-%02d-%02d" txn.year txn.month txn.day in
    if not (Sumofmoney.is_zero txn.amount) then
    begin
      let amount_str = Sumofmoney.to_string txn.amount in
        while (checkpoint_day != -1 &&
               date_on_or_after (txn.year, txn.month, txn.day) !next_checkpoint)
        do
          let (cp_year, cp_month, cp_day) = !next_checkpoint in
          let date_str =
            Printf.sprintf "%d-%02d-%02d" cp_year cp_month cp_day
          in
            output_string channel "<tr class=\"checkpoint\">";
            output_string channel "<td>";
            output_string channel date_str;
            output_string channel "</td>";
            output_string channel "<td>";
            output_string channel "Account balance at this point: ";
            output_string channel "</td><td>";
            output_string channel (Sumofmoney.to_string_default !cur_total);
            output_string channel "</td><td></td>";
            output_string channel "</tr>";
            next_checkpoint :=
              find_next_checkpoint checkpoint_day !next_checkpoint
        done;
        cur_total := Sumofmoney.add txn.amount !cur_total;
        output_string channel "<tr class=\"";
        output_string channel css_class;
        output_string channel "\">";
        output_string channel "<td>";
        output_string channel date_str;
        output_string channel "</td>";
        (if not (Account.is_virtual acct) then
         begin
           output_string channel "<td>";
           (if txn.creditor <> "phantom" then
             (output_string channel "<a href=\"bill-";
             output_string channel txn.creditor;
             output_string channel ".html\">"));
           output_string channel real_name;
           (if txn.creditor <> "phantom" then
             output_string channel "</a>");
           (if (not txn.automatically_added) && (not txn.do_not_symmetrise) then
              output_string channel "&nbsp;<img src=\"smiley.png\">");
           (if txn.automatically_added && txn.do_not_symmetrise then
              output_string channel "&nbsp;<img src=\"padlock.png\">");
           (if (not txn.automatically_added) && txn.do_not_symmetrise then
              output_string channel "&nbsp;<img src=\"exclamation.png\">");
           output_string channel "</td>"
        end);
        output_string channel "<td>";
        output_string channel amount_str;
        output_string channel "</td><td>";
        output_string channel txn.description;
        output_string channel "</tr>"
    end

(* Dump an HTML account statement to an output channel. *)
let dump acct =
  next_checkpoint := (-1, -1, -1);
  let short_name = Account.short_name acct in
  let tm = Unix.localtime (Unix.time ()) in
  let now = Printf.sprintf "%02d:%02d on %d-%02d-%02d"
            tm.tm_hour tm.tm_min (tm.tm_year + 1900) (tm.tm_mon+1) tm.tm_mday
  in
  try
    let page_name = "bill-" ^ short_name in
    let full_name = Account.full_name acct in
    let html_channel = Htmlout.create_page page_name full_name in
    let cur_total = ref Sumofmoney.zero_default in
    let checkpoint_day =
    begin try
      Account.lookup_integer_variable "checkpoint_before_day" acct
      with Not_found -> -1
    end
    in
    let context = "whilst generating HTML page for " ^ full_name in
      output_string html_channel "<p><b>Account short name:</b> ";
      output_string html_channel short_name;
      (*
      output_string html_channel "<br><b>Group:</b> ";
      output_string html_channel group;*)
      output_string html_channel "<br><b>Number of transactions:</b> ";
      output_string html_channel (string_of_int (Account.number_of_txns acct));
      (if not (Account.is_virtual acct) then
      begin
      output_string html_channel "<p>Negative amounts indicate that <i>";
      output_string html_channel full_name;
      output_string html_channel "</i> is indebted to a creditor." end);
      output_string html_channel "<p><table width=\"100%\">\n";
      output_string html_channel "<tr class=\"header\">\n";
      output_string html_channel "<td><b>Date (YYYY-MM-DD)</b></td>";
      (if not (Account.is_virtual acct) then
        output_string html_channel "<td><b>Creditor/Debtor</b></td>");
      output_string html_channel "<td><b>Amount</b></td>";
      output_string html_channel "<td><b>Description</b></td>";
      output_string html_channel "</tr>";
      Account.iter_txns_sorted (print_transaction html_channel acct context
                                                  cur_total checkpoint_day)
                               acct;
      let total = Account.total acct in
      let total_str = Sumofmoney.to_string_default total in
      let income_str = Sumofmoney.to_string_default (Account.income acct) in
      let expenditure_str =
        Sumofmoney.to_string_default (Account.expenditure acct)
      in
        output_string html_channel "<tr class=\"incomeexp\"><td>";
	output_string html_channel "</td><td>Income:</td><td>";
	output_string html_channel income_str;
        output_string html_channel "</td><td></td></tr><tr class=\"incomeexp\"><td>";
	output_string html_channel "</td><td>Expenditure:</td><td>";
	output_string html_channel expenditure_str;
	output_string html_channel "</td><td></td></tr>";
        (if (Sumofmoney.non_negative_default total) then
           output_string html_channel "<tr class=\"grandtotalp\">"
         else
           output_string html_channel "<tr class=\"grandtotaln\">");
        output_string html_channel "<td></td><td><b>Grand total:</b></td><td>";
        output_string html_channel total_str;
        output_string html_channel "</td><td></td></tr>";
        output_string html_channel "</table>\n";
        output_string html_channel "<p><a href=\"index.html\">Go back to summary sheet</a>.";
        output_string html_channel "<p><i>Processed at ";
        output_string html_channel now;
        output_string html_channel ".</i>";
        Htmlout.finish_page html_channel
  with Sys_error msg -> Misc.fail ("Error dumping HTML for " ^ short_name ^
                                   ":\n  " ^ msg)

(* Create HTML balance sheets for all accounts. *)
let create_balance_sheets () =
  Accountdbase.iter dump;
  Printf.printf "Emitted %d transactions.\n" !txn_total

