#!/usr/bin/env python3

import re
import os
import json
import numpy as np
import multiprocessing as mp
import matplotlib  
#matplotlib.use('TkAgg')   
import matplotlib.pyplot as plt
import glob
import measurement
import datetime
import webbrowser
from pathlib import Path
from markdown import markdown

# name of the program
PROGRAM_NAME = "PIGOR v1.2"

# functions that the user can utilize
USER_FUNCTIONS = dict()

# global config dict
configuration = dict()

# global vars for init() default
configuration_fallback = {
    'PIGOR_ROOT'            :   Path(os.path.dirname(os.path.abspath(__file__))),
    'PIGOR_ROOT_RECURSIVE'  :   True,
    'FILE_EXTENTION'        :   '.dat',
    'IMAGE_FORMAT'          :   '.png',
    'CREATE_HTML'           :   True,
    'CREATE_MD'             :   True,
    'EXPORT_THEME'          :   'github',
    'EXPORT_FIT_FUNCTIONS'  :   True
}


# decorator for registering functions
def show_user(func):
    """Register a function to be displayed to the user as an option"""

    global USER_FUNCTIONS

    try:
        key = re.search(r'\[(.+?)\]', func.__doc__).group(1)
    except AttributeError as e:
        key = func.__name__
        print(e)

    USER_FUNCTIONS[key] = func
    return func

def bool2yn(b):
    """Converts a boolean to yes or no with the mapping: y = True, n = False."""
    return 'y' if b else 'n'

def yn2bool(s):
    """Converts yes and no to True and False."""
    return True if s.casefold().startswith('y') else False

def is_valid_theme(theme):
    """Checks if this theme exists."""
    themes = sorted(Path('./markdown_themes').glob('*.css'))
    themes = [t.stem for t in themes]
    if theme in themes:
        return True
    else:
        return False

def list_themes():
    """Returns a list of all themes available."""
    themes = sorted(Path('./markdown_themes').glob('*.css'))
    return [t.stem for t in themes]

@show_user
def init(create_new_config_file=True):
    """This function will read the config file and initialize some variables
    accordingly. This function can be used by the command [i].

    Available options:

    - root directory where PIGOR will start to look for measurement files
    - Should PIGOR look for files to analyse recursively?
    - Which file extention do the measurement files posess?
    - What plot output format should PIGOR use?
    - Should PIGOR automatically create an html file?
    - Should PIGOR automatically create a md file?
    - Should PIGOR create a txt file containing all used fit functions for the use in Mathematica?
    
    .. note:: If no config file can be found, it will create one.
    
    """
    
    # make configuration editable
    global configuration


    # try to read the configuration
    try:
        with open('pigor-config.json', 'r', encoding='utf-8') as f:
            c = json.load(f)
            # make it a path object
            c['PIGOR_ROOT'] = Path(c['PIGOR_ROOT']).resolve()

            # check if new item has been added to configuration
            if len(c) != len(configuration_fallback):
                create_new_config_file = True
                for k, v in configuration_fallback:
                    if k not in c:
                        c[k] = v
    except Exception:
        print('Could not read configuration file. Creating a new one now:\n')
        c = configuration_fallback
        create_new_config_file = True
        

    # questions to aks the user
    questions = {
        'PIGOR_ROOT'            :   (f'Where should {PROGRAM_NAME} start looking for measurement files? [{c["PIGOR_ROOT"]}]', 'path'),
        'PIGOR_ROOT_RECURSIVE'  :   (f'Should {PROGRAM_NAME} look for files recursively? (y/n) [{bool2yn(c["PIGOR_ROOT_RECURSIVE"])}]', 'bool'),
        'FILE_EXTENTION'        :   (f'Which file extention should {PROGRAM_NAME} look for? (string) [{c["FILE_EXTENTION"]}]', 'file-extention-data'),
        'IMAGE_FORMAT'          :   (f'What image format should the plots have? (.png,.svg,.eps,.pdf) [{c["IMAGE_FORMAT"]}]', 'file-extention-image'),
        'CREATE_HTML'           :   (f'Should {PROGRAM_NAME} create an HTML file automatically after analysis? (y/n) [{bool2yn(c["CREATE_HTML"])}]', 'bool'),
        'CREATE_MD'             :   (f'Should {PROGRAM_NAME} create a Markdown file automatically after analysis? (y/n) [{bool2yn(c["CREATE_MD"])}]', 'bool'),
        'EXPORT_THEME'          :   (f'Please choose a theme for the exported html files: ({list_themes()}) [{c["EXPORT_THEME"]}]', is_valid_theme),
        'EXPORT_FIT_FUNCTIONS'  :   (f'Should {PROGRAM_NAME} create a txt file containing all used fit functions for later use in Mathematica? (y/n) [{bool2yn(c["EXPORT_FIT_FUNCTIONS"])}]', 'bool')
    }

    for k, q in questions.items():
        if not k in c.keys() or create_new_config_file:
            # ask user a question
            print(q[0])
            user_input = input()
            # if a Path object must be read
            if q[1] == 'path':
                while True:
                    try:
                        if user_input == '':
                            value = None
                            break
                        value = Path(user_input)
                        if value.exists():
                            break
                        else:
                            print('The path you entered does not exist. Please try again.')
                            user_input = input()
                    except Exception:
                        user_input = input('Could not find the path, please input another one:\n')
            
            # if treating booleans
            elif q[1] == 'bool':
                if not user_input == '':
                    value = yn2bool(user_input)
                else:
                    value = None
            
            # if answer is a file extention
            elif not callable(q[1]) and q[1].startswith('file-extention'):
                if user_input == '' and q[1] == 'file-extention-image':
                    value = None
                elif user_input == '' and q[1] == 'file-extention-data':
                    value = None
                else:
                    # adding . to file extention if not given by the user
                    value = user_input.casefold() if user_input.startswith('.') else '.' + user_input.casefold()
            elif callable(q[1]):
                while True:
                    if user_input == '':
                        value = None
                        break
                    if q[1](user_input) == True:
                        value = user_input
                        break
                    else:
                        user_input = input('Sorry, PIGOR does not know this input. Try again.')
            else:
                value = user_input

            # renew old configuration value
            if value != None:
                c[k] = value



    # write config to global var
    configuration = c.copy()

    # write configuration into file
    with open('pigor-config.json', 'w') as f:
        if isinstance(c['PIGOR_ROOT'], Path):
            c['PIGOR_ROOT'] = str(c['PIGOR_ROOT'].resolve())
        json.dump(c, f, ensure_ascii=False, indent=4)


def print_header(text):
    """This function prints a beautiful header followed by one empty line.

    :param text: text to be displayed as header

    """
    print("\n" + "=" * len(text))
    print(text)
    print("=" * len(text) + "\n")

@show_user
def print_help(display="all"):
    """Prints a help menu on the screen for the user. This function can be used by the command [h].

    :param display: specify the lenght of the help menu, options are 'all' or 'quick' (Default value = "all")

    """
    # print a list of all available commands
    if display == 'all':
        for k,v in USER_FUNCTIONS.items():
            print(f'{k} ... {v.__name__}')
        print(f'q ... quit {PROGRAM_NAME}\n')

    # show only the function that should be displayed, but this time with a docstring
    else:
        try:
            f = USER_FUNCTIONS[display[0]]
            print(f'{display[0]}: \n\n {f.__doc__}\n\n')
        except Exception:
            print(f'Sorry, but there is not help page for {display[0]} available.\n')


def find_all_files():
    """Finds all dat files recursively in all subdirectories ignoring hidden directories
    and Python specific ones.
    
    Returns a list of filepaths.


    """
    if configuration['PIGOR_ROOT_RECURSIVE']:
        return sorted(configuration['PIGOR_ROOT'].rglob('*' + configuration['FILE_EXTENTION']))
    else:
        return sorted(configuration['PIGOR_ROOT'].glob('*' + configuration['FILE_EXTENTION']))

@show_user
def analyse_files(filepaths='all'):
    """Analyses all given files in list. This function can be used by the command [a].

    :param filepaths:   list of files to analyse with
                        their relative dir path added

    .. todo:: Change to no override mode. measurement.Measurement.plot(override=False)
    .. todo:: a + today => only analyse files for today
    .. todo:: a + override => override=True

    """
    # get the theme from config
    theme = configuration['EXPORT_THEME']

    if filepaths == 'all':
        filepaths = find_all_files()
    elif filepaths == 'today':
        pass # TODO: only analyse the files of today

    # analyse all files
    for f in filepaths:
        try:
            m = measurement.Measurement(f)
            export_fit = 'Mathematica' if configuration['EXPORT_FIT_FUNCTIONS'] == True else False 
            m.fit(fit_function_export=export_fit)
            m.plot(file_extention=configuration['IMAGE_FORMAT'])
            m.export_meta(make_html=True, theme=theme)
        except Exception as e:
            print(f'The following exception occured during runtime:\n\n{e}\n\nContinuing operation.')

@show_user
def create_index():
    """Creates an index.html listing all directories and subdirectories and their HTML and Markdown files. If 
    a default browser is found, it will automatically open index.html. This function can be used by the 
    command [j]."""
    
    # theme for the index.html
    theme = configuration['EXPORT_THEME']

    # find all measurements
    files = sorted(configuration['PIGOR_ROOT'].rglob('*' + configuration['FILE_EXTENTION']))
    files = [f.relative_to(configuration['PIGOR_ROOT'].parent) for f in files]

    files.sort(key=lambda x: x.name, reverse=True)

    # list all measurement files, if they have a corresponding html or md file
    l = []
    for f in files:
        buffer = f'- {f.parent}: [{f.stem}]({f.resolve().as_uri()}): '

        m = f.with_suffix('.md').exists()
        h = f.with_suffix('.html').exists()
        p = f.with_suffix(configuration['IMAGE_FORMAT']).exists()

        
        if m:
            buffer += f'[MD]({f.with_suffix(".md").resolve().as_uri()})'
        if h:
            buffer += f' | [HTML]({f.with_suffix(".html").resolve().as_uri()})'
        if p:
            buffer += f' | [PLOT]({f.with_suffix(configuration["IMAGE_FORMAT"]).resolve().as_uri()})'

        if m or h:
            l.append(buffer)

    if l:
        # write header of index.html
        t = [
            f'# {PROGRAM_NAME} Index File',
            '',
            'Here is a handy list of all the files that have been analysed so far:',
            ''
        ]
        t.extend(l)

        # write html template
        h1 = [
            '<!DOCTYPE html>',
            '<html>',
            '<head>',
            '<meta charset="UTF-8">',
            f'<title>{PROGRAM_NAME} Index File</title>',
            '<style>'
        ]
        h2 = [
            '</style>',
            '</head>',
            '<body>',
            markdown('\n'.join(t)),
            '</body>',
            '</html>'
        ]

        index_file_path = configuration['PIGOR_ROOT'].joinpath('index_pigor.html')
        with open(index_file_path, 'w') as htmlfile:
            # writing html head
            for line in h1:
                htmlfile.write(f'{line}\n')

            # copying the css file
            css_path = Path('./markdown_themes/{}.css'.format(theme))
            with open(css_path, 'r') as cssfile:
                for line in cssfile:
                    htmlfile.write(line)

            # write actual content of html file and end
            for line in h2:
                htmlfile.write(f'{line}\n')

        print(f'Wrote index file at {index_file_path}')

        
        webbrowser.open(index_file_path.as_uri(), new=2, autoraise=True)


@show_user
def remove_generated_files(files='all'):
    """Removes the generated png, html and md files. This function can be used by the command [r].
    
    :param files:   list of Path objects to files that should be removed; if set to 'all' it will delete all generated files (Default value = 'all')

    .. todo:: Cover the case when files are not a list of path, e.g. wrong input given.
    """
    if files == 'all':
        files = find_all_files()
    

    # TODO: case when files is not a list of Paths
    try:
        for f in files:
            f.with_suffix(configuration['IMAGE_FORMAT']).unlink()
            f.with_suffix('.md').unlink()
            f.with_suffix('.html').unlink()
            f.with_name(f.stem + '_fit_functions.txt').unlink()
    except Exception as e:
        print(e)
        #raise NotImplementedError
    
@show_user
def print_root():
    """Prints the root for PIGOR, e.g. where it will look for files to analyse. This function
    can be used by the command [x].
    """
    print(f"{PROGRAM_NAME} will look for measurement files in {configuration['PIGOR_ROOT'].resolve()}.\n")


def main():
    """Main Loop"""

    # perform initialization
    init(create_new_config_file=False)
    
    # starting main loop
    print_header("Welcome to {}.".format(PROGRAM_NAME))
    # print help menu
    print_help(display="all")
    # print where PIGOR will look for files in
    print_root()
    print('If you need more information about a command, just type h + [command] + <ENTER> to get more help. For example: h + a + <ENTER>.\n')

    while True:
        print("Please type a command you want to perform and press <ENTER>.")

        # get the user's input
        user_input = input()
        # split it for additional but optional arguments
        cmd, *args = user_input.split(' ')

        if cmd == "q":
            break
        elif cmd in USER_FUNCTIONS.keys():
            f = USER_FUNCTIONS[cmd]
            if args:
                f(args)
            else:
                f()
        else:
            print('The command you typed does not exist. Press h + <ENTER> for help.')
        
if __name__ == '__main__':
    mp.freeze_support()
    main()

#!/usr/bin/env python3

import re
import os
import json
import numpy as np
import multiprocessing as mp
import matplotlib  
#matplotlib.use('TkAgg')   
import matplotlib.pyplot as plt
import glob
import measurement
import datetime
import webbrowser
from pathlib import Path
from markdown import markdown

# name of the program
PROGRAM_NAME = "PIGOR v1.2"

# functions that the user can utilize
USER_FUNCTIONS = dict()

# global config dict
configuration = dict()

# global vars for init() default
configuration_fallback = {
    'PIGOR_ROOT'            :   Path(os.path.dirname(os.path.abspath(__file__))),
    'PIGOR_ROOT_RECURSIVE'  :   True,
    'FILE_EXTENTION'        :   '.dat',
    'IMAGE_FORMAT'          :   '.png',
    'CREATE_HTML'           :   True,
    'CREATE_MD'             :   True,
    'EXPORT_THEME'          :   'github',
    'EXPORT_FIT_FUNCTIONS'  :   True
}


# decorator for registering functions
def show_user(func):
    """Register a function to be displayed to the user as an option"""

    global USER_FUNCTIONS

    try:
        key = re.search(r'\[(.+?)\]', func.__doc__).group(1)
    except AttributeError as e:
        key = func.__name__
        print(e)

    USER_FUNCTIONS[key] = func
    return func

def bool2yn(b):
    """Converts a boolean to yes or no with the mapping: y = True, n = False."""
    return 'y' if b else 'n'

def yn2bool(s):
    """Converts yes and no to True and False."""
    return True if s.casefold().startswith('y') else False

def is_valid_theme(theme):
    """Checks if this theme exists."""
    themes = sorted(Path('./markdown_themes').glob('*.css'))
    themes = [t.stem for t in themes]
    if theme in themes:
        return True
    else:
        return False

def list_themes():
    """Returns a list of all themes available."""
    themes = sorted(Path('./markdown_themes').glob('*.css'))
    return [t.stem for t in themes]

@show_user
def init(create_new_config_file=True):
    """This function will read the config file and initialize some variables
    accordingly. This function can be used by the command [i].

    Available options:

    - root directory where PIGOR will start to look for measurement files
    - Should PIGOR look for files to analyse recursively?
    - Which file extention do the measurement files posess?
    - What plot output format should PIGOR use?
    - Should PIGOR automatically create an html file?
    - Should PIGOR automatically create a md file?
    - Should PIGOR create a txt file containing all used fit functions for the use in Mathematica?
    
    .. note:: If no config file can be found, it will create one.
    
    """
    
    # make configuration editable
    global configuration


    # try to read the configuration
    try:
        with open('pigor-config.json', 'r', encoding='utf-8') as f:
            c = json.load(f)
            # make it a path object
            c['PIGOR_ROOT'] = Path(c['PIGOR_ROOT']).resolve()

            # check if new item has been added to configuration
            if len(c) != len(configuration_fallback):
                create_new_config_file = True
                for k, v in configuration_fallback:
                    if k not in c:
                        c[k] = v
    except Exception:
        print('Could not read configuration file. Creating a new one now:\n')
        c = configuration_fallback
        create_new_config_file = True
        

    # questions to aks the user
    questions = {
        'PIGOR_ROOT'            :   (f'Where should {PROGRAM_NAME} start looking for measurement files? [{c["PIGOR_ROOT"]}]', 'path'),
        'PIGOR_ROOT_RECURSIVE'  :   (f'Should {PROGRAM_NAME} look for files recursively? (y/n) [{bool2yn(c["PIGOR_ROOT_RECURSIVE"])}]', 'bool'),
        'FILE_EXTENTION'        :   (f'Which file extention should {PROGRAM_NAME} look for? (string) [{c["FILE_EXTENTION"]}]', 'file-extention-data'),
        'IMAGE_FORMAT'          :   (f'What image format should the plots have? (.png,.svg,.eps,.pdf) [{c["IMAGE_FORMAT"]}]', 'file-extention-image'),
        'CREATE_HTML'           :   (f'Should {PROGRAM_NAME} create an HTML file automatically after analysis? (y/n) [{bool2yn(c["CREATE_HTML"])}]', 'bool'),
        'CREATE_MD'             :   (f'Should {PROGRAM_NAME} create a Markdown file automatically after analysis? (y/n) [{bool2yn(c["CREATE_MD"])}]', 'bool'),
        'EXPORT_THEME'          :   (f'Please choose a theme for the exported html files: ({list_themes()}) [{c["EXPORT_THEME"]}]', is_valid_theme),
        'EXPORT_FIT_FUNCTIONS'  :   (f'Should {PROGRAM_NAME} create a txt file containing all used fit functions for later use in Mathematica? (y/n) [{bool2yn(c["EXPORT_FIT_FUNCTIONS"])}]', 'bool')
    }

    for k, q in questions.items():
        if not k in c.keys() or create_new_config_file:
            # ask user a question
            print(q[0])
            user_input = input()
            # if a Path object must be read
            if q[1] == 'path':
                while True:
                    try:
                        if user_input == '':
                            value = None
                            break
                        value = Path(user_input)
                        if value.exists():
                            break
                        else:
                            print('The path you entered does not exist. Please try again.')
                            user_input = input()
                    except Exception:
                        user_input = input('Could not find the path, please input another one:\n')
            
            # if treating booleans
            elif q[1] == 'bool':
                if not user_input == '':
                    value = yn2bool(user_input)
                else:
                    value = None
            
            # if answer is a file extention
            elif not callable(q[1]) and q[1].startswith('file-extention'):
                if user_input == '' and q[1] == 'file-extention-image':
                    value = None
                elif user_input == '' and q[1] == 'file-extention-data':
                    value = None
                else:
                    # adding . to file extention if not given by the user
                    value = user_input.casefold() if user_input.startswith('.') else '.' + user_input.casefold()
            elif callable(q[1]):
                while True:
                    if user_input == '':
                        value = None
                        break
                    if q[1](user_input) == True:
                        value = user_input
                        break
                    else:
                        user_input = input('Sorry, PIGOR does not know this input. Try again.')
            else:
                value = user_input

            # renew old configuration value
            if value != None:
                c[k] = value



    # write config to global var
    configuration = c.copy()

    # write configuration into file
    with open('pigor-config.json', 'w') as f:
        if isinstance(c['PIGOR_ROOT'], Path):
            c['PIGOR_ROOT'] = str(c['PIGOR_ROOT'].resolve())
        json.dump(c, f, ensure_ascii=False, indent=4)


def print_header(text):
    """This function prints a beautiful header followed by one empty line.

    :param text: text to be displayed as header

    """
    print("\n" + "=" * len(text))
    print(text)
    print("=" * len(text) + "\n")

@show_user
def print_help(display="all"):
    """Prints a help menu on the screen for the user. This function can be used by the command [h].

    :param display: specify the lenght of the help menu, options are 'all' or 'quick' (Default value = "all")

    """
    # print a list of all available commands
    if display == 'all':
        for k,v in USER_FUNCTIONS.items():
            print(f'{k} ... {v.__name__}')
        print(f'q ... quit {PROGRAM_NAME}\n')

    # show only the function that should be displayed, but this time with a docstring
    else:
        try:
            f = USER_FUNCTIONS[display[0]]
            print(f'{display[0]}: \n\n {f.__doc__}\n\n')
        except Exception:
            print(f'Sorry, but there is not help page for {display[0]} available.\n')


def find_all_files():
    """Finds all dat files recursively in all subdirectories ignoring hidden directories
    and Python specific ones.
    
    Returns a list of filepaths.


    """
    if configuration['PIGOR_ROOT_RECURSIVE']:
        return sorted(configuration['PIGOR_ROOT'].rglob('*' + configuration['FILE_EXTENTION']))
    else:
        return sorted(configuration['PIGOR_ROOT'].glob('*' + configuration['FILE_EXTENTION']))

@show_user
def analyse_files(filepaths='all'):
    """Analyses all given files in list. This function can be used by the command [a].

    :param filepaths:   list of files to analyse with
                        their relative dir path added

    .. todo:: Change to no override mode. measurement.Measurement.plot(override=False)
    .. todo:: a + today => only analyse files for today
    .. todo:: a + override => override=True

    """
    # get the theme from config
    theme = configuration['EXPORT_THEME']

    if filepaths == 'all':
        filepaths = find_all_files()
    elif filepaths == 'today':
        pass # TODO: only analyse the files of today

    # analyse all files
    for f in filepaths:
        try:
            m = measurement.Measurement(f)
            export_fit = 'Mathematica' if configuration['EXPORT_FIT_FUNCTIONS'] == True else False 
            m.fit(fit_function_export=export_fit)
            m.plot(file_extention=configuration['IMAGE_FORMAT'])
            m.export_meta(make_html=True, theme=theme)
        except Exception as e:
            print(f'The following exception occured during runtime:\n\n{e}\n\nContinuing operation.')

@show_user
def create_index():
    """Creates an index.html listing all directories and subdirectories and their HTML and Markdown files. If 
    a default browser is found, it will automatically open index.html. This function can be used by the 
    command [j]."""
    
    # theme for the index.html
    theme = configuration['EXPORT_THEME']

    # find all measurements
    files = sorted(configuration['PIGOR_ROOT'].rglob('*' + configuration['FILE_EXTENTION']))
    files = [f.relative_to(configuration['PIGOR_ROOT'].parent) for f in files]

    files.sort(key=lambda x: x.name, reverse=True)

    # list all measurement files, if they have a corresponding html or md file
    l = []
    for f in files:
        buffer = f'- {f.parent}: [{f.stem}]({f.resolve().as_uri()}): '

        m = f.with_suffix('.md').exists()
        h = f.with_suffix('.html').exists()
        p = f.with_suffix(configuration['IMAGE_FORMAT']).exists()

        
        if m:
            buffer += f'[MD]({f.with_suffix(".md").resolve().as_uri()})'
        if h:
            buffer += f' | [HTML]({f.with_suffix(".html").resolve().as_uri()})'
        if p:
            buffer += f' | [PLOT]({f.with_suffix(configuration["IMAGE_FORMAT"]).resolve().as_uri()})'

        if m or h:
            l.append(buffer)

    if l:
        # write header of index.html
        t = [
            f'# {PROGRAM_NAME} Index File',
            '',
            'Here is a handy list of all the files that have been analysed so far:',
            ''
        ]
        t.extend(l)

        # write html template
        h1 = [
            '<!DOCTYPE html>',
            '<html>',
            '<head>',
            '<meta charset="UTF-8">',
            f'<title>{PROGRAM_NAME} Index File</title>',
            '<style>'
        ]
        h2 = [
            '</style>',
            '</head>',
            '<body>',
            markdown('\n'.join(t)),
            '</body>',
            '</html>'
        ]

        index_file_path = configuration['PIGOR_ROOT'].joinpath('index_pigor.html')
        with open(index_file_path, 'w') as htmlfile:
            # writing html head
            for line in h1:
                htmlfile.write(f'{line}\n')

            # copying the css file
            css_path = Path('./markdown_themes/{}.css'.format(theme))
            with open(css_path, 'r') as cssfile:
                for line in cssfile:
                    htmlfile.write(line)

            # write actual content of html file and end
            for line in h2:
                htmlfile.write(f'{line}\n')

        print(f'Wrote index file at {index_file_path}')

        
        webbrowser.open(index_file_path.as_uri(), new=2, autoraise=True)


@show_user
def remove_generated_files(files='all'):
    """Removes the generated png, html and md files. This function can be used by the command [r].
    
    :param files:   list of Path objects to files that should be removed; if set to 'all' it will delete all generated files (Default value = 'all')

    .. todo:: Cover the case when files are not a list of path, e.g. wrong input given.
    """
    if files == 'all':
        files = find_all_files()
    

    # TODO: case when files is not a list of Paths
    try:
        for f in files:
            f.with_suffix(configuration['IMAGE_FORMAT']).unlink()
            f.with_suffix('.md').unlink()
            f.with_suffix('.html').unlink()
            f.with_name(f.stem + '_fit_functions.txt').unlink()
    except Exception as e:
        print(e)
        #raise NotImplementedError
    
@show_user
def print_root():
    """Prints the root for PIGOR, e.g. where it will look for files to analyse. This function
    can be used by the command [x].
    """
    print(f"{PROGRAM_NAME} will look for measurement files in {configuration['PIGOR_ROOT'].resolve()}.\n")


def main():
    """Main Loop"""

    # perform initialization
    init(create_new_config_file=False)
    
    # starting main loop
    print_header("Welcome to {}.".format(PROGRAM_NAME))
    # print help menu
    print_help(display="all")
    # print where PIGOR will look for files in
    print_root()
    print('If you need more information about a command, just type h + [command] + <ENTER> to get more help. For example: h + a + <ENTER>.\n')

    while True:
        print("Please type a command you want to perform and press <ENTER>.")

        # get the user's input
        user_input = input()
        # split it for additional but optional arguments
        cmd, *args = user_input.split(' ')

        if cmd == "q":
            break
        elif cmd in USER_FUNCTIONS.keys():
            f = USER_FUNCTIONS[cmd]
            if args:
                f(args)
            else:
                f()
        else:
            print('The command you typed does not exist. Press h + <ENTER> for help.')
        
if __name__ == '__main__':
    mp.freeze_support()
    main()

#!/usr/bin/env python3 #matplotlib.use('TkAgg') # name of the program # functions that the user can utilize # global config dict # global vars for init() default # decorator for registering functions Register a function to be displayed to the user as an option Converts a boolean to yes or no with the mapping: y = True, n = False. Converts yes and no to True and False. Checks if this theme exists. Returns a list of all themes available. This function will read the config file and initialize some variables
     accordingly. This function can be used by the command [i].
 
     Available options:
 
     - root directory where PIGOR will start to look for measurement files
     - Should PIGOR look for files to analyse recursively?
     - Which file extention do the measurement files posess?
     - What plot output format should PIGOR use?
     - Should PIGOR automatically create an html file?
     - Should PIGOR automatically create a md file?
     - Should PIGOR create a txt file containing all used fit functions for the use in Mathematica?
     
     .. note:: If no config file can be found, it will create one. # make configuration editable # try to read the configuration # make it a path object # check if new item has been added to configuration # questions to aks the user # ask user a question # if a Path object must be read # if treating booleans # if answer is a file extention # adding . to file extention if not given by the user # renew old configuration value # write config to global var # write configuration into file This function prints a beautiful header followed by one empty line.
 
     :param text: text to be displayed as header Prints a help menu on the screen for the user. This function can be used by the command [h].
 
     :param display: specify the lenght of the help menu, options are 'all' or 'quick' (Default value = "all") # print a list of all available commands # show only the function that should be displayed, but this time with a docstring Finds all dat files recursively in all subdirectories ignoring hidden directories
     and Python specific ones.
     
     Returns a list of filepaths. Analyses all given files in list. This function can be used by the command [a].
 
     :param filepaths:   list of files to analyse with
                         their relative dir path added
 
     .. todo:: Change to no override mode. measurement.Measurement.plot(override=False)
     .. todo:: a + today => only analyse files for today
     .. todo:: a + override => override=True # get the theme from config # TODO: only analyse the files of today # analyse all files Creates an index.html listing all directories and subdirectories and their HTML and Markdown files. If 
     a default browser is found, it will automatically open index.html. This function can be used by the 
     command [j]. # theme for the index.html # find all measurements # list all measurement files, if they have a corresponding html or md file # write header of index.html # write html template # writing html head # copying the css file # write actual content of html file and end Removes the generated png, html and md files. This function can be used by the command [r].
     
     :param files:   list of Path objects to files that should be removed; if set to 'all' it will delete all generated files (Default value = 'all')
 
     .. todo:: Cover the case when files are not a list of path, e.g. wrong input given. # TODO: case when files is not a list of Paths #raise NotImplementedError Prints the root for PIGOR, e.g. where it will look for files to analyse. This function
     can be used by the command [x]. Main Loop # perform initialization # starting main loop # print help menu # print where PIGOR will look for files in # get the user's input # split it for additional but optional arguments