#!/usr/bin/python
# -*- coding: Latin-1 -*-
#
# Created on March 21 2016 by Jim Kornelsen.
#
# 22-Feb-17 JDK  Remove final blank line to keep pylint happy.
# 15-Jun-20 JDK  Allow script to run from different directory.

"""
Parse data from dialog definition files.
Used to generate lingt/ui/common/dlgdefs.py.
"""
from collections import namedtuple
import datetime
import os
import re

FILEPATH = os.path.realpath(__file__)
CURRENT_DIR = os.path.dirname(FILEPATH)
INFOLDER_PATH = os.path.join(CURRENT_DIR, "../../LingToolsBasic")
OUTFILE = os.path.join(CURRENT_DIR, "out/dlgdefs.py")

FileTuple = namedtuple('FileTuple', ['path', 'name'])

class FolderReader:
    def __init__(self):
        # keys dialog name, values list of control names
        self.dialog_control_names = {}

    def read_folder(self):
        try:
            self.traverse_folder_and_handle_files()
        except (OSError, IOError):
            print("Couldn't open folder for reading: %s", INFOLDER_PATH)
            exit()
        return self.dialog_control_names

    def traverse_folder_and_handle_files(self):
        for filename in os.listdir(INFOLDER_PATH):
            filepath = os.path.join(INFOLDER_PATH, filename)
            if os.path.isfile(filepath) and filename.endswith(".xdl"):
                self.handle_file(FileTuple(filepath, filename))

    def handle_file(self, filetuple):
        dialog_name = os.path.splitext(filetuple.name)[0]
        fileReader = FileReader(filetuple)
        self.dialog_control_names[dialog_name] = fileReader.readFile()


class LineSearcher:
    """Quick Perl-style pattern match of a line,
    to make a series of elif statements easier when parsing a file.
    """
    def __init__(self):
        self.matchObj = None
        self.line = ""

    def searchLine(self, pattern):
        """Sets self.matchObj"""
        self.matchObj = re.search(pattern, self.line)
        if self.matchObj:
            return True
        return False


class FileReader(LineSearcher):
    def __init__(self, filetuple):
        LineSearcher.__init__(self)
        self.filetuple = filetuple
        self.control_names = []

    def readFile(self):
        try:
            with open(self.filetuple.path, 'r') as infile:
                for self.line in infile:
                    self.parseLine()
        except (OSError, IOError):
            print("Couldn't open file for reading: %s", self.filetuple.path)
            exit()
        return self.control_names

    def parseLine(self):
        if self.searchLine(r'^<dlg:window'):
            return
        if self.searchLine(r'dlg:id="([^"]+)"'):
            self.control_names.append(self.matchObj.group(1))


def tabSpaces(numTabs):
    TABWIDTH = 4  # following PEP8 style guide
    numSpaces = TABWIDTH * numTabs
    return ' ' * numSpaces

class FileWriter:
    """Write results to file"""
    CTRL_NAMES_TO_SKIP = []

    def __init__(self):
        self.outfile = None
        self.numTabs = 3  # how far to indent

    def write_file(self, dialog_control_names):
        try:
            with open(OUTFILE, 'w') as self.outfile:
                self.write_header()
                self.write_classes(dialog_control_names)
        except (OSError, IOError):
            print("Couldn't open file for writing: %s" % OUTFILE)
            exit()

    def write_header(self):
        timestamp = datetime.datetime.now()
        filepath = os.path.relpath(FILEPATH, start="../..")
        self.outfile.write(
            """# -*- coding: Latin-1 -*-
#
# Last Generated: %s
# Generated By:   %s
#
# **IT IS NOT RECOMMENDED TO MAKE CHANGES DIRECTLY TO THIS FILE.**
# Instead, modify the generating script and then re-run it.

""" % (timestamp, filepath))

    def write_classes(self, dlg_ctrl_names):
        #self.indent(self.START_INDENT + 0)
        first_class = True
        for dlg, ctrl_names in sorted(dlg_ctrl_names.items()):
            if first_class:
                first_class = False
            else:
                self.outfile.write("\n")
            self.outfile.write("class %s:\n" % dlg)
            self.write_ctrl_names(ctrl_names)

    def write_ctrl_names(self, ctrl_names):
        for ctrl_name in ctrl_names:
            if ctrl_name in self.CTRL_NAMES_TO_SKIP:
                continue
            var_name = get_var_name(ctrl_name)
            self.indent(1)
            self.outfile.write('%s = "%s"\n' % (var_name, ctrl_name))

    def indent(self, numTabs):
        self.outfile.write(tabSpaces(numTabs))

def get_var_name(ctrl_name):
    var_name = ''
    for i, c in enumerate(ctrl_name):
        var_name += c.upper()
        if needs_underscore(ctrl_name, i):
            var_name += "_"
    return var_name

def needs_underscore(ctrl_name, i):
    if i >= len(ctrl_name) - 1:
        # Variables do not need to end in an underscore.
        return False
    this_char = ctrl_name[i]
    next_char = ctrl_name[i + 1]
    if this_char.islower() and next_char.isupper() and this_char != "_":
        return True
    return False


if __name__ == "__main__":
    FileWriter().write_file(
        FolderReader().read_folder())
    print("Finished!")

