#!/usr/bin/env ruby
require 'cmxl' # for parsing the MT940 file
require 'roo' # for parsing the Excel file
require 'humanize' # for converting numbers to words

# set process locale (affects strftime, number formatting via locales)
ENV['LANG'] = 'de_DE.UTF-8'
ENV['LC_ALL'] ||= 'de_DE.UTF-8'

# date
def german_date(d); d.strftime("%d.%m.%Y"); end

# number
def german_number(n)
  parts = sprintf("%.2f", n).split('.')
  parts[0] = parts[0].gsub(/(?<=\d)(?=(?:\d{3})+$)/, '.')
  parts.join(',')
end



Humanize.configure do |config|
  config.default_locale = :de  # [:en, :es, :fr, :tr, :de, :id], default: :en
  config.decimals_as = :number # [:digits, :number], default: :digits
end

# usage ./beitragsquittung.rb <MT940 file> <Excel file> <member id>
# the member ID is optional. If not provided, all members are processed
# the time range is used from the MT940 file

def generate_reciept(mitgliedsnummer, name, vorname, strasse, hausnummer, plz, ort, betrag, betrag_in_worten, start_datum, end_datum, payments)
  puts "Generating reciept for #{mitgliedsnummer} #{name} #{vorname} #{strasse} #{hausnummer} #{plz} #{ort} #{betrag} #{betrag_in_worten} #{start_datum} #{end_datum}"

  # create a csv file with the payments
  # CSV.open("payments.csv", "wb") do |csv|
  # CSV.open("payments.csv", "wb", col_sep: ";", encoding: "UTF-8") do |csv|
  # CSV.open("payments.csv", "wb", encoding: "UTF-8", force_quotes: true) do |csv|
  CSV.open("payments.csv", "wb", col_sep: "\t", force_quotes: false, encoding: "UTF-8") do |csv|
    csv << ["datum", "betrag", "type", "verzicht"]
    payments.each do |payment|
      csv << [german_date(payment[:date]), german_number(payment[:amount]), payment[:type], payment[:verzicht]]
    end
  end

  # call latex to generate the pdf
  system("pdflatex -jobname=\"#{vorname}_#{name}-#{start_datum}_#{end_datum}\"  \"\\newcommand{\\vorname}{#{vorname}} \\newcommand{\\nachname}{#{name}} \\newcommand{\\strasse}{#{strasse + " " + hausnummer.to_s}} \\newcommand{\\plz}{#{plz}} \\newcommand{\\ort}{#{ort}} \\newcommand{\\summe}{#{betrag}} \\newcommand{\\betraginworten}{#{betrag_in_worten}} \\newcommand{\\startdatum}{#{start_datum}} \\newcommand{\\findatum}{#{end_datum}} \\newcommand{\\mitgliedsnummer}{#{mitgliedsnummer}} \\input{document.tex}\"")
end

def get_payments_from_member_id(mitgliedsnummer)

  Cmxl.config[:statement_separator] = /\n-.\n/m
  Cmxl.config[:raise_line_format_errors] = true

  # get the filename from the command line arguments
  filename = ARGV[0]

  # check if the filename is valid or not presented
  if filename.nil? || !File.exist?(filename)
    puts "Enter the filename of the MT940 statement to parse"
    filename = gets.chomp
  end

  # parse the MT940 file
  statements = Cmxl.parse(File.read(filename), :encoding => 'ISO-8859-1')

  # The statement contains transactions called Mitgliedsbeitrag. They are labled like this: "MREF"=>"Mitgliedsbeitrag Nr. 17" where 17 is the mitgliedsnummer.
  # for each payment of the mitgliedsbeitrag create an entry with the date and amount in the following datastructure
  payments = []

  statements.each do |s|
    s.transactions.each do |t|
      reference = ""
      t.sepa.each do |sf|
        # add the second entry of the sepa reference (sf) to the reference string if the first entry contans "MREF"
        if sf[0] == "MREF"
          reference = sf[1]
          # puts reference
        end

      end

      # if the mitgliedsnummer is a single digit number, the reference is "Mitgliedsbeitrag Nr. 01" and not "Mitgliedsbeitrag Nr. 1"
      # so we have to check for both cases
      if reference.include?("Mitgliedsbeitrag Nr. #{mitgliedsnummer.to_s.rjust(2, '0')}") ||
          reference.include?("Mitgliedsbeitrag Nr. #{mitgliedsnummer}") ||
          reference.include?("Mitgliedsbeitrag Mitgliedsnr.: #{mitgliedsnummer.to_s.rjust(2, '0')}") ||
          reference.include?("Mitgliedsbeitrag Mitgliedsnr.: #{mitgliedsnummer}")

        # puts t.entry_date
        # puts t.amount
        payment = {
            date: t.entry_date,
            amount: t.amount,
            type: "Mitgliedsbeitrag",
            verzicht: "nein"
        }
        payments << payment
        # puts
      end
    end
  end

  # get the start and end date of the statements
  start_datum = statements[0].opening_balance.date.strftime("%d.%m.%Y")
  end_datum = statements[statements.length() -1].closing_balance.date.strftime("%d.%m.%Y")

  return payments, start_datum, end_datum
end

def get_member_details_from_mitgliedsnummer(mitgliedsnummer)
  # get the member details from the excel file
  # the member details are stored in the first sheet
  # the first row contains the headings of the columns
  # the second row contains the values for the first member
  # the headings are: Nr.,	Nachname,	Vorname,	Geburtsdatum,	Straße,	Hausnummer,	Postleitzahl,	Ort
  # the values are: 1,	Mustermann,	Max,	01.01.1970,	Musterstraße,	1,	12345,	Musterstadt
  xlsx = Roo::Spreadsheet.open(ARGV[1])

  # create data structure
  member = {
      name: xlsx.sheet(0).row(mitgliedsnummer+1)[1],
      vorname: xlsx.sheet(0).row(mitgliedsnummer+1)[2],
      strasse: xlsx.sheet(0).row(mitgliedsnummer+1)[4],
      hausnummer: xlsx.sheet(0).row(mitgliedsnummer+1)[5],
      plz: xlsx.sheet(0).row(mitgliedsnummer+1)[6],
      ort: xlsx.sheet(0).row(mitgliedsnummer+1)[7]
  }
  return member
end

mitgliedsnummer = ARGV[2].to_i
if mitgliedsnummer.nil?
  # get number of members from the excel file it is the first value of the last row
  xlsx = Roo::Spreadsheet.open(ARGV[1])
  anzahl_members = xlsx.sheet(0).last_row[0]
  puts "Number of members: #{anzahl_members}"

  # loop over all members
  for mitgliedsnummer in 1..anzahl_members
    puts "Processing member #{mitgliedsnummer}"
    get_payments_from_member_id(mitgliedsnummer)
    generate_reciept(mitgliedsnummer, name, vorname, strasse, plz, ort, betrag, betrag_in_worten, start_datum, end_datum)
  end
else
  # get the payments from the MT940 file
  payments, start_datum, end_datum = get_payments_from_member_id(mitgliedsnummer)
  puts "Payments:"
  total_payment = 0
  payments.each do |payment|
    puts payment[:date].to_s + " " + payment[:amount].to_s
    total_payment += payment[:amount]
  end

  # convert the total payment to a string in german
  betrag_in_worten = total_payment.humanize
  puts "Total payment:" + total_payment.to_s + " in worten: " + betrag_in_worten
  puts

  member = get_member_details_from_mitgliedsnummer(mitgliedsnummer)
  generate_reciept(mitgliedsnummer, member[:name], member[:vorname], member[:strasse], member[:hausnummer], member[:plz], member[:ort], german_number(total_payment), betrag_in_worten, start_datum, end_datum, payments)
end
