#####################################################
### Lazy DayZ config generator 1.0.0 - By Kerkkoh ###
#####################################################
### Prerequisites: - Python >3.0                  ###
### License: MIT License                          ###
### Use at your own discretion!                   ###
#####################################################
#
# Use "py config_generator.py --help" to see details on usage!
#

import os
import sys
from datetime import datetime

args = sys.argv

header = '''###################################################
## Lazy DayZ config generator 1.0.0 - By Kerkkoh ##
## Prerequisites: - Python >3.0                  ##
## License: MIT License                          ##
## Use at your own discretion!                   ##
###################################################'''

if '--help' in args and len(args) == 2:
    print(header + '''

USAGE:
    py config_generator.py [FLAGS] [OPTIONS] [pdrive] [folder]

FLAGS:
    -d, --debug       Activate debug mode
    -o, --override    Override existing config.cpp if any present
                      if this flag isn't present the config is saved as config.gen.cpp recursively with
                      more and more .gen in the name if previous generated files exist.
    -r, --recursive   Run for each subfolder and produce a config at EACH subfolder's root (i.e. MANY configs)
    -s, --single      Generate a SINGLE config file at the root containing entries from all SUBFOLDERS
    -h, --help        Prints this lovely message...
    -p, --purge       Removes any config.gen.cpp and config.gen.cpp and so on...
OPTIONS:
    +c, ++className=<value>                Classname used in the cfgPatches entry, defaults to generated_<addonName>
    +p, ++prefix=<value>                   Prefix in cfgVehicles entries, underscore is applied to the end automatically, defaults to Land
    +b, ++baseClass=<value>                Class that all objects will inherit, defaults to HouseNoDestruct
    +r, ++requiredAddons=<requiredAddons>  List in format "Addon1","Addon2",...,"AddonX" that is input as requiredAddons, defaults to "DZ_Data"
ARGS:
    <pdrive>  The prefix of the <folder> path, i.e. the P-drive, which get cut out of the <folder> path to form
              a path for the model in the class e.g. P:\\mod\\model.p3d gets turned to mod\\model.p3d -- defaults to P:\\
    <folder>  Folder to process

EXAMPLE:
    $ py config_generator.py P:\\ P:\\DZ\\structures\\roads\\Tunnels
    Outputs
    class CfgPatches
    {
        class generated_Tunnels
        {
            units[] = { "Land_Tunnel_Biathlon_Left", ... };
            weapons[] = {};
            requiredVersion = 0.1;
            requiredAddons[] = { "DZ_Data" };
        };
    };
    class CfgVehicles
    {
        class HouseNoDestruct;
        class Land_Tunnel_Biathlon_Left: HouseNoDestruct
        {
            scope = 2;
            displayName = "Land_Tunnel_Biathlon_Left";
            descriptionShort = "Autogenerated class Land_Tunnel_Biathlon_Left";
            model = "DZ\\structures\\roads\\Tunnels\\Tunnel_Biathlon_Left.p3d";
        };
        ...
    };

NOTE!
    Python filesystem might not recognize the P-drive as an actual drive, so the workaround is to use your P-drive location in the paths instead.
    So if your P-drive is mounted to C:\\dayzstuff, instead of
    $ py config_generator.py P:\\ P:\\DZ\\structures\\roads\\Tunnels
    you should run
    $ py config_generator.py C:\\dayzstuff C:\\dayzstuff\\DZ\\structures\\roads\\Tunnels
''')
    quit()

if len(args) < 3:
    print(header + '''

USAGE:
    py config_generator.py [FLAGS] [OPTIONS] [pdrive] [folder]

For more information try --help''')
    quit()

# Flags
DEBUG = False
OVERRIDE_CONFIG = False
RECURSIVE = False
SINGLE = False
PURGE = False

# Options
CFGPATCHES_CLASSNAME = ''
CLASSNAME_PREFIX = 'Land'
BASECLASS = 'HouseNoDestruct'
REQUIRED_ADDONS = '"DZ_Data"'

# Args
PDRIVE_ARG = args[-2]
FOLDER_ARG = args[-1]

def log(what):
    if DEBUG:
        print(what)
def error(what):
    if DEBUG:
        raise Exception(what)
    else:
        print('!! ERROR !!')
        print(what)
        print('Can\'t run script further...')
        print('!! ERROR !!')

for arg in args:
    if arg[0] == '-' and arg[1] != '-':
        flag = arg[1:].lower()
        if flag == 'd':
            DEBUG = True
        if flag == 'o':
            OVERRIDE_CONFIG = True
        if flag == 'r':
            RECURSIVE = True
        if flag == 's':
            SINGLE = True
        if flag == 'p':
            PURGE = True
        continue
    if arg[0] == '-' and arg[1] == '-':
        flag = arg[2:].lower()
        if flag == 'debug':
            DEBUG = True
        if flag == 'override':
            OVERRIDE_CONFIG = True
        if flag == 'recursive':
            RECURSIVE = True
        if flag == 'single':
            SINGLE = True
        if flag == 'purge':
            PURGE = True
        continue
    if arg[0] == '+' and arg[1] != '+':
        opt = arg[1].lower()
        val = arg.split('=')[1]
        if opt == 'c':
            CFGPATCHES_CLASSNAME = val
        if opt == 'p':
            CLASSNAME_PREFIX = val
        if opt == 'b':
            BASECLASS = val
        if opt == 'r':
            REQUIRED_ADDONS = val
        continue
    if arg[0] == '+' and arg[1] == '+':
        opt = arg[2:].lower()
        val = arg.split('=')[1]
        if opt == 'classname':
            CFGPATCHES_CLASSNAME = val
        if opt == 'prefix':
            CLASSNAME_PREFIX = val
        if opt == 'baseclass':
            BASECLASS = val
        if opt == 'requiredaddons':
            REQUIRED_ADDONS = val

if len(CFGPATCHES_CLASSNAME) == 0:
    CFGPATCHES_CLASSNAME = 'generated_{}'.format(FOLDER_ARG.split('\\')[-1])

log('\\___ Flags: DEBUG = {}, OVERRIDE_CONFIG = {}, RECURSIVE = {}, SINGLE = {}'.format(DEBUG, OVERRIDE_CONFIG, RECURSIVE, SINGLE))
log('\\___ Options: CFGPATCHES_CLASSNAME = {}, CLASSNAME_PREFIX = {}, BASECLASS = {}, REQUIRED_ADDONS = {}'.format(CFGPATCHES_CLASSNAME, CLASSNAME_PREFIX, BASECLASS, REQUIRED_ADDONS))

tag = '''//////////////////////////////////////////////////////////////
// DayZ Config Generator: {0}
// Produced from Kerkkoh's DayZ Config Generator version 1.0.0
// https://github.com/kerkkoh/dayzconfiggen
// 'now' is {1}
//////////////////////////////////////////////////////////////
'''.format(FOLDER_ARG.split(PDRIVE_ARG)[-1], datetime.now().strftime("%m/%d/%Y, %H:%M:%S"))

cfgPatchesTemplate = tag + '''class CfgPatches
{{
    class {0}
    {{
        units[] = {{ {1} }};
        weapons[] = {{}};
        requiredVersion = 0.1;
        requiredAddons[] = {{ {2} }};
    }};
}};
class CfgVehicles
{{
	class {3};
'''
cfgVehiclesTemplate = '''    class {0}: {1}
    {{
        scope = 2;
        displayName = "{0}";
        descriptionShort = "Autogenerated class {0}";
        model = "{2}";
    }};
'''

def safeSave(filepath, extension, contents):
    file = '{}.{}'.format(filepath, extension)
    log('Trying safely save into ' + file)
    if os.path.isfile(file):
        safeSave(filepath + '.gen', extension, contents)
    else:
        print('Saved config into ' + file)
        f = open(file, "w")
        f.write(contents)
        f.close()
def purge(filepath, extension):
    file = '{}.{}'.format(filepath, extension)
    log('Trying to purge ' + file)
    if os.path.isfile(file):
        os.remove(file)
        purge(filepath + '.gen', extension)
        

def generator(pdrive, folder, isroot):
    if not os.path.isdir(folder):
        return error('The folder (last parameter) "{}" is not a folder'.format(folder))
    log('-->Folder {} is valid.'.format(folder))
    if not os.path.isdir(pdrive):
        return error('The P-drive (second last parameter) "{}" is not a folder'.format(folder))
    if pdrive not in folder:
        return error('P-drive path "{}" is not contained within the folder path "{}" -> can not generate engine compatible paths for models'.format(pdrive, folder))
    log('-->P-drive path {} is valid.'.format(pdrive))

    if (folder[-1] != '\\'):
        folder = folder + '\\'
    if (pdrive[-1] != '\\'):
        pdrive = pdrive + '\\'

    def fullPath(f):
        return os.path.join(folder, f)

    subdirs = [fullPath(f) for f in os.listdir(folder) if os.path.isdir(fullPath(f)) and f != 'source']
    units = ''
    entries = []

    if PURGE:
        log('Purging...')
        purge(fullPath('config.gen'), 'cpp')

    if RECURSIVE and not SINGLE:
        for subdir in subdirs:
            generator(pdrive, subdir, False)
    if SINGLE:
        for subdir in subdirs:
            subunits, subentries = generator(pdrive, subdir, False)
            units += subunits
            entries.extend(subentries)
    
    files = [f.split(".")[0] for f in os.listdir(folder) if os.path.isfile(fullPath(f)) and f.split('.')[-1] == 'p3d']
    files.sort()

    for p3d in files:
        classname = '{}_{}'.format(CLASSNAME_PREFIX, p3d)
        units += '"{}", '.format(classname)

        engineFolder = folder.split(pdrive)[-1]

        p3dpath = '{}{}.p3d'.format(engineFolder, p3d)
        log('---> Generating class {} for {}'.format(classname, p3dpath))
        entries.append(cfgVehiclesTemplate.format(classname, BASECLASS, p3dpath))

    if SINGLE and not isroot:
        return [units, entries]

    if len(units) > 0:
        units = units[:-2]
    else:
        return

    config = cfgPatchesTemplate.format(CFGPATCHES_CLASSNAME, units, REQUIRED_ADDONS, BASECLASS)
    for e in entries:
        config += e
    config += '};'

    configPath = fullPath('config.cpp')
    if os.path.isfile(configPath):
        if OVERRIDE_CONFIG:
            print('Removing ' + configPath)
            os.remove(configPath)
        else:
            safeSave(fullPath('config'), 'cpp', config)
    else:
        print('Creating a new config ' + configPath)
        f = open(configPath, 'x')
        f.write(config)
        f.close()

print(header)
print('Starting generator...')
generator(args[-2], FOLDER_ARG, True)
print('Done!')
