# Path: ledgerbil/colorable.py
# class Colorable:
# 
#     START_CODE = "\033"
#     END_CODE = f"{START_CODE}[0m"
# 
#     BRIGHT_OFFSET = 60
# 
#     COLORS = {
#         "black": 30,
#         "gray": 30,
#         "grey": 30,
#         "red": 31,
#         "green": 32,
#         "yellow": 33,
#         "blue": 34,
#         "magenta": 35,
#         "purple": 35,
#         "cyan": 36,
#         "white": 37,
#     }
# 
#     ansi_escape = re.compile(r"\x1B\[[0-?]*[ -/]*[@-~]")
# 
#     def __init__(self, color, value, fmt="", bright=False):
# 
#         if color not in self.COLORS:
#             raise UnsupportedColorError(
#                 f"I don't know what to do with this color: {color}"
#             )
# 
#         self.my_color = color
#         self.value = value
#         self.bright = bright
#         self.format_string = fmt
# 
#     def __repr__(self):
#         return (
#             f"Colorable('{self.my_color}', '{self.value}', "
#             f"fmt='{self.format_string}', bright={self.bright})"
#         )
# 
#     def __str__(self):
#         start = self.ansi_sequence(self.COLORS[self.my_color], bright=self.bright)
#         ansi_str = f"{start}{self.value:{self.format_string}}{self.END_CODE}"
#         return ansi_str
# 
#     def __len__(self):
#         return len(self.value)
# 
#     def __eq__(self, other):
#         return str(self) == str(other)
# 
#     def ansi_sequence(self, code, bright=False):
#         offset = 60 if bright else 0
#         color = code + offset
#         return f"{self.START_CODE}[0;{color}m"
# 
#     def plain(self):
#         return self.value
# 
#     @staticmethod
#     def get_plain_string(ansi_string):
#         return Colorable.ansi_escape.sub("", ansi_string)
# 
# Path: ledgerbil/settings_getter.py
# def get_setting(setting, default=None):
#     if settings and hasattr(settings, setting):
#         return getattr(settings, setting)
# 
#     return defaults.get(setting, default)
# 
# Path: ledgerbil/util.py
# def get_date(date_string, the_format=None):
#     if not the_format:
#         the_format = get_setting("DATE_FORMAT")
#     return datetime.strptime(date_string, the_format).date()
# 
# def parse_args(args):
#     # args should be a string, but we'll make sure it isn't None
#     # (which would cause the string to be read from stdin)
#     try:
#         return shlex.split(args or "")
#     except ValueError as e:
#         print(f"*** {e}")
#         return None
# 
# Path: ledgerbil/ledgershell/runner.py
# def get_ledger_output(args=None):
#     cmd = get_ledger_command(args)
#     process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
#     output, _ = process.communicate()
#     return output.decode("unicode_escape").rstrip()
# 
# Path: ledgerbil/ledgershell/util.py
# def get_account_balance(line, shares=False, strip_account=True):
#     if shares:
#         match = SHARES_REGEX.match(line)
#         if match:
#             amount, symbol, account = match.groups()
#     else:
#         match = DOLLARS_REGEX.match(line)
#         if match:
#             amount, account = match.groups()
#             symbol = "$"
# 
#     if not match:
#         return None
# 
#     return AccountBalance(
#         account.strip() if strip_account else account, get_float(amount), symbol
#     )
# 
# def get_first_dollar_amount_float(line):
#     DOLLARS = 0
#     match = FIRST_DOLLAR_AMOUNT_REGEX.match(line)
#     if match:
#         return get_float(match.groups()[DOLLARS])
#     return None
# 
# def get_payee_subtotal(line):
#     DOLLARS = 0
#     match = PAYEE_SUBTOTAL_REGEX.match(line)
#     if match:
#         return get_float(match.groups()[DOLLARS])
#     return None


import argparse
import csv
import re
import sys
from collections import defaultdict
from concurrent import futures
from datetime import date
from io import StringIO
from textwrap import dedent
from dateutil.relativedelta import relativedelta
from .. import util
from ..colorable import Colorable
from ..settings_getter import get_setting
from ..util import get_date, parse_args
from .runner import get_ledger_output
from .util import get_account_balance, get_first_dollar_amount_float, get_payee_subtotal
        networth_period = "tomorrow" if ending else period_name
        column = get_column_networth(networth_period, ledger_args)
    else:
        column = get_column_accounts(period_name, ledger_args + ending, args.depth)

    return period_name, column


def get_column_accounts(period_name, ledger_args, depth=0):
    lines = get_ledger_output(
        ("balance", "--flat", "--period", period_name) + ledger_args
    ).split("\n")
    column = defaultdict(int)
    next_is_total = False

    for line in lines:

        if next_is_total:
            validate_column_total(
                period_name,
                column_total=sum(column.values()),
                ledgers_total=util.get_float(line),
            )
            break
        elif line and line[0] == "-":  # ledger's total line separator
            next_is_total = True
            continue
        elif line == "":  # last element when ledger doesn't give a total
            break


# Path: ledgerbil/colorable.py
# class Colorable:
# 
#     START_CODE = "\033"
#     END_CODE = f"{START_CODE}[0m"
# 
#     BRIGHT_OFFSET = 60
# 
#     COLORS = {
#         "black": 30,
#         "gray": 30,
#         "grey": 30,
#         "red": 31,
#         "green": 32,
#         "yellow": 33,
#         "blue": 34,
#         "magenta": 35,
#         "purple": 35,
#         "cyan": 36,
#         "white": 37,
#     }
# 
#     ansi_escape = re.compile(r"\x1B\[[0-?]*[ -/]*[@-~]")
# 
#     def __init__(self, color, value, fmt="", bright=False):
# 
#         if color not in self.COLORS:
#             raise UnsupportedColorError(
#                 f"I don't know what to do with this color: {color}"
#             )
# 
#         self.my_color = color
#         self.value = value
#         self.bright = bright
#         self.format_string = fmt
# 
#     def __repr__(self):
#         return (
#             f"Colorable('{self.my_color}', '{self.value}', "
#             f"fmt='{self.format_string}', bright={self.bright})"
#         )
# 
#     def __str__(self):
#         start = self.ansi_sequence(self.COLORS[self.my_color], bright=self.bright)
#         ansi_str = f"{start}{self.value:{self.format_string}}{self.END_CODE}"
#         return ansi_str
# 
#     def __len__(self):
#         return len(self.value)
# 
#     def __eq__(self, other):
#         return str(self) == str(other)
# 
#     def ansi_sequence(self, code, bright=False):
#         offset = 60 if bright else 0
#         color = code + offset
#         return f"{self.START_CODE}[0;{color}m"
# 
#     def plain(self):
#         return self.value
# 
#     @staticmethod
#     def get_plain_string(ansi_string):
#         return Colorable.ansi_escape.sub("", ansi_string)
# 
# Path: ledgerbil/settings_getter.py
# def get_setting(setting, default=None):
#     if settings and hasattr(settings, setting):
#         return getattr(settings, setting)
# 
#     return defaults.get(setting, default)
# 
# Path: ledgerbil/util.py
# def get_date(date_string, the_format=None):
#     if not the_format:
#         the_format = get_setting("DATE_FORMAT")
#     return datetime.strptime(date_string, the_format).date()
# 
# def parse_args(args):
#     # args should be a string, but we'll make sure it isn't None
#     # (which would cause the string to be read from stdin)
#     try:
#         return shlex.split(args or "")
#     except ValueError as e:
#         print(f"*** {e}")
#         return None
# 
# Path: ledgerbil/ledgershell/runner.py
# def get_ledger_output(args=None):
#     cmd = get_ledger_command(args)
#     process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
#     output, _ = process.communicate()
#     return output.decode("unicode_escape").rstrip()
# 
# Path: ledgerbil/ledgershell/util.py
# def get_account_balance(line, shares=False, strip_account=True):
#     if shares:
#         match = SHARES_REGEX.match(line)
#         if match:
#             amount, symbol, account = match.groups()
#     else:
#         match = DOLLARS_REGEX.match(line)
#         if match:
#             amount, account = match.groups()
#             symbol = "$"
# 
#     if not match:
#         return None
# 
#     return AccountBalance(
#         account.strip() if strip_account else account, get_float(amount), symbol
#     )
# 
# def get_first_dollar_amount_float(line):
#     DOLLARS = 0
#     match = FIRST_DOLLAR_AMOUNT_REGEX.match(line)
#     if match:
#         return get_float(match.groups()[DOLLARS])
#     return None
# 
# def get_payee_subtotal(line):
#     DOLLARS = 0
#     match = PAYEE_SUBTOTAL_REGEX.match(line)
#     if match:
#         return get_float(match.groups()[DOLLARS])
#     return None


# Path: ledgerbil/ledgershell/grid.py
import argparse
import csv
import re
import sys
from collections import defaultdict
from concurrent import futures
from datetime import date
from io import StringIO
from textwrap import dedent
from dateutil.relativedelta import relativedelta
from .. import util
from ..colorable import Colorable
from ..settings_getter import get_setting
from ..util import get_date, parse_args
from .runner import get_ledger_output
from .util import get_account_balance, get_first_dollar_amount_float, get_payee_subtotal

        networth_period = "tomorrow" if ending else period_name
        column = get_column_networth(networth_period, ledger_args)
    else:
        column = get_column_accounts(period_name, ledger_args + ending, args.depth)

    return period_name, column


def get_column_accounts(period_name, ledger_args, depth=0):
    lines = get_ledger_output(
        ("balance", "--flat", "--period", period_name) + ledger_args
    ).split("\n")
    column = defaultdict(int)
    next_is_total = False

    for line in lines:

        if next_is_total:
            validate_column_total(
                period_name,
                column_total=sum(column.values()),
                ledgers_total=util.get_float(line),
            )
            break
        elif line and line[0] == "-":  # ledger's total line separator
            next_is_total = True
            continue
        elif line == "":  # last element when ledger doesn't give a total
            break


        balance = get_account_balance(line)