#!/usr/bin/env python3

"""

A very simply utility to find all files that contains given string(s).

Very useful when working with huge source code base.

http://yurichev.com/news/20200228_megagrep/

History:
24-Mar-2021: new option: "--limit=n"

3-Oct-2021: print filenames in sorted order

27-Nov-2021: a bug fixed. "--no-ansi" option didn't work

-- dennis(@)yurichev.com

"""

import os, sys

ignore_case=False
ansi=True

# see also: http://www.lihaoyi.com/post/BuildyourownCommandLinewithANSIescapecodes.html
ANSI_RED=u"\u001b[31m"
ANSI_BRIGHT_RED=u"\u001b[31;1m"
ANSI_BRIGHT_GREEN=u"\u001b[32;1m"
ANSI_RESET=u"\u001b[0m"

def load_file (fname):
    f=open(fname, "r")
    lines=f.readlines()
    f.close()
    return lines

def my_find (s, substring):
    if ignore_case:
        return s.lower().find(substring.lower())
    else:
        return s.find(substring)

def all_strings_present_in_array(strings, lines):
    found=[False]*len(strings)
    for l in lines:
        for s in range(len(strings)):
            if my_find(l, strings[s])!=-1:
                found[s]=True
        if all(found):
            return True
    return False

def highlight_string (string, substring):
    if my_find(string, substring)==-1:
        return string
    idx=my_find(string, substring)
    # recursively ...
    if ansi:
        return string[0:idx] + ANSI_BRIGHT_RED + string[idx:idx+len(substring)] + ANSI_RESET \
            + highlight_string (string[idx+len(substring):], substring)
    else:
        return string[0:idx] + string[idx:idx+len(substring)] \
            + highlight_string (string[idx+len(substring):], substring)
        #return string[0:idx] + "[" + string[idx:idx+len(substring)] + "]" \
        #    + highlight_string (string[idx+len(substring):], substring)

def print_highlighted (string, substrings):
    for substring in substrings:
        string=highlight_string (string, substring)
    print (string)

def any_substring_in_string (string, substrings):
    for substring in substrings:
        if my_find (string, substring)!=-1:
            return True
    return False

def show_strings_in_array(substrings, lines, limit):
    cur=0
    for l in lines:
        if cur>=limit:
            print("[... skipped ...]")
            return
        if any_substring_in_string (l, substrings):
            print_highlighted(l.rstrip(), substrings)
            cur=cur+1

if len(sys.argv)==1:
    print ("Usage:")
    print ("[python[.exe]] [./]megagrep.py [-i] [--no-ansi] [--limit=n] start_dir string1 [string2] [string3] ...")
    print ("-i         ignore case, default: case sensitive")
    print ("--no-ansi  don't use ANSI codes, default: use")
    print ("--limit=n  limit matched lines to be printed, default is 10")
    exit(0)

idx=1
limit=10

while sys.argv[idx].startswith("-"):
    if sys.argv[idx]=='-i':
        ignore_case=True
        idx=idx+1
    elif sys.argv[idx]=='--no-ansi':
        ansi=False
        idx=idx+1
    elif sys.argv[idx].startswith('--limit='):
        limit=int(sys.argv[idx][len('--limit='):])
        print ("Setting limit to", limit)
        idx=idx+1
    else:
        print ("unknown option: ", sys.argv[idx])
        exit(0)

start_dir=sys.argv[idx]
idx=idx+1

strings=sys.argv[idx:]
if len(strings)==0:
    print ("error: no strings set")
    exit(0)

print ("searching for strings:", strings)

fnames_to_process=[]

for root, dirnames, filenames in os.walk(start_dir):
    for filename in sorted(filenames):
        fname=os.path.join(root, filename)
        fnames_to_process.append(fname)

for fname in sorted(fnames_to_process):
    try:
        lines=load_file (fname)
        if all_strings_present_in_array (strings, lines):
            if ansi:
                print (ANSI_BRIGHT_GREEN + fname + ANSI_RESET)
            else:
                print (fname)
            show_strings_in_array(strings, lines, limit)
    except UnicodeDecodeError:
        # the file is not in utf-8 format, ignore it
        pass
    except FileNotFoundError:
        # some other error we want to sweep under the carpet
        pass

