#!/usr/bin/env python

import argparse
import codecs
import datetime
import json
import logging
import os.path
import subprocess
import tempfile

import inseven.finance


PDF_TABLE_COLUMNS = ["Date", "Payment ", "type ", "and ", "details", "Paid ", "out", "Paid ", "in", "Balance"]
PDF_TABLE_START = ["BALANCE ", "BROUGHT ", "FORWARD"]
PDF_TABLE_END = ["BALANCE ", "CARRIED ", "FORWARD"]
PDF_POSITION_THRESHOLD = 20


def pdf2json(path):
    with tempfile.NamedTemporaryFile(delete=False) as temp:
        subprocess.check_output(["/usr/local/bin/pdf2json", path, temp.name])
        with codecs.open(temp.name, "r", "ISO-8859-1") as f:
            return json.loads(f.read())


def line_matches_offset(line, strings, offset):
    try:
        for i, data in enumerate(strings):
            if line[i + offset]["data"] != data:
                return False
    except IndexError:
        return False
    return True


def line_matches(line, strings):
    if len(line) < len(strings):
        return False
    else:
        for i in xrange(0, len(line) - len(strings) + 1):
            if line_matches_offset(line, strings, i):
                return True
    return False


def lines_between(lines, start, end):
    result = []
    active = False
    for line in lines:
        if line_matches(line, start):
            active = True
            result.append(line)
        elif line_matches(line, end):
            active = False
        else:
            if active:
                result.append(line)
    return result


def format_number(text):
    return float(text.replace(",", ""))


def parse_page(page, date=None):
    nodes = sorted(page["text"], key=lambda x: x["top"])

    # Group the individual nodes into lines.
    lines = []
    top = 0
    line = []
    for node in nodes:
        if node["top"] != top:
            line = []
            lines.append(line)
            top = node["top"]
        line.append(node)

    # Determine the column offsets.
    details = None
    for line in lines:
        if line_matches(line, PDF_TABLE_COLUMNS):
                details = [line[0]["left"], line[1]["left"], line[5]["left"], line[7]["left"], line[9]["left"]]

    # Filter out the lines we're interested in.
    statements = lines_between(lines, PDF_TABLE_START, PDF_TABLE_END)

    record = None
    results = []
    for line in statements:

        # Check to see if the row has changed.
        line = sorted(line, key=lambda x: x["left"])
        if line[0]["left"] == details[0] or line[0]["left"] == details[1] or record is None:
            record = inseven.finance.Record()
            results.append(record)

        # Process nodes for the current row.
        for node in line:
            left = node["left"]
            data = node["data"]
            if left >= details[4] - PDF_POSITION_THRESHOLD:
                record.balance = format_number(data)
            elif left >= details[3] - PDF_POSITION_THRESHOLD:
                # Paid in.
                record.value = format_number(data)
            elif left >= details[2] - PDF_POSITION_THRESHOLD:
                # Paid out.
                record.value = (format_number(data) * -1)
            elif left >= details[1]:
                record.description = (record.description + " " + data).strip()
            elif left > details[0]:
                date = (date + " " + data).strip()
            elif left == details[0]:
                date = data.strip()

            # Keep attempting to parse the date until we have a valid date.
            try:
                record.date = datetime.datetime.strptime(date, "%d %b %y")
            except ValueError:
                pass

    if not len(results):
        return (results, date, None)

    # Check and retrieve the starting balance.
    balance = results.pop(0)
    assert balance.description == "BALANCE BROUGHT FORWARD", "Unable to determine starting balance."

    return (results, date, balance.balance)


def parse_pdf(path):

    contents = pdf2json(path)

    results = []
    date = None
    starting_balance = None
    for page in contents:
        (records, date, balance) = parse_page(page, date)
        if starting_balance is None:
            starting_balance = balance
        results.extend(records)

    total = starting_balance
    for result in results:
        logging.debug("%s", result)
        total = total + result.value
        if result.balance is not None:
            assert abs(total - result.balance) < 0.0001, "Unable to reconcile balances"

    return results

@@ -2692,24 +2692,56 @@
 de in line:%0A
+            logging.debug(line)%0A
             
@@ -2844,32 +2844,203 @@
 TION_THRESHOLD:%0A
+                logging.debug(%22Parsing '%25s'...%22 %25 data)%0A                if data == %22D%22:%0A                    record.balance = -1 * record.balance%0A                else:%0A    
                 

Support overdrawn.