#!/usr/bin/python3
"""
TODO:
    If g/data file now has default value but before
    had non default, it doesn't get written because it's
    now default, but the old non-default file didn't get erased,
    giving entirely wrong information.

    Figure out what to do when a changed type leaves g/data files
    that shouldn't be there, according to the type. They could be
    erased, but if the type change was an accident, you lose data.

    Perhaps some sort of a lint operation using props?
    """
import sys
import os
import errno
from UmenuConf import Conf
from UmenuMetadata import Metadata

USEICONS = False

def is_lc_single_char(ltr):
    if len(ltr) == 1 and ltr.islower():
        return True
    else:
        return False



def mkdir_sure(path):
    try:
        os.mkdir(path)
    except OSError as exc:
        if exc.errno == errno.EEXIST and os.path.isdir(path):
            pass
        else:
            raise

def snag(datadir, key):
    fname = datadir + '/' + key
    try:
        f=open(fname, 'r')
    except:
        print('FATAL ERROR, could not open {} for read, aborting!'.format(fname))
        sys.exit(1)
    rtrn = f.readline().strip()
    f.close()
    return rtrn


### WARNING: mnustring2dir PREPENDS A SLASH: BE AWARE!
def mnustring2dir(mnustring):
    ss=0
    ssmax = len(mnustring)
    dirr = ''
    while ss < ssmax:
        ltr = mnustring[ss]
        dirr += ('/' + ltr)
        ss += 1
    return(dirr)

def deduce_thisdir(info):
    info['thisdir'] = info['umenu_menudir']  + mnustring2dir(info['menustring'])
    return(info['thisdir'])


def get_existing_data(mypath):
    datapath = mypath + '/data'
    data = {}
    for fname in os.listdir(datapath):
        f = open(datapath + '/' + fname, 'r')
        val = f.readline().strip()
        f.close()
        data[fname] = val
    return data


def get_menu_type(info):
    info['mnutype'] = snag(info['thisdatadir'], 'type')
    if info['mnutype'] != 'm':
        s='FATAL ERROR: Menu \'{}\' (type {}) is not a menu, aborting.'
        s=s.format(info['menustring'], info['mnutype'])
        print(s)
        sys.exit(1)


def print_menu_header(info):
    print('\nMenu string "{}", ({}) '.format(info['menustring'], info['mnutitle']))
    print('contains the following directories:\n')

def print_and_store_contained_directories(info):
    info['existing']=[]
    for fil in os.listdir(info['thisdir']):
        info['existing'].append(fil)
        print(fil)


def get_new_menu_letter_from_user(info):
    done=False
    while not done:
        print('\nInput menu letter of choice you want to create=>', end='', flush=True)
        ltr = sys.stdin.readline().strip().lower()
        print('You chose {}.'.format(ltr))
        done = False
        if not is_lc_single_char(ltr):
            print('Sorry, menu letter must be lower case single letter, a-z')
        else:
            done = True
    return ltr


def get_new_type_from_user(info):
    try:
        itmtype = info['nuitemdata']['type']
    except:
        itmtype = ''
    else:
        print('\n=============================================')
        print('This is an edit of an existing item,')
        s='so the original item type ({}) can\'t be changed.'
        print(s.format(itmtype))
        print('If you really need to change the item type,')
        print('do it from the command prompt.')
        print('=============================================\n')
        return itmtype

    if not itmtype in ['m', 'c', 'r']:
        itmtype = ''
    done=False
    print('New item\'s type is either m (menu), c (command), or r (return).')
    while not done:
        s='Input new item\'s type [{}] (m, c or r)=>'
        s=s.format(itmtype)
        print('Input new item\'s type (m, c or r)=>', end='', flush=True)
        itmtype = sys.stdin.readline().strip().lower()
        if itmtype in ['m', 'c', 'r']:
            info['type'] = itmtype
            done = True
        else:
            print('Invalid input: Type must be single letter either m, c or r!')
    info['nuitemdata']['type'] = itmtype
    return(itmtype)



def get_data_from_disk(info, datapath):
    data = {}
    if os.path.isdir(datapath):
        for fname in os.listdir(datapath):
            val = snag(datapath, fname)
            data[fname] = val
    return data





def get_data_from_user(info, props):

    ### HEAVILY CHECK INCOMING DATA FOR EXISTANCE ###
    if not info:
        raise AssertionError
    if not props:
        raise AssertionError
    if not 'nuitemdata' in info:
        raise AssertionError
    if not 'type' in info['nuitemdata']:
        raise AssertionError
    if not info['nuitemdata']['type'] in ['m', 'c', 'r']:
        raise AssertionError

    if   not   'menutitle'     in   props    :   raise AssertionError
    if   not   'choicetitle'   in   props    :   raise AssertionError
    if   not   'cmd'           in   props    :   raise AssertionError
    if   not   'env'           in   props    :   raise AssertionError
    if   not   'path'          in   props    :   raise AssertionError
    if   not   'dir'           in   props    :   raise AssertionError
    if   not   'bkground'      in   props    :   raise AssertionError
    if   not   'stop'          in   props    :   raise AssertionError

    if info['useicons']:
        if not 'icon'          in   props    :   raise AssertionError


    ### GET EXISTING DATA IF APPROPRIATE
    #d = {'type': info['nuitemdata']['type']}
    d = info['nuitemdata']


    ### USER ACQUISITION LOOP
    done = False
    while not done: ### Don't quit til data OK and user says it suits him/her
        valid = True    ### Assume true, any mistake turns it false.
        for key in sorted(props.keys()): ### Do each key if necessary
            print('')
            if props[key]['needed'][info['nuitemdata']['type']] == 'n':  ### Don't query for keys not needed by this item type
                continue
            if key == 'type':      ### You got 'type' earlier, don't query for it again.
                continue


            ### IF YOU GOT THIS FAR, THIS CHOICE TYPE CAN USE THIS DATA TYPE AND YOU'RE NOT RE-EVALUATING KEY 'type'.
            if key not in d:
                d[key] =  props[key]['default'][info['nuitemdata']['type']]
                if d[key] == '^^^':
                    d[key] == ''

            ### BEGIN FORMING THE PROMPT
            s='Input |{}| please [{}]'

            ### Logic for lowercase and booleans
            if props[key]['isboolean']:
                s += '(t or f), '
            else:
                s += ', '

            if props[key]['isboolean']:
                s += '=>'
            elif props[key]['needed'][info['nuitemdata']['type']] == 'y':
                s += '(required)=>'
            else:
                s += '^^^ for none=>'
            s=s.format(key, d[key])
            print(s, end='', flush=True)
            userinput = sys.stdin.readline().strip()
            if userinput == '^^^':
                d[key] = ''
                userinput = ''
            if userinput == '':
                userinput = d[key]

            if props[key]['lowercase']:
                userinput = userinput.lower()
            if props[key]['isboolean']:
                if userinput in ['t', 'f']:
                    d[key] = userinput
                else:
                    valid = False
                    print('{} must be t or f, go around again.'.format(key))
            elif props[key]['needed'][info['nuitemdata']['type']] == 'y':
                if userinput != '':
                    d[key] = userinput
                else:
                    valid = False
                    print('{} is a required field, go around again.'.format(key))
            else:
                d[key] = userinput

        if valid:
            print('Done inputting data for this item (y or n)?=>', end='', flush=True)
            userinput = sys.stdin.readline().lower().strip()
            if userinput == 'y':
                done = True
    return d





def write_info_to_disk(info, props):
    dir2write = info['nuitemdir']
    for key in sorted(info['nuitemdata'].keys()):
        val = info['nuitemdata'][key]
        file2write = dir2write + '/data/' + key
        if val != '^^^' and key != 'type' and val == props[key]['default'][info['nuitemdata']['type']]:
            try:
                os.remove(file2write)
            except OSError as e:
                if e.errno != errno.ENOENT:
                    print('\n================================================')
                    print('ERROR: write_info_to_disk() failed to delete')
                    print('{}, aborting.'.format(file2write))
                    print('================================================\n')
                    raise
        else:
            file2write = dir2write + '/data/' + key
            f=open(file2write, 'w')
            print(str.strip(val), file=f)
            f.close()
            #s='Writing {:<30} to {:<30}.' 
            #print(s.format(val, file2write))


def main():
    info = {'useicons': USEICONS}
    info['menustring'] = sys.argv[1]
    cnf = Conf(sys.argv)
    #info['umenu_menudir'] = sys.argv[1]
    info['umenu_menudir'] = cnf.cnf['menu']
    info['thisdir'] = deduce_thisdir(info)
    info['thisdatadir'] = info['thisdir'] + '/data'
    info['mnutype'] = get_menu_type(info)
    info['mnutitle'] = snag(info['thisdatadir'], 'menutitle')

    print_menu_header(info)
    print_and_store_contained_directories(info)

    ltr = get_new_menu_letter_from_user(info)
    if ltr in info['existing']:
        info['crud'] = 'u'
    else:
        info['crud'] = 'c'

    info['nuitemltr'] = ltr
    info['nuitemstring'] = info['menustring'] + ltr
    info['nuitemdir'] = info['thisdir'] + '/' + ltr

    mkdir_sure(info['nuitemdir'])
    mkdir_sure(info['nuitemdir'] + '/data')

    info['nuitemdata'] = get_data_from_disk(info, info['nuitemdir'] + '/data')

    info['nuitemdata']['type'] = get_new_type_from_user(info)

    md = Metadata(cnf.cnf['icons'])
    info['nuitemdata'] = get_data_from_user(info, md.props)

    write_info_to_disk(info, md.props)


if __name__ == '__main__':
    main()
