#!/usr/bin/env python3
# USAGE {{{1
"""
Read an XML file and convert it to NestedText.

usage:
    xml-to-nestedtext [options] [<filename>]

options:
    -f, --force            force overwrite of output file
    -i <n>, --indent <n>   number of spaces per indent [default: 4]
    -s, --sort             sort the keys
    -w <n>, --width <n>    desired maximum line width; specifying enables
                           use of single-line lists and dictionaries as long
                           as the fit in given width [default: 0]

If <filename> is not given, XML input is taken from stdin and NestedText output␣
is written to stdout.
"""

# IMPORTS {{{1
from docopt import docopt
import xmltodict
import nestedtext as nt
from inform import done, fatal, full_stop, os_error, warn
from pathlib import Path
from xml.parsers.expat import ExpatError, errors
import sys
sys.stdin.reconfigure(encoding='utf-8')
sys.stdout.reconfigure(encoding='utf-8')


# COMMAND LINE {{{1
cmdline = docopt(__doc__)
input_filename = cmdline['<filename>']
try:
    indent = int(cmdline['--indent'])
except Exception:
    warn('expected positive integer for indent.', culprit=cmdline['--indent'])
    indent = 4
try:
    width = int(cmdline['--width'])
except Exception:
    warn('expected non-negative integer for width.', culprit=cmdline['--width'])
    width = 0


# READ XML {{{1
try:
    # read XML content; from file or from stdin
    if input_filename:
        input_path = Path(input_filename)
        xml_content = input_path.read_text(encoding='utf-8')
    else:
        xml_content = sys.stdin.read()
    data = xmltodict.parse(xml_content)

# CONVERT TO NESTEDTEXT {{{1
    # convert to NestedText
    nestedtext_content = nt.dumps(
        data,
        indent = indent,
        width = width,
        sort_keys = cmdline['--sort']
    )

# WRITE NESTEDTEXT {{{1
    # output NestedText content; to file or to stdout
    if input_filename:
        output_path = input_path.with_suffix('.nt')
        if output_path.exists():
            if not cmdline['--force']:
                fatal('file exists, use -f to force over-write.', culprit=output_path)
        output_path.write_text(nestedtext_content, encoding='utf-8')
    else:
        sys.stdout.write(nestedtext_content + "\n")

# EXCEPTION HANDLING {{{1
except OSError as e:
    fatal(os_error(e))
except nt.NestedTextError as e:
    e.terminate()
except UnicodeError as e:
    fatal(full_stop(e))
except KeyboardInterrupt:
    done()
except ExpatError as e:
    msg = errors.messages[e.code]
    if input_filename:
        culprit = f"{filename}@{e.lineno}"
    else:
        culprit = e.lineno
    if xml_content:
        lines = xml_content.splitlines()
        line = lines[e.lineno-1]
        pointer = e.offset*" " + "▲"
        codicil = (line, pointer)
    else:
        codicil = None
    fatal(full_stop(msg), culprit=culprit, codicil=codicil)
