#!/usr/bin/python3
# author: leadingsuspect(npub1xl56l7zr2464c2h3vj34nzqamwcf7eatvy2x47u87pkyuh4csrcq45zcuk)
# usage: lookup [-h] [-b BEGIN] [-x MAX] [--version] [-q | -v] infile [outfile] 'searchstring as multiple regexpatterns'
# ===========================================================

import argparse
import sys
import logging
import re

# import shutil
from io import UnsupportedOperation
from builtins import FileNotFoundError, FileExistsError, PermissionError

from collections import defaultdict

def priorities():
    return ([],[],[],[])

result = defaultdict(priorities)


# Errorcodes not yet used as return codes
E_BADARGS = 85
E_NOFILE = 86
E_NOPERM = 2
E_FILEEXISTS = 3
E_UNSUPPORTEDOP = 4
E_OTHERERR = 99

BLUE = '94m' # ansi light blue

def colored_text(color, text):
    colored_text = f"\033[{color}{text}\033[00m"
    return colored_text

def highlight(text):
    return colored_text(BLUE, text)


# logging.warning('I print to stderr by default')
# logging.info('For this you must change the level and add a handler.')


# ===========================================================
# Parsing Script Arguments
parser = argparse.ArgumentParser(
    description='This script allows you to do multiple Pearl-Regex searches given as one big sentence with a dictionary file')
parser.add_argument(
    'infile', help='path/to/inputfile for the operation (default: stdin)',
    type=str, default=sys.stdin)
parser.add_argument(
    'outfile', help='path/to/outputfile for the operation (default: stdout)',
    nargs='?', type=str, default=sys.stdout)
parser.add_argument(
    'searchstring', help='Multiple Words that can be Perl-Regex-formed to lookup in a dictionary',
    type=str)
parser.add_argument(
    '-b', '--begin', help='b - begin from line b of the file (default= 83)',
    type=int, default=83)
parser.add_argument(
    '-x', '--max', help='i - max number of items shown per pattern (default: 3)',
    type=int, default=3)
parser.add_argument(
    '--version', action='version', version='%(prog)s 2.0')
group = parser.add_mutually_exclusive_group()
# group.add_argument("-v", "--verbose", action="store_true")
group.add_argument(
    "-q", "--quiet", help="opposite of verbosity",
    action="store_true")
group.add_argument(
    '-v', '--verbosity', help="increase output verbosity",
    action="count", default=0)

args = parser.parse_args()

# ===========================================================
# Check if file argument is a file and set processingFile if true
# if os.path.isfile(args.infile):
#     processingFile = args.infile
# else:
#     processingFile = None

if args.infile:
    processingFile = args.infile
else:
    processingFile = ''
# ===========================================================
# Check if searchstring argument holds valid Perl Regex Patterns and set searchWords if true
if args.searchstring:
    try:
        delimiters = ' |,|-'
        # patterns = list(args.searchstring.split(delimiter))
        patterns = list(re.split(delimiters, args.searchstring))
    except:
        print("could not split searchstring")
else:
    print ("No search string provided")
    sys.exit(E_BADARGS)

# Check if Lines range is valid
if args.begin:
    try:
        begin = int(args.begin)
    except:
        print("Plz provide line beginning: -bBeginlinenumber")
else:
    print ("No begin linenumber provided")
    sys.exit(E_BADARGS)


# Check if max items per Searchpattern is valid
if args.max:
    try:
        maxitems = int(args.max)
    except:
        print("Plz provide max number of items per searchstring: -xMaxItemnumber")
else:
    print ("No max itemnumber provided")
    sys.exit(E_BADARGS)
# ===========================================================
# The process itself


def openfile(filename, modus):
    # use it within a exception handler
    with open(filename, modus, encoding="latin-1") as file:
        lines = file.readlines()

        pairmatches = []
        exactmatches = []


        if args.verbosity >= 1:
            # print("processing open file {}".format(filename))
            if args.verbosity >=2:
                print("There are %d lines in the dictionary %s" % (len(lines), filename))
                print(f"beginnig at line -b{begin}")
                print(f"max items per Pattern: -i{maxitems}.")
            print(f"searching... {patterns}")

        # for line in lines[83:]:
        for line in lines[begin:]:
            if (len(patterns)>1) and (re.search(r'' + args.searchstring, line)):
                exactmatches.append(line)

            else:
                for i in range(len(patterns)):

                    p = r'' + patterns[i]
                    if i>=1:
                        if re.search((patterns[i-1] + r' ' + p),line):
                            pairmatches.insert(0, highlight((patterns[i-1] + ' ' + p + ': ')) + line)
                            # result[p].append(line)
                            result[p][0].append(line)
                            continue

                    if re.search(r'(^|[^a-zA-Z\'])' + p + r'($|[^a-zA-Z\'])', line):
                        # result[p].append(line)
                        result[p][1].append(line)
                        continue


                    if re.search(r'([a-zA-Z\'-]' + p + r'[^a-zA-Z\']|[^a-zA-Z\']' + p + '[a-zA-Z\'-])', line):
                        result[p][2].append(line)
                        continue

                    if re.search(p, line):
                        result[p][3].append(line)


        if pairmatches or result or exactmatches:
            if result:
                for p in patterns:
                    print(highlight((p + ': ')))
                    matches_per_priority = result.get(p)
                    if matches_per_priority:
                        print(''.join([match for matches in matches_per_priority for match in matches][:maxitems]))

            if pairmatches:
                print("paired matches: ")
                print(''.join(pairmatches[:maxitems]))
            if exactmatches:
                print(highlight(args.searchstring + ': '))
                print(''.join(exactmatches[:maxitems]))
        else: print("no matches found")


def process(filename, modus):
    try:
        openfile(filename, modus)
    except FileNotFoundError:
        logging.warning("I did not find filename '%s'!" % filename)
        sys.exit(E_NOFILE)
    except PermissionError:
        logging.warning(
            "Permission denied to open '%s' in '%s' mode." % (filename, modus))
        sys.exit(E_NOPERM)
    except FileExistsError:
        logging.warning("File '%s' already exists" % filename)
        sys.exit(E_FILEEXISTS)
    except UnsupportedOperation as UsO:
        logging.warning(
            "UnsupportedOperation: %s in '%s' mode" % (str(UsO), modus))
        sys.exit(E_UNSUPPORTEDOP)
    except IOError:
        logging.warning("I/O Error occurred: %s" % str(IOError))
        sys.exit(E_OTHERERR)
    # finally:



# ===========================================================
# verbosity level influences processing messages...
if args.verbosity >= 2:
    print("verbosity level is: %s" % args.verbosity)
    # Process file if set
    if processingFile:
        print("'%s' is a file and will be processed" % args.infile)
        process(processingFile, 'r')
    else:
        print("'%s' is not a file to be processed" % args.infile)
elif args.verbosity >= 1:
    # print("verbose")
    # Process file if set
    if processingFile:
        print("Processing '%s'" % args.infile)
        process(processingFile, 'r')
    else:
        print("'%s' no file" % args.infile)
else:
    # print("processing in silence")
    if processingFile:
        process(processingFile, 'r')
