#!/usr/bin/env python3
import os
import sys
import random
import string
import time
import shutil
import glob
import filecmp
import atexit
import Core.System as Sys
import simplebuild.cfg as cfg
import pathlib

def find_reflog(command):
    rl = os.path.join(cfg.dirs.testrefdir,'%s.log'%command)
    if not os.path.exists(rl):
        print("ERROR: No reference log found for command: %s"%command)
        print("Are you sure the command actually has a reference log?")
        sys.exit(1)
    return rl

def find_testdir(command,testbasedir):
    td=os.path.join(testbasedir,command)
    ecfile=os.path.join(td,'ec.txt')
    if not os.path.exists(ecfile):
        print("ERROR: No test seems to have been run for command: %s"%command)
        print("Did you just run \"sb -t\" as required?")
        sys.exit(1)
    with open(ecfile) as fh:
        if fh.read().strip()!='0':
            print("ERROR: Test did not seem to run with exitcode=0 for command: %s"%command)
            print("Make sure it runs ok before you even consider updating the reference log!")
            sys.exit(1)
    return td

def find_testoutput(command,testbasedir=None):
    td=find_testdir(command,testbasedir)
    output,refdiff = os.path.join(td,'output.log'),os.path.join(td,'refdiff.log')
    assert os.path.exists(output)
    if not os.path.exists(refdiff):
        print("ERROR: Reference log for command %s does not seem to need any updating"%command)
        print("Did you just run \"sb -t\" as required?")
        sys.exit(1)
    return output

#prepare (also checks all commands):
assert len(set(sys.argv[1:]))==len(sys.argv[1:]),"same argument specified more than once"

args = sys.argv[1:]

if '-h' in args or '--help' in args:
    print('Usage: ')
    print()
    print('%s [-h|--help] [TESTDIR] '%os.path.basename(sys.argv[0])+
          '[sb_some_testcommand1] [sb_some_testcommand2] [..]')
    print()
    sys.exit(0)


if args and os.path.exists(os.path.join(os.path.expanduser(args[0]),'Makefile')):
    testbasedir = os.path.abspath(os.path.expanduser(args[0]))
    args=args[1:]
else:
    testbasedir = cfg.dirs.testdir

if not args:
    args = [ p.split(os.sep)[-2] for p in
             glob.glob(os.path.join(testbasedir,'sb_*/refdiff.log'))
             if os.path.getsize(p) ]

commands=[]
for c in args:
    if not Sys.which(c):
        print('ERROR: unknown command: %s'%c)
        sys.exit(1)
    commands += [(c,find_testoutput(c,testbasedir),find_reflog(c))]

try:
    import readline
except ImportError:
    print("WARNING: could not load readline module - typing input will be slightly more annoying")
    readline=None

def show_diff(reflog,log,side_by_side=True,sbs_column_width=130):
    prompt = 'difference with respect to reference output of command %s (type Q when done viewing)'%cmd
    coldiff = '|colordiff' if Sys.which('colordiff') else ''
    Sys.system_throw('diff -a%s %s %s %s| less -R -c -f -K -L -n -S -P%s'%( f' -y -W{sbs_column_width}' if side_by_side else '',
                                                                            reflog,
                                                                            log,
                                                                            coldiff,
                                                                            Sys.quote(prompt) ) )
updated_files=[]
def update_file(log,reflog):
    global updated_files
    import simplebuild.cfg
    reflogname = os.path.basename(reflog)
    #We used to simply use os.path.realpath here to get to the log file in the
    #package, however these days that file might itself be a link elsewhere. So
    #we do the following search instead:

    pinfo = None
    for pkgname, _pinfo in simplebuild.cfg.pkgs.items():
        if not _pinfo['enabled']:
            continue
        if reflogname in _pinfo['reflogs']:
            pinfo = _pinfo
            break
    assert pinfo is not None
    pdir = pathlib.Path(pinfo['dirname'])
    target = None
    for pcandidate in pdir.glob('*/*.log'):
        if pcandidate.samefile(reflog):
            target = str(pcandidate)
            break
    assert target is not None
    target = str(target)

    assert any([target.startswith(str(d)) for d in cfg.dirs.pkgsearchpath])
    i=0
    while True:
        i += 1
        assert i<1e4
        backup='%s.reflogupdate%s.orig'%(target,'.%i'%i if i>1 else '')
        if not os.path.exists(backup):
            break
    shutil.copy2(target,backup)
    targetrp = os.path.realpath(target)
    Sys.rm_f(targetrp)
    shutil.copy2(log,targetrp)
    updated_files+=[os.path.relpath(target)]
    return os.path.relpath(target)

def final_summary():
    global updated_files
    print("Updated %i files%s"%(len(updated_files),':' if updated_files else ''))
    print('    %s'%(' '.join(updated_files)))

atexit.register(final_summary)

for cmd,log,reflog in commands:
    if filecmp.cmp(log,reflog,False):
        print("Ignoring command %s since the log seems to have been already updated (time to run \"sb -t\" again?)."%cmd)
        time.sleep(0.5)
        continue
    show_diff(reflog,log)
    randstr = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(5))
    print('Do you want to update the reference log for %s?'%cmd)
    print('     - Press ENTER to ignore')
    print('     - Type "log"/"l"+ENTER to see file comparison again (column width 130)')
    print('     - Type "log<value>"+ENTER to see file comparison again with custom colum-width')
    print('     - Type "changes"/"c"+ENTER to see just the changed lines')
    print('     - Type "%s" in reverse order (+ENTER) to actually perform the update IF YOU ARE SURE!'%randstr)
    while True:
        try:
            choice=input('  >> ').strip().lower()
        except (EOFError,KeyboardInterrupt):
            print("Aborted by user")
            sys.exit(0)
        if choice=='ignore' or not choice:
            break
        if choice.startswith('log') or choice=='l':
            _colwidth=None
            if len(choice)>3:
                try:
                    _colwidth=int(choice[3:])
                except ValueError:
                    _colwidth=None
                if _colwidth is None or _colwidth<=50 or _colwidth>=10000:
                    print('invalid column width (supply integer >50 and <10000)')
                    continue
            show_diff(reflog,log,sbs_column_width=(_colwidth or 130))
            continue
        elif choice in ('changes','c'):
            show_diff(reflog,log,side_by_side=False)
            continue
        elif choice==randstr[::-1]:
            if readline:
                readline.remove_history_item(readline.get_current_history_length()-1)
            updatedfile = update_file(log,reflog)
            print("Updated: %s"%updatedfile)
            time.sleep(0.5)
            break
        else:
            print("unknown choice!")
            continue
