#!/usr/bin/env python
# -*- coding: utf8 -*-


import sys
import argparse


class Parser:
    "Parser for files with IF_XYZ(A|B) constructs"
    def __init__(self, switches):
        self.mSwitches = switches

    def Switches(self):
        "outputs the switches used by parser"
        return self.mSwitches

    def SetSwitch(self, key, val):
        "Set or add a (key,val) switch"
        self.mSwitches[key] = val

    def ParseSingleIf(self, string, switch):
        """
        switch should be a tuple (key, val)
        ParseSingleIf(string, switch) will replace in string the
        first occurance of IF_key(A|B) with A if val = True;
        B if val = False
        """
        init = string.find('IF_' + switch[0])
        start = init
        end = len(string)
        mark = end
        counter = 0
        ind = start
        # determine the correct location for '|'
        for cc in string[init:]:
            if cc == '(':
                if counter == 0: start = ind
                counter += 1
            elif cc == ')':
                counter -= 1
                if counter == 0: end = ind; break
            elif cc == "|" and counter == 1:
                mark = ind
            ind += 1
        # resolve the option
        if switch[1]:
            result = string[0         : init] + \
                     string[start + 1 : mark] + \
                     string[end + 1   : len(string)]
        else:
            result = string[0         : init] + \
                     string[mark + 1  : end]  + \
                     string[end + 1   : len(string)]
        return result

    def ParseIf(self, string, switch):
        """
ParseIf(string, switch) will replace in string recursively
occurance of IF_key(A|B) statements with A if val = True;
B if val = False
        """
        result = string
        while result.find('IF_' + switch[0]) > -1:
            result = self.ParseSingleIf(result, switch)
        return result

    def ParseString(self, string):
        """
ParseString(string) will parse in string recursively
all of IF_key(A|B) statements for all (key, val) pairs
in dictionary self.mSwitches
        """
        result = string
        for switch in self.mSwitches.items():
            result = self.ParseIf(result, switch)
        return result

    def ParseDocument(self, input_file, output_file):
        """
        ParseDocument(input_file, output_fiel) will replace recursively
        all of IF_key(A|B) statements for all (key, val) pairs
        in dictionary self.mSwitches in the input_file stream and output
        to the output_file stream.
        """
        output=[]
        for line in input_file:
            output.append(self.ParseString(line))

        if input_file == output_file:
            output_file.seek(0)
            output_file.truncate()

        # write to the same file
        for line in output:
            output_file.write(line)

# ------------------------------------------------------------------------
# main program
# ------------------------------------------------------------------------
if __name__ == '__main__':
    argparser = argparse.ArgumentParser(description="Resolve IF_*() macros based on given tags")
    argparser.add_argument('-f', '--file', metavar="FILENAME", type=str, help="read from  given file instead of stdin")
    argparser.add_argument('-i', '--inplace', action="store_true", help="do in-place replacement for the given file instead of echo to stdout")
    argparser.add_argument('--selftest', action="store_true", help="run self test")
    argparser.add_argument('flags', metavar="FLAG", type=str, nargs='*', help="specified flags are set to true")

    args = argparser.parse_args()

    # default list of switches used by the parser
    switches = {
       "MPI": False,
       "OMP": False,
       "CUDA": False,
       "WARNALL": False,
       "DEBUG": False,
       "VALGRIND": False,
       "COVERAGE": False,
       }

    parser = Parser(switches)

    # set the list of switches given on the command line to True
    for flag in args.flags:
        parser.SetSwitch(flag, True)

    if args.selftest:
        sys.exit(0) #TODO implement selftest

    # do parsing

    if not args.file:
       parser.ParseDocument(sys.stdin, sys.stdout)
    else:
       if args.inplace:
          with open(args.file, mode='r+') as fhandle:
             parser.ParseDocument(fhandle, fhandle)
       else:
          with open(args.file, mode='r') as fhandle:
             parser.ParseDocument(fhandle, sys.stdout)
