#!/usr/bin/env python3
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

import argparse
import sys
import shutil
import textwrap
import csv
import re
from collections import namedtuple

Clipping = namedtuple('Clipping', 'title, body')


def open_clippings_txt(path, mode):
    newline = None
    if 'a' in mode or 'w' in mode:
        newline = '\r\n'
    return open(path, mode=mode, encoding='utf-8-sig', newline=newline)


def each_clipping():
    title = None
    body = []
    for line in open_clippings_txt('My Clippings.txt', mode='r'):
        if title is None:
            title = line
        else:
            body.append(line)

        if line == '==========\n':
            if title.strip() == '':
                print("EMPTY: " + ''.join(body))
            yield Clipping(title, ''.join(body))
            title = None
            body = []


def on_list(args):
    seen = set()
    for clip in each_clipping():
        if clip.title not in seen:
            seen.add(clip.title)
            print(clip.title.rstrip())


def on_export(args):
    titles = {}
    for clip in each_clipping():
        if args.pattern in clip.title:
            if clip.title not in titles:
                clippings = []
                titles[clip.title] = clippings
            else:
                clippings = titles[clip.title]

            clippings.append(clip)

    if args.format == 'csv':
        export_csv(titles)
    else:
        export_markdown(titles)


def export_markdown(titles):
    first_title = True
    for title, clippings in titles.items():
        if first_title:
            first_title = False
        else:
            print('')

        print("### {}".format(title))

        for clipping in clippings:
            body = clipping.body.split('\n', 2)[2]
            body = body.rsplit('\n', 2)[0]
            print('-   {}'.format(textwrap.indent(body, '    ')[4:]))
            print('')


META_RE = re.compile(
    r".*位置 (#\d+)(-\d+| )）?的(标注|笔记) \| 添加于 (\d+)年(\d+)月(\d+)日星期. (上午|下午)(\d+):(\d+):(\d+)")


def split_title_author(line):
    left_count = 1
    last_left_paren = line.rfind('(')
    while left_count < line.count(')', last_left_paren):
        left_count += 1
        last_left_paren = line.rfind('(', 0, last_left_paren)

    title = line[0:last_left_paren - 1]
    author = line[last_left_paren + 1:-2]

    return (title, author)


def parse_meta_time(meta_fields):
    year = int(meta_fields[3])
    month = int(meta_fields[4])
    day = int(meta_fields[5])

    ampm = meta_fields[6]
    hour = int(meta_fields[7])
    if hour == 12:
        hour = 0

    min = int(meta_fields[8])
    sec = int(meta_fields[9])

    if ampm == '下午':
        hour = hour + 12

    return "{:04}-{:02}-{:02} {:02}:{:02}:{:02}".format(year, month, day, hour, min, sec)


def export_csv(titles):
    w = csv.writer(sys.stdout, delimiter=',', quotechar='"')
    w.writerow(['Highlight', 'Title', 'Author', 'Note', 'Location', 'Date'])

    for title, clippings in titles.items():
        title, author = split_title_author(title)

        prev_highlight = None
        prev_end_loc = None
        prev_note = None

        for clipping in clippings:
            parts = clipping.body.split('\n', 2)
            meta = parts[0]
            body = parts[2]
            body = body.rsplit('\n', 2)[0]

            # 0 - start, 1 - end, 2 - type, 3,4,5 - date, 6 - am/pm, 7,8,9 - time
            meta_match = META_RE.match(meta)
            if meta_match is None:
                print("Unknown meta: {}".format(meta), file=sys.stderr)
            meta_fields = meta_match.groups()

            time = parse_meta_time(meta_fields)

            if meta_fields[2] == '标注':
                loc = meta_fields[0][1:]
                prev_end_loc = meta_fields[1][1:]

                if prev_note is not None:
                    if prev_note[0] == prev_end_loc:
                        w.writerow(
                            [body, title, author, prev_note[1], loc, time])
                        prev_note = None
                    else:
                        print("笔记位置和标注不匹配: #{} {}".format(
                            prev_note[0], prev_note[1]), file=sys.stderr)
                        sys.exit(1)
                else:
                    if prev_highlight is not None:
                        w.writerow(prev_highlight)

                    prev_highlight = [body, title, author, '', loc, time]
            else:
                loc = meta_fields[0][1:]
                if loc != prev_end_loc:
                    if prev_note is not None:
                        print("笔记位置和标注不匹配: #{} {}".format(
                            prev_note[0], prev_note[1]), file=sys.stderr)
                        sys.exit(1)
                    prev_note = [loc, body]
                else:
                    prev_highlight[3] = body

                if prev_highlight is not None:
                    w.writerow(prev_highlight)
                    prev_highlight = None
                    prev_end_loc = None

        if prev_highlight is not None:
            w.writerow(prev_highlight)


def on_archive(args):
    keep = []
    seen = set()
    with open_clippings_txt('archive.txt', mode='a') as archive:
        for clip in each_clipping():
            if args.pattern in clip.title:
                if clip.title not in seen:
                    seen.add(clip.title)
                    print("Archive {}".format(clip.title.rstrip()))
                archive.write(clip.title)
                archive.write(clip.body)
            else:
                keep.append(clip)

    shutil.copy('My Clippings.txt', 'backup.txt')
    with open_clippings_txt('My Clippings.txt', mode='w') as orig:
        for clip in keep:
            if clip.title not in seen:
                seen.add(clip.title)
                print("Keep {}".format(clip.title.rstrip()))
            orig.write(clip.title)
            orig.write(clip.body)


parser = argparse.ArgumentParser(description='Kindle Clippings Cli')
subparsers = parser.add_subparsers(help='sub-command help')

parser_list = subparsers.add_parser('list', help='List titles.')
parser_list.set_defaults(func=on_list)
parser_archive = subparsers.add_parser(
    'archive', help='extract matching titles and append them to archive.txt')
parser_archive.add_argument(
    'pattern', help='archive titles matching the pattern')
parser_archive.set_defaults(func=on_archive)
parser_export = subparsers.add_parser(
    'export', help='export matching titles as markdown')
parser_export.add_argument(
    '-f', '--format', help='choose export format (default: markdown)', choices=['markdown', 'csv'], default='markdown')
parser_export.add_argument(
    'pattern', help='export titles matching the pattern')
parser_export.set_defaults(func=on_export)

args = parser.parse_args()
args.func(args)

832dcdf62bc8b9b432a692704909f9fc2ef0b20e
<mask token>


def on_list(args):
    seen = set()
    for clip in each_clipping():
        if clip.title not in seen:
            seen.add(clip.title)
            print(clip.title.rstrip())


<mask token>


def export_markdown(titles):
    first_title = True
    for title, clippings in titles.items():
        if first_title:
            first_title = False
        else:
            print('')
        print('### {}'.format(title))
        for clipping in clippings:
            body = clipping.body.split('\n', 2)[2]
            body = body.rsplit('\n', 2)[0]
            print('-   {}'.format(textwrap.indent(body, '    ')[4:]))
            print('')


<mask token>


def split_title_author(line):
    left_count = 1
    last_left_paren = line.rfind('(')
    while left_count < line.count(')', last_left_paren):
        left_count += 1
        last_left_paren = line.rfind('(', 0, last_left_paren)
    title = line[0:last_left_paren - 1]
    author = line[last_left_paren + 1:-2]
    return title, author


def parse_meta_time(meta_fields):
    year = int(meta_fields[3])
    month = int(meta_fields[4])
    day = int(meta_fields[5])
    ampm = meta_fields[6]
    hour = int(meta_fields[7])
    if hour == 12:
        hour = 0
    min = int(meta_fields[8])
    sec = int(meta_fields[9])
    if ampm == '下午':
        hour = hour + 12
    return '{:04}-{:02}-{:02} {:02}:{:02}:{:02}'.format(year, month, day,
        hour, min, sec)


def export_csv(titles):
    w = csv.writer(sys.stdout, delimiter=',', quotechar='"')
    w.writerow(['Highlight', 'Title', 'Author', 'Note', 'Location', 'Date'])
    for title, clippings in titles.items():
        title, author = split_title_author(title)
        prev_highlight = None
        prev_end_loc = None
        prev_note = None
        for clipping in clippings:
            parts = clipping.body.split('\n', 2)
            meta = parts[0]
            body = parts[2]
            body = body.rsplit('\n', 2)[0]
            meta_match = META_RE.match(meta)
            if meta_match is None:
                print('Unknown meta: {}'.format(meta), file=sys.stderr)
            meta_fields = meta_match.groups()
            time = parse_meta_time(meta_fields)
            if meta_fields[2] == '标注':
                loc = meta_fields[0][1:]
                prev_end_loc = meta_fields[1][1:]
                if prev_note is not None:
                    if prev_note[0] == prev_end_loc:
                        w.writerow([body, title, author, prev_note[1], loc,
                            time])
                        prev_note = None
                    else:
                        print('笔记位置和标注不匹配: #{} {}'.format(prev_note[0],
                            prev_note[1]), file=sys.stderr)
                        sys.exit(1)
                else:
                    if prev_highlight is not None:
                        w.writerow(prev_highlight)
                    prev_highlight = [body, title, author, '', loc, time]
            else:
                loc = meta_fields[0][1:]
                if loc != prev_end_loc:
                    if prev_note is not None:
                        print('笔记位置和标注不匹配: #{} {}'.format(prev_note[0],
                            prev_note[1]), file=sys.stderr)
                        sys.exit(1)
                    prev_note = [loc, body]
                else:
                    prev_highlight[3] = body
                if prev_highlight is not None:
                    w.writerow(prev_highlight)
                    prev_highlight = None
                    prev_end_loc = None
        if prev_highlight is not None:
            w.writerow(prev_highlight)


def on_archive(args):
    keep = []
    seen = set()
    with open_clippings_txt('archive.txt', mode='a') as archive:
        for clip in each_clipping():
            if args.pattern in clip.title:
                if clip.title not in seen:
                    seen.add(clip.title)
                    print('Archive {}'.format(clip.title.rstrip()))
                archive.write(clip.title)
                archive.write(clip.body)
            else:
                keep.append(clip)
    shutil.copy('My Clippings.txt', 'backup.txt')
    with open_clippings_txt('My Clippings.txt', mode='w') as orig:
        for clip in keep:
            if clip.title not in seen:
                seen.add(clip.title)
                print('Keep {}'.format(clip.title.rstrip()))
            orig.write(clip.title)
            orig.write(clip.body)


<mask token>

<mask token>


def open_clippings_txt(path, mode):
    newline = None
    if 'a' in mode or 'w' in mode:
        newline = '\r\n'
    return open(path, mode=mode, encoding='utf-8-sig', newline=newline)


<mask token>


def on_list(args):
    seen = set()
    for clip in each_clipping():
        if clip.title not in seen:
            seen.add(clip.title)
            print(clip.title.rstrip())


def on_export(args):
    titles = {}
    for clip in each_clipping():
        if args.pattern in clip.title:
            if clip.title not in titles:
                clippings = []
                titles[clip.title] = clippings
            else:
                clippings = titles[clip.title]
            clippings.append(clip)
    if args.format == 'csv':
        export_csv(titles)
    else:
        export_markdown(titles)


def export_markdown(titles):
    first_title = True
    for title, clippings in titles.items():
        if first_title:
            first_title = False
        else:
            print('')
        print('### {}'.format(title))
        for clipping in clippings:
            body = clipping.body.split('\n', 2)[2]
            body = body.rsplit('\n', 2)[0]
            print('-   {}'.format(textwrap.indent(body, '    ')[4:]))
            print('')


<mask token>


def split_title_author(line):
    left_count = 1
    last_left_paren = line.rfind('(')
    while left_count < line.count(')', last_left_paren):
        left_count += 1
        last_left_paren = line.rfind('(', 0, last_left_paren)
    title = line[0:last_left_paren - 1]
    author = line[last_left_paren + 1:-2]
    return title, author


def parse_meta_time(meta_fields):
    year = int(meta_fields[3])
    month = int(meta_fields[4])
    day = int(meta_fields[5])
    ampm = meta_fields[6]
    hour = int(meta_fields[7])
    if hour == 12:
        hour = 0
    min = int(meta_fields[8])
    sec = int(meta_fields[9])
    if ampm == '下午':
        hour = hour + 12
    return '{:04}-{:02}-{:02} {:02}:{:02}:{:02}'.format(year, month, day,
        hour, min, sec)


def export_csv(titles):
    w = csv.writer(sys.stdout, delimiter=',', quotechar='"')
    w.writerow(['Highlight', 'Title', 'Author', 'Note', 'Location', 'Date'])
    for title, clippings in titles.items():
        title, author = split_title_author(title)
        prev_highlight = None
        prev_end_loc = None
        prev_note = None
        for clipping in clippings:
            parts = clipping.body.split('\n', 2)
            meta = parts[0]
            body = parts[2]
            body = body.rsplit('\n', 2)[0]
            meta_match = META_RE.match(meta)
            if meta_match is None:
                print('Unknown meta: {}'.format(meta), file=sys.stderr)
            meta_fields = meta_match.groups()
            time = parse_meta_time(meta_fields)
            if meta_fields[2] == '标注':
                loc = meta_fields[0][1:]
                prev_end_loc = meta_fields[1][1:]
                if prev_note is not None:
                    if prev_note[0] == prev_end_loc:
                        w.writerow([body, title, author, prev_note[1], loc,
                            time])
                        prev_note = None
                    else:
                        print('笔记位置和标注不匹配: #{} {}'.format(prev_note[0],
                            prev_note[1]), file=sys.stderr)
                        sys.exit(1)
                else:
                    if prev_highlight is not None:
                        w.writerow(prev_highlight)
                    prev_highlight = [body, title, author, '', loc, time]
            else:
                loc = meta_fields[0][1:]
                if loc != prev_end_loc:
                    if prev_note is not None:
                        print('笔记位置和标注不匹配: #{} {}'.format(prev_note[0],
                            prev_note[1]), file=sys.stderr)
                        sys.exit(1)
                    prev_note = [loc, body]
                else:
                    prev_highlight[3] = body
                if prev_highlight is not None:
                    w.writerow(prev_highlight)
                    prev_highlight = None
                    prev_end_loc = None
        if prev_highlight is not None:
            w.writerow(prev_highlight)


def on_archive(args):
    keep = []
    seen = set()
    with open_clippings_txt('archive.txt', mode='a') as archive:
        for clip in each_clipping():
            if args.pattern in clip.title:
                if clip.title not in seen:
                    seen.add(clip.title)
                    print('Archive {}'.format(clip.title.rstrip()))
                archive.write(clip.title)
                archive.write(clip.body)
            else:
                keep.append(clip)
    shutil.copy('My Clippings.txt', 'backup.txt')
    with open_clippings_txt('My Clippings.txt', mode='w') as orig:
        for clip in keep:
            if clip.title not in seen:
                seen.add(clip.title)
                print('Keep {}'.format(clip.title.rstrip()))
            orig.write(clip.title)
            orig.write(clip.body)


<mask token>

<mask token>
Clipping = namedtuple('Clipping', 'title, body')


def open_clippings_txt(path, mode):
    newline = None
    if 'a' in mode or 'w' in mode:
        newline = '\r\n'
    return open(path, mode=mode, encoding='utf-8-sig', newline=newline)


def each_clipping():
    title = None
    body = []
    for line in open_clippings_txt('My Clippings.txt', mode='r'):
        if title is None:
            title = line
        else:
            body.append(line)
        if line == '==========\n':
            if title.strip() == '':
                print('EMPTY: ' + ''.join(body))
            yield Clipping(title, ''.join(body))
            title = None
            body = []


def on_list(args):
    seen = set()
    for clip in each_clipping():
        if clip.title not in seen:
            seen.add(clip.title)
            print(clip.title.rstrip())


def on_export(args):
    titles = {}
    for clip in each_clipping():
        if args.pattern in clip.title:
            if clip.title not in titles:
                clippings = []
                titles[clip.title] = clippings
            else:
                clippings = titles[clip.title]
            clippings.append(clip)
    if args.format == 'csv':
        export_csv(titles)
    else:
        export_markdown(titles)


def export_markdown(titles):
    first_title = True
    for title, clippings in titles.items():
        if first_title:
            first_title = False
        else:
            print('')
        print('### {}'.format(title))
        for clipping in clippings:
            body = clipping.body.split('\n', 2)[2]
            body = body.rsplit('\n', 2)[0]
            print('-   {}'.format(textwrap.indent(body, '    ')[4:]))
            print('')


META_RE = re.compile(
    '.*位置 (#\\d+)(-\\d+| )）?的(标注|笔记) \\| 添加于 (\\d+)年(\\d+)月(\\d+)日星期. (上午|下午)(\\d+):(\\d+):(\\d+)'
    )


def split_title_author(line):
    left_count = 1
    last_left_paren = line.rfind('(')
    while left_count < line.count(')', last_left_paren):
        left_count += 1
        last_left_paren = line.rfind('(', 0, last_left_paren)
    title = line[0:last_left_paren - 1]
    author = line[last_left_paren + 1:-2]
    return title, author


def parse_meta_time(meta_fields):
    year = int(meta_fields[3])
    month = int(meta_fields[4])
    day = int(meta_fields[5])
    ampm = meta_fields[6]
    hour = int(meta_fields[7])
    if hour == 12:
        hour = 0
    min = int(meta_fields[8])
    sec = int(meta_fields[9])
    if ampm == '下午':
        hour = hour + 12
    return '{:04}-{:02}-{:02} {:02}:{:02}:{:02}'.format(year, month, day,
        hour, min, sec)


def export_csv(titles):
    w = csv.writer(sys.stdout, delimiter=',', quotechar='"')
    w.writerow(['Highlight', 'Title', 'Author', 'Note', 'Location', 'Date'])
    for title, clippings in titles.items():
        title, author = split_title_author(title)
        prev_highlight = None
        prev_end_loc = None
        prev_note = None
        for clipping in clippings:
            parts = clipping.body.split('\n', 2)
            meta = parts[0]
            body = parts[2]
            body = body.rsplit('\n', 2)[0]
            meta_match = META_RE.match(meta)
            if meta_match is None:
                print('Unknown meta: {}'.format(meta), file=sys.stderr)
            meta_fields = meta_match.groups()
            time = parse_meta_time(meta_fields)
            if meta_fields[2] == '标注':
                loc = meta_fields[0][1:]
                prev_end_loc = meta_fields[1][1:]
                if prev_note is not None:
                    if prev_note[0] == prev_end_loc:
                        w.writerow([body, title, author, prev_note[1], loc,
                            time])
                        prev_note = None
                    else:
                        print('笔记位置和标注不匹配: #{} {}'.format(prev_note[0],
                            prev_note[1]), file=sys.stderr)
                        sys.exit(1)
                else:
                    if prev_highlight is not None:
                        w.writerow(prev_highlight)
                    prev_highlight = [body, title, author, '', loc, time]
            else:
                loc = meta_fields[0][1:]
                if loc != prev_end_loc:
                    if prev_note is not None:
                        print('笔记位置和标注不匹配: #{} {}'.format(prev_note[0],
                            prev_note[1]), file=sys.stderr)
                        sys.exit(1)
                    prev_note = [loc, body]
                else:
                    prev_highlight[3] = body
                if prev_highlight is not None:
                    w.writerow(prev_highlight)
                    prev_highlight = None
                    prev_end_loc = None
        if prev_highlight is not None:
            w.writerow(prev_highlight)


def on_archive(args):
    keep = []
    seen = set()
    with open_clippings_txt('archive.txt', mode='a') as archive:
        for clip in each_clipping():
            if args.pattern in clip.title:
                if clip.title not in seen:
                    seen.add(clip.title)
                    print('Archive {}'.format(clip.title.rstrip()))
                archive.write(clip.title)
                archive.write(clip.body)
            else:
                keep.append(clip)
    shutil.copy('My Clippings.txt', 'backup.txt')
    with open_clippings_txt('My Clippings.txt', mode='w') as orig:
        for clip in keep:
            if clip.title not in seen:
                seen.add(clip.title)
                print('Keep {}'.format(clip.title.rstrip()))
            orig.write(clip.title)
            orig.write(clip.body)


parser = argparse.ArgumentParser(description='Kindle Clippings Cli')
subparsers = parser.add_subparsers(help='sub-command help')
parser_list = subparsers.add_parser('list', help='List titles.')
parser_list.set_defaults(func=on_list)
parser_archive = subparsers.add_parser('archive', help=
    'extract matching titles and append them to archive.txt')
parser_archive.add_argument('pattern', help=
    'archive titles matching the pattern')
parser_archive.set_defaults(func=on_archive)
parser_export = subparsers.add_parser('export', help=
    'export matching titles as markdown')
parser_export.add_argument('-f', '--format', help=
    'choose export format (default: markdown)', choices=['markdown', 'csv'],
    default='markdown')
parser_export.add_argument('pattern', help='export titles matching the pattern'
    )
parser_export.set_defaults(func=on_export)
args = parser.parse_args()
args.func(args)

import argparse
import sys
import shutil
import textwrap
import csv
import re
from collections import namedtuple
Clipping = namedtuple('Clipping', 'title, body')


def open_clippings_txt(path, mode):
    newline = None
    if 'a' in mode or 'w' in mode:
        newline = '\r\n'
    return open(path, mode=mode, encoding='utf-8-sig', newline=newline)


def each_clipping():
    title = None
    body = []
    for line in open_clippings_txt('My Clippings.txt', mode='r'):
        if title is None:
            title = line
        else:
            body.append(line)
        if line == '==========\n':
            if title.strip() == '':
                print('EMPTY: ' + ''.join(body))
            yield Clipping(title, ''.join(body))
            title = None
            body = []


def on_list(args):
    seen = set()
    for clip in each_clipping():
        if clip.title not in seen:
            seen.add(clip.title)
            print(clip.title.rstrip())


def on_export(args):
    titles = {}
    for clip in each_clipping():
        if args.pattern in clip.title:
            if clip.title not in titles:
                clippings = []
                titles[clip.title] = clippings
            else:
                clippings = titles[clip.title]
            clippings.append(clip)
    if args.format == 'csv':
        export_csv(titles)
    else:
        export_markdown(titles)


def export_markdown(titles):
    first_title = True
    for title, clippings in titles.items():
        if first_title:
            first_title = False
        else:
            print('')
        print('### {}'.format(title))
        for clipping in clippings:
            body = clipping.body.split('\n', 2)[2]
            body = body.rsplit('\n', 2)[0]
            print('-   {}'.format(textwrap.indent(body, '    ')[4:]))
            print('')


META_RE = re.compile(
    '.*位置 (#\\d+)(-\\d+| )）?的(标注|笔记) \\| 添加于 (\\d+)年(\\d+)月(\\d+)日星期. (上午|下午)(\\d+):(\\d+):(\\d+)'
    )


def split_title_author(line):
    left_count = 1
    last_left_paren = line.rfind('(')
    while left_count < line.count(')', last_left_paren):
        left_count += 1
        last_left_paren = line.rfind('(', 0, last_left_paren)
    title = line[0:last_left_paren - 1]
    author = line[last_left_paren + 1:-2]
    return title, author


def parse_meta_time(meta_fields):
    year = int(meta_fields[3])
    month = int(meta_fields[4])
    day = int(meta_fields[5])
    ampm = meta_fields[6]
    hour = int(meta_fields[7])
    if hour == 12:
        hour = 0
    min = int(meta_fields[8])
    sec = int(meta_fields[9])
    if ampm == '下午':
        hour = hour + 12
    return '{:04}-{:02}-{:02} {:02}:{:02}:{:02}'.format(year, month, day,
        hour, min, sec)


def export_csv(titles):
    w = csv.writer(sys.stdout, delimiter=',', quotechar='"')
    w.writerow(['Highlight', 'Title', 'Author', 'Note', 'Location', 'Date'])
    for title, clippings in titles.items():
        title, author = split_title_author(title)
        prev_highlight = None
        prev_end_loc = None
        prev_note = None
        for clipping in clippings:
            parts = clipping.body.split('\n', 2)
            meta = parts[0]
            body = parts[2]
            body = body.rsplit('\n', 2)[0]
            meta_match = META_RE.match(meta)
            if meta_match is None:
                print('Unknown meta: {}'.format(meta), file=sys.stderr)
            meta_fields = meta_match.groups()
            time = parse_meta_time(meta_fields)
            if meta_fields[2] == '标注':
                loc = meta_fields[0][1:]
                prev_end_loc = meta_fields[1][1:]
                if prev_note is not None:
                    if prev_note[0] == prev_end_loc:
                        w.writerow([body, title, author, prev_note[1], loc,
                            time])
                        prev_note = None
                    else:
                        print('笔记位置和标注不匹配: #{} {}'.format(prev_note[0],
                            prev_note[1]), file=sys.stderr)
                        sys.exit(1)
                else:
                    if prev_highlight is not None:
                        w.writerow(prev_highlight)
                    prev_highlight = [body, title, author, '', loc, time]
            else:
                loc = meta_fields[0][1:]
                if loc != prev_end_loc:
                    if prev_note is not None:
                        print('笔记位置和标注不匹配: #{} {}'.format(prev_note[0],
                            prev_note[1]), file=sys.stderr)
                        sys.exit(1)
                    prev_note = [loc, body]
                else:
                    prev_highlight[3] = body
                if prev_highlight is not None:
                    w.writerow(prev_highlight)
                    prev_highlight = None
                    prev_end_loc = None
        if prev_highlight is not None:
            w.writerow(prev_highlight)


def on_archive(args):
    keep = []
    seen = set()
    with open_clippings_txt('archive.txt', mode='a') as archive:
        for clip in each_clipping():
            if args.pattern in clip.title:
                if clip.title not in seen:
                    seen.add(clip.title)
                    print('Archive {}'.format(clip.title.rstrip()))
                archive.write(clip.title)
                archive.write(clip.body)
            else:
                keep.append(clip)
    shutil.copy('My Clippings.txt', 'backup.txt')
    with open_clippings_txt('My Clippings.txt', mode='w') as orig:
        for clip in keep:
            if clip.title not in seen:
                seen.add(clip.title)
                print('Keep {}'.format(clip.title.rstrip()))
            orig.write(clip.title)
            orig.write(clip.body)


parser = argparse.ArgumentParser(description='Kindle Clippings Cli')
subparsers = parser.add_subparsers(help='sub-command help')
parser_list = subparsers.add_parser('list', help='List titles.')
parser_list.set_defaults(func=on_list)
parser_archive = subparsers.add_parser('archive', help=
    'extract matching titles and append them to archive.txt')
parser_archive.add_argument('pattern', help=
    'archive titles matching the pattern')
parser_archive.set_defaults(func=on_archive)
parser_export = subparsers.add_parser('export', help=
    'export matching titles as markdown')
parser_export.add_argument('-f', '--format', help=
    'choose export format (default: markdown)', choices=['markdown', 'csv'],
    default='markdown')
parser_export.add_argument('pattern', help='export titles matching the pattern'
    )
parser_export.set_defaults(func=on_export)
args = parser.parse_args()
args.func(args)

#!/usr/bin/env python3
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

import argparse
import sys
import shutil
import textwrap
import csv
import re
from collections import namedtuple

Clipping = namedtuple('Clipping', 'title, body')


def open_clippings_txt(path, mode):
    newline = None
    if 'a' in mode or 'w' in mode:
        newline = '\r\n'
    return open(path, mode=mode, encoding='utf-8-sig', newline=newline)


def each_clipping():
    title = None
    body = []
    for line in open_clippings_txt('My Clippings.txt', mode='r'):
        if title is None:
            title = line
        else:
            body.append(line)

        if line == '==========\n':
            if title.strip() == '':
                print("EMPTY: " + ''.join(body))
            yield Clipping(title, ''.join(body))
            title = None
            body = []


def on_list(args):
    seen = set()
    for clip in each_clipping():
        if clip.title not in seen:
            seen.add(clip.title)
            print(clip.title.rstrip())


def on_export(args):
    titles = {}
    for clip in each_clipping():
        if args.pattern in clip.title:
            if clip.title not in titles:
                clippings = []
                titles[clip.title] = clippings
            else:
                clippings = titles[clip.title]

            clippings.append(clip)

    if args.format == 'csv':
        export_csv(titles)
    else:
        export_markdown(titles)


def export_markdown(titles):
    first_title = True
    for title, clippings in titles.items():
        if first_title:
            first_title = False
        else:
            print('')

        print("### {}".format(title))

        for clipping in clippings:
            body = clipping.body.split('\n', 2)[2]
            body = body.rsplit('\n', 2)[0]
            print('-   {}'.format(textwrap.indent(body, '    ')[4:]))
            print('')


META_RE = re.compile(
    r".*位置 (#\d+)(-\d+| )）?的(标注|笔记) \| 添加于 (\d+)年(\d+)月(\d+)日星期. (上午|下午)(\d+):(\d+):(\d+)")


def split_title_author(line):
    left_count = 1
    last_left_paren = line.rfind('(')
    while left_count < line.count(')', last_left_paren):
        left_count += 1
        last_left_paren = line.rfind('(', 0, last_left_paren)

    title = line[0:last_left_paren - 1]
    author = line[last_left_paren + 1:-2]

    return (title, author)


def parse_meta_time(meta_fields):
    year = int(meta_fields[3])
    month = int(meta_fields[4])
    day = int(meta_fields[5])

    ampm = meta_fields[6]
    hour = int(meta_fields[7])
    if hour == 12:
        hour = 0

    min = int(meta_fields[8])
    sec = int(meta_fields[9])

    if ampm == '下午':
        hour = hour + 12

    return "{:04}-{:02}-{:02} {:02}:{:02}:{:02}".format(year, month, day, hour, min, sec)


def export_csv(titles):
    w = csv.writer(sys.stdout, delimiter=',', quotechar='"')
    w.writerow(['Highlight', 'Title', 'Author', 'Note', 'Location', 'Date'])

    for title, clippings in titles.items():
        title, author = split_title_author(title)

        prev_highlight = None
        prev_end_loc = None
        prev_note = None

        for clipping in clippings:
            parts = clipping.body.split('\n', 2)
            meta = parts[0]
            body = parts[2]
            body = body.rsplit('\n', 2)[0]

            # 0 - start, 1 - end, 2 - type, 3,4,5 - date, 6 - am/pm, 7,8,9 - time
            meta_match = META_RE.match(meta)
            if meta_match is None:
                print("Unknown meta: {}".format(meta), file=sys.stderr)
            meta_fields = meta_match.groups()

            time = parse_meta_time(meta_fields)

            if meta_fields[2] == '标注':
                loc = meta_fields[0][1:]
                prev_end_loc = meta_fields[1][1:]

                if prev_note is not None:
                    if prev_note[0] == prev_end_loc:
                        w.writerow(
                            [body, title, author, prev_note[1], loc, time])
                        prev_note = None
                    else:
                        print("笔记位置和标注不匹配: #{} {}".format(
                            prev_note[0], prev_note[1]), file=sys.stderr)
                        sys.exit(1)
                else:
                    if prev_highlight is not None:
                        w.writerow(prev_highlight)

                    prev_highlight = [body, title, author, '', loc, time]
            else:
                loc = meta_fields[0][1:]
                if loc != prev_end_loc:
                    if prev_note is not None:
                        print("笔记位置和标注不匹配: #{} {}".format(
                            prev_note[0], prev_note[1]), file=sys.stderr)
                        sys.exit(1)
                    prev_note = [loc, body]
                else:
                    prev_highlight[3] = body

                if prev_highlight is not None:
                    w.writerow(prev_highlight)
                    prev_highlight = None
                    prev_end_loc = None

        if prev_highlight is not None:
            w.writerow(prev_highlight)


def on_archive(args):
    keep = []
    seen = set()
    with open_clippings_txt('archive.txt', mode='a') as archive:
        for clip in each_clipping():
            if args.pattern in clip.title:
                if clip.title not in seen:
                    seen.add(clip.title)
                    print("Archive {}".format(clip.title.rstrip()))
                archive.write(clip.title)
                archive.write(clip.body)
            else:
                keep.append(clip)

    shutil.copy('My Clippings.txt', 'backup.txt')
    with open_clippings_txt('My Clippings.txt', mode='w') as orig:
        for clip in keep:
            if clip.title not in seen:
                seen.add(clip.title)
                print("Keep {}".format(clip.title.rstrip()))
            orig.write(clip.title)
            orig.write(clip.body)


parser = argparse.ArgumentParser(description='Kindle Clippings Cli')
subparsers = parser.add_subparsers(help='sub-command help')

parser_list = subparsers.add_parser('list', help='List titles.')
parser_list.set_defaults(func=on_list)
parser_archive = subparsers.add_parser(
    'archive', help='extract matching titles and append them to archive.txt')
parser_archive.add_argument(
    'pattern', help='archive titles matching the pattern')
parser_archive.set_defaults(func=on_archive)
parser_export = subparsers.add_parser(
    'export', help='export matching titles as markdown')
parser_export.add_argument(
    '-f', '--format', help='choose export format (default: markdown)', choices=['markdown', 'csv'], default='markdown')
parser_export.add_argument(
    'pattern', help='export titles matching the pattern')
parser_export.set_defaults(func=on_export)

args = parser.parse_args()
args.func(args)
