#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import subprocess
import glob
import time
import logging
import argparse
import json
import shutil
from datetime import datetime
from termcolor import colored, cprint

#
# Customized global variables
#
TIME_FORMAT = '%Y-%m-%d %H:%M:%S %a'
TEMPLATE_PATH = './template'
NOTE_JSON = '.note.json'
PREPEND_TIME = False

if 'EDITOR' in os.environ.keys() and os.environ['EDITOR']:
    EDITOR = os.environ["EDITOR"]
else:
    EDITOR = 'vim'


def configLog(debug):
    "Configure logging"
    logfile = os.path.basename(__file__) + \
        '.debug.log' if debug is True else None
    loglevel = logging.DEBUG if logfile is not None else None
    logging.basicConfig(
        format='%(asctime)s [%(levelname)8s] %(message)s', filename=logfile, level=loglevel)


def getmtime(fname):
    "Get last modify time and format it"
    return datetime.fromtimestamp(os.path.getmtime(fname + '.md')).strftime(TIME_FORMAT)


def findFiles(pattern):
    "Get all files which mataches pattern"
    logging.debug('  >> findFiles')

    files = glob.glob(pattern, recursive=True)
    files.sort(key=os.path.getmtime, reverse=True)
    logging.debug('files:\n' + str(files))

    logging.debug('  << findFiles')
    return files


def writeFile(fname, line, mode):
    "Append line at the end of file or Prepend line at the beginning of file"
    logging.debug('  >> writeFile')
    logging.debug('line: ' + str(line))

    try:
        f = open(fname, 'r+')
    except IOError:
        f = open(fname, 'w+')

    content = f.read()
    f.seek(0, 0)
    if mode == 'prepend':
        f.write(line.rstrip('\r\n') + '\n' + content)
    else:
        f.write(content + '\n' + line.rstrip('\r\n'))
    f.close()
    logging.debug('  << writeFile')


def updateJSON(jfile, folder):
    "Update local file id namd file name mapping"
    logging.debug('  >> updateJSON')
    logging.debug('folder:' + str(folder))

    files = findFiles(folder + '/**/*.md')
    logging.debug('files: ' + str(files))

    i = 1
    data = {}
    for fn in files:
        if os.path.dirname(fn).startswith('./archive') is not True:
            data[i] = fn.replace('./', '').replace('.md', '')
            i += 1
    logging.debug('update json: ' + str(data))
    with open(jfile, 'w') as f:
        json.dump(data, f, ensure_ascii=False)
    logging.debug('  << updateJSON')


def showNotes(folder='.'):
    "Display all notes in path"
    logging.debug('  >> showNotes')

    # Initial local json file
    updateJSON(NOTE_JSON, folder)

    with open(NOTE_JSON, 'r') as f:
        jfile = json.load(f)
        for k in sorted(jfile.keys(), key=int, reverse=True):
            v = jfile[k]
            if 'FILTER' in globals() and FILTER != '':
                if str(FILTER).lower() in str(v).lower():
                    index = str(v).lower().index(str(FILTER).lower())
                    before = v[:index]
                    match = v[index:index+len(FILTER)]
                    after = v[index+len(FILTER):]
                    print(str(getmtime(v)), '|', colored(k, 'yellow'), '|', colored(
                        before, 'green') + colored(match, 'red') + colored(after, 'green'))
            else:
                print(str(getmtime(v)), '|', colored(
                    k, 'yellow'), '|', colored(v, 'green'))
    logging.debug('  << showNotes')


def getTemplate(fname):
    "Get template content"
    logging.debug('  << getTemplate')

    if fname is None:
        return None

    fname = TEMPLATE_PATH + '/' + ''.join(fname)
    if os.path.isfile(fname):
        f = open(fname)
        content = f.read()
        f.close()
        logging.debug('  >> getTemplate')
        return content
    else:
        print('Template "' + fname + '" not found')
        logging.debug('  >> getTemplate')
        sys.exit(1)


def moveFiletoFolder(fname, folder, extension=True):
    "Move file to folder with/without keeping file extension"
    logging.debug('  >> moveFiletoFolder')

    os.makedirs(folder, exist_ok=True)

    if os.path.isfile(fname):
        if extension is True:
            shutil.move(fname, folder + '/' + os.path.basename(fname))
        else:
            shutil.move(fname, folder + '/' +
                        os.path.splitext(os.path.basename(fname))[0])
    else:
        print(colored('File "' + fname + '" not found'))
    logging.debug('  << moveFiletoFolder')


def getFileName(fname):
    "Return file name with extension md"
    logging.debug('  >> getFileName')

    fname = './' + ' '.join(fname)
    if '.md' not in ''.join(fname):
        fname += '.md'

    logging.debug('file name: ' + str(fname))
    logging.debug('  << getFileName')

    return fname


def editFile(fname, tmpl=None):
    "Edit file"
    logging.debug('  >> editFile')
    logging.debug('file: ' + str(fname))
    logging.debug('tmpl: ' + str(tmpl))

    # create folder if it does not exist yet
    logging.debug(os.path.dirname(fname))
    os.makedirs(os.path.dirname(fname), exist_ok=True)

    cmd = EDITOR + ' "+normal G$" ' + fname.replace(' ', '\ ')
    timeStr = time.strftime('*' + TIME_FORMAT + '*')

    if tmpl is not None:
        writeFile(fname, tmpl, 'append')

    if PREPEND_TIME:
        writeFile(fname, timeStr, 'prepend')
    subprocess.call(cmd, shell=True)
    logging.debug('  << editFile')


def main():
    #
    # Setup parameter
    #   run script with -d, active debug mode (log file will be created)
    #
    parser = argparse.ArgumentParser()
    parser.add_argument('text', nargs='*', help='input text')
    parser.add_argument('-t', '--template', nargs=1,
                        dest="template", help='use template')
    parser.add_argument('-i', '--id', nargs=1, dest="id",
                        help='use note id to select a specific note')
    parser.add_argument('-f', '--filter', nargs=1,
                        dest="filter", help='filter display result')
    parser.add_argument('-rm', '--remove', action='store_true',
                        dest="remove", help='remove a note')
    parser.add_argument('-a', '--archive', action='store_true', dest="archive",
                        help='move note to archive, without parameter to show all archived notes')
    parser.add_argument('-d', '--debug',  action='store_true',
                        dest="debug", help='active debug log')

    args = parser.parse_args()

    # Config logging
    configLog(args.debug)
    logging.debug('args:\n' + str(args))
    logging.debug('len argv: ' + str(len(sys.argv)))

    # Apply filter
    if args.filter:
        global FILTER
        FILTER = str(args.filter[0])
        logging.debug('FILTER: ' + FILTER)

    # Get file name by id
    if args.id:
        noteid = str(args.id[0])
        logging.debug('ID: ' + noteid)
        with open(NOTE_JSON, 'r') as f:
            for k, v in json.load(f).items():
                if str(k) == noteid:
                    args.text = [v]
        if len(args.text) == 0:
            cprint('Cannot find a note related to id "' + noteid + '"', 'red')
        logging.debug('args.text: ' + str(args.text))

    # Create/Modify/Remove/Archive note
    # Or else, show all notes
    if len(args.text) != 0:
        # Get filename
        fname = getFileName(args.text)

        # Remove note
        if args.remove is True:
            moveFiletoFolder(fname, 'trash', False)
            sys.exit(1)

        # Archive note
        if args.archive is True:
            moveFiletoFolder(fname, 'archive')
            sys.exit(1)

        # Create/Modify notes
        editFile(fname, getTemplate(args.template))

    else:
        if args.archive is True:
            # Show archived notes
            print('All archived notes:')
            showNotes('archive')

        else:
            # Show all notes
            showNotes()


if __name__ == '__main__':
    main()
