#!/usr/bin/python3

import os
import sys
import shutil
import argparse
import re
import json
import subprocess

def parse_args():
    """ Command line argument parser """

    # Raise terminal size, See https://bugs.python.org/issue13041
    os.environ['COLUMNS'] = str(shutil.get_terminal_size().columns)

    parser = argparse.ArgumentParser(description="Create customized configuration file for XMR Stak CPU", formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument('-t', '--template'    ,    default='/etc/xmr-stak-cpu/config.txt.template', type=argparse.FileType('r'),  help='Default config.txt file to be used as a template')
    parser.add_argument('-o', '--output',          default='-',                                              help='Write generated config to filename or "-" for stdout', metavar='/etc/xmr-stak-cpu/config.txt')
    parser.add_argument('-x', '--xmr-stak-bin',    default='/usr/bin/xmr-stak-cpu',  type=str,               help='Path to xmr-stak-cpu binary')
    parser.add_argument('-p', '--pool-address',    default='europe.cryptonight-hub.miningpoolhub.com:20580', help='Stratum pool address host:port')
    parser.add_argument('-u', '--username-wallet', default='acecile.xmr-stak-cpu',                           help='Wallet address or your pool login username')
    parser.add_argument('-w', '--pool-password',   default='x',                                              help='Pool password usually not required, use "x" to unset')
    parser.add_argument('-n', '--no-overwrite',    action='store_true',                                      help='Do not overwrite output file if already existing')
    parser.add_argument('-s', '--httpd-port',      default=42001,  type=int,                                 help='Port to start embedded reporting HTTP server on, 0 to disable')
    
    return parser.parse_args()


def comment_remover(text):
    """ Remove C-style comment (/* */) because JSON module cannot handle this """

    def replacer(match):
        s = match.group(0)
        if s.startswith('/'):
            return " " # note: a space and not an empty string
        else:
            return s
    pattern = re.compile(
        r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
        re.DOTALL | re.MULTILINE
    )
    return re.sub(pattern, replacer, text)


def dirty_json_cleanup(text):
    text = re.sub(",[ \t\r\n]+}", "}", text)
    text = re.sub(",[ \t\r\n]+\]", "]", text)
    return text


def get_recommended_cpu_threads_conf(xmr_bin, template_config):
    """ Run xmr-stak-cpu binary with template config file and extract suggested cpu_thread_conf settings """

    output = subprocess.check_output([xmr_bin, template_config.name])

    # Find start/end tags and keep what's inside
    r_output = output.decode('utf-8').splitlines()
    for index, line in enumerate(r_output):
        if 'Copy&Paste BEGIN' in line:
            start_index = index
        if 'Copy&Paste END' in line:
            end_index = index

    # We'll now replace first and ends tags by { } to get a valid JSON
    r_output = r_output[start_index:end_index]
    r_output[0] ='{'
    r_output[-1] = '}'

    # Back to string and clean trailing comas
    cpu_threads_conf = '\n'.join(r_output)
    cpu_threads_conf = dirty_json_cleanup(cpu_threads_conf)

    # Convert to a Python dict by loading string as a JSON
    d_cpu_threads_conf = json.loads(cpu_threads_conf)

    return d_cpu_threads_conf['cpu_threads_conf']


if __name__ == '__main__':

    # Parse command line arguments
    config = parse_args()

    # Read config template
    template = config.template.read()

    # Strip C-style comments
    template = comment_remover(template)

    # Let's kill all empty lines too
    template = '\n'.join([ line.strip() for line in template.splitlines() if line.strip() ])

    # Well a valid JSON should start with brackets
    if template.splitlines()[0] != '{':
       template = '{\n' + template
    # And does not want a trailing coma at last line
    if template.splitlines()[-1] != '}':
       template = template.rstrip(',') + '\n}'

    # Turn into a Python dict, assuming it's now a valid JSON file
    d_config = json.loads(template)

    # Run xmr-stak-cpu to generate cpu_threads_config suggestion
    cpu_threads_conf = get_recommended_cpu_threads_conf(config.xmr_stak_bin, config.template)

    # Modify configuration
    d_config['cpu_threads_conf'] = cpu_threads_conf
    d_config['pool_address'] = config.pool_address
    d_config['pool_address'] = config.pool_address
    d_config['wallet_address'] = config.username_wallet
    d_config['pool_password'] = config.pool_password
    d_config['verbose_level'] = 4 # Enable periodic hashrate printout
    d_config['httpd_port'] = config.httpd_port

    # Back to string
    # Remove leading space, remove main {}
    s_config = json.dumps(d_config, indent=4, sort_keys=True)
    s_config = '\n'.join([ re.sub('^    ', '', line) for line in s_config.splitlines()[1:-1] ])

    if config.output == '-':
        print(s_config)
    else:
        if os.path.exists(config.output) and config.no_overwrite:
            print('%s already exists and --no-overwrite asked' % config.output, file=sys.stderr)
        else:
            with open(config.output, 'w') as fh:
                print(s_config, file=fh)
