#!/usr/bin/env python3

"""A simple blog generator with Pandoc as backend."""

# TODO: auto retouch: prompt for git commit amend after touching
# (display commit message to avoid amending the wrong commit)

# pylint: disable=too-many-lines

import argparse
from contextlib import contextmanager
import copy
import curses
import datetime
import email.utils
import fileinput
import io
import http.client
import http.server
import multiprocessing
import os
import re
import shutil
import signal
import string
import subprocess
import sys
import tempfile
import time
import urllib.parse

import blessed
import bs4
import colorama
import dateutil.parser
import dateutil.tz
import lxml.etree as ET

from bs4 import UnicodeDammit
from pprint import pprint
import requests

import toml

from rss import *

from utils import utils

from config.config import *

from generators import generators

from cli import cli


class PostSelector:

    def __init__(self, term, posts):
        self._term = term
        self.posts_per_page = term.height - 2
        self.pages = [posts[i:i+self.posts_per_page]
                      for i in range(0, len(posts), self.posts_per_page)]
        self.num_pages = len(self.pages)
        self.pagepos = 0
        self.postpos = 0
        self.inserting = False  # True if in the middle of inserting a post #, False otherwise
        term.enter_fullscreen()
        print(term.clear(), end="")
        sys.stdout.flush()
        self.selection = ""
        self.quit = False

        self.display_page()

    def _clear_to_eol(self):
        term = self._term
        print(term.clear_eol, end="")
        sys.stdout.flush()

    def _print_line(self, line, linenum, highlight=False):
        term = self._term
        width = term.width
        with term.location(0, linenum):
            if highlight:
                print(term.reverse(line[:width]), end="")
            else:
                print(line[:width], end="")
            self._clear_to_eol()

    def _print_post(self, page, pos, highlight=False):
        if pos >= len(page):
            # if position out of range, just clear the line
            self._print_line("", pos + 1, highlight)
        else:
            date, title, path = page[pos]
            line = "%3d: %s %s" % (pos, date.strftime("%m/%d/%y"), title)
            self._print_line(line, pos + 1, highlight)

    def display_page(self):
        term = self._term
        page = self.pages[self.pagepos]

        with term.hidden_cursor():
            topline = "  PAGE %d/%d  POST %d" % (self.pagepos + 1, self.num_pages, self.postpos)
            if self.inserting:
                topline += term.blink("_")
            self._print_line(topline, 0, highlight=True)

            for i in range(self.posts_per_page):
                self._print_post(page, i)
            # highlight selected post
            self._print_post(page, self.postpos, highlight=True)

            bottomline = "  Press h for help."
            self._print_line(bottomline, term.height - 1, highlight=True)

    def dispatch(self, key):
        term = self._term
        if key in string.digits:
            # insert
            if self.inserting:
                newpostpos = 10 * self.postpos + int(key)
                if newpostpos < len(self.pages[self.pagepos]):
                    self.postpos = newpostpos
            else:
                self.postpos = int(key)
            self.inserting = True
        elif key.name == "KEY_DELETE":
            self.postpos //= 10
            self.inserting = True
        else:
            self.inserting = False
            if key.name == "KEY_ENTER":
                self.selection = self.pages[self.pagepos][self.postpos][2]
            if key in {"q", "Q"}:
                self.quit = True
            elif key.name == "KEY_DOWN" or key in {"n", "N"}:
                if self.postpos + 1 < len(self.pages[self.pagepos]):
                    self.postpos += 1
            elif key.name == "KEY_UP" or key in {"p", "P"}:
                if self.postpos > 0:
                    self.postpos -= 1
            elif key.name == "KEY_RIGHT" or key in {".", ">"}:
                if self.pagepos + 1 < self.num_pages:
                    self.pagepos += 1
                    self.postpos = 0
            elif key.name == "KEY_LEFT" or key in {",", "<"}:
                if self.pagepos > 0:
                    self.pagepos -= 1
                    self.postpos = 0
            elif key in {"h", "H"}:
                print(term.clear_eol, end="")
                sys.stdout.flush()
                help_text_lines = [
                    "Next post:         n or <down>",
                    "Previous post:     p or <up>",
                    "Next page:         . or > or <right>",
                    "Previous page:     , or < or <left>",
                    "Select post:       <enter> or <return>",
                    "Select by number:  type number as shown (delete or backspace to edit)",
                    "Get help:          h",
                    "Quit program:      q",
                ]
                for i in range(term.height - 1):
                    self._print_line(help_text_lines[i] if i < len(help_text_lines) else "", i)
                bottomline = "  Press any key to continue."
                self._print_line(bottomline, term.height - 1, highlight=True)

                with term.raw():
                    term.inkey()

    def restore(self):
        term = self._term
        term.exit_fullscreen()
        print(term.clear(), end="")
        sys.stdout.flush()

    def select(self):
        term = self._term
        try:
            while True:
                with term.raw():
                    self.dispatch(term.inkey())
                if self.selection or self.quit:
                    break
                self.display_page()
        except Exception:
            raise
        finally:
            self.restore()

        return self.selection


def edit_existing_post(args):
    selector = PostSelector(blessed.Terminal(), utils.list_posts())
    selection = selector.select()
    if selection:
        print(selection)
        cli.edit_post_with_editor(selection)
    else:
        return 1


def main():
    """CLI interface."""
    description = "Simple blog generator in Python with Pandoc as backend."
    parser = argparse.ArgumentParser(description=description)
    subparsers = parser.add_subparsers(dest="action")
    subparsers.required = True

    parser_new_post = subparsers.add_parser(
        "new_post", aliases=["n", "new"],
        description="Create a new post with metadata pre-filled.")
    parser_new_post.add_argument("title", help="title of the new post")
    parser_new_post.set_defaults(func=cli.new_post_cli)

    parser_new_post = subparsers.add_parser(
        "touch", aliases=["t", "tou"],
        description="""Touch an existing post, i.e., update its
        timestamp to current time.  Why is this ever useful? Well, the
        timestamp filled in by new_post is the time of creation, but one
        might spend several hours after the creation of the file to
        finish the post. Sometimes the post is even created on one day
        and finished on another (say created at 11pm and finished at
        1am). Therefore, one may want to retouch the timestamp before
        publishing.""")
    parser_new_post.add_argument("filename",
                                 help="path or basename of the source file, "
                                 "e.g., 2015-05-05-new-blog-new-start.md")
    parser_new_post.set_defaults(func=cli.touch_cli)

    parser_generate = subparsers.add_parser(
        "generate", aliases=["g", "gen"],
        description="Generate new or changed objects.")
    parser_generate.set_defaults(func=cli.generate)

    parser_regenerate = subparsers.add_parser(
        "regenerate", aliases=["r", "regen"],
        description="Regenerate the entire blog afresh.")
    parser_regenerate.set_defaults(func=cli.regenerate)

    parser_new_post = subparsers.add_parser(
        "preview", aliases=["p", "pre"],
        description="Serve the blog locally and auto regenerate upon changes.")
    parser_new_post.set_defaults(func=cli.preview)

    parser_new_post = subparsers.add_parser(
        "deploy", aliases=["d", "dep"],
        description="Deploy build/ to origin/master without regenerating.")
    parser_new_post.set_defaults(func=cli.deploy)

    parser_new_post = subparsers.add_parser(
        "gen_deploy", aliases=["gd", "gendep"],
        description="Rebuild entire blog and deploy build/ to origin/master.")
    parser_new_post.set_defaults(func=cli.gen_deploy)

    parser_new_post = subparsers.add_parser(
        "edit", aliases=["e", "ed"],
        description="Bring up post selector to select post for editing.")
    parser_new_post.set_defaults(func=edit_existing_post)

    with utils.init_colorama():
        args = parser.parse_args()
        returncode = args.func(args)
    exit(returncode)


if __name__ == '__main__':
    main()