import sys
import os
from os import path
from mhelper import file_helper
import re

from pyguisignal import configuration


config = configuration.load()


def main() -> None:
    print( "pyguisignal starts" )
    commands = sys.argv[1:]
    
    # Print help if we don't have everything
    if not commands:
        print( "pyguisignal <path>" )
        print( "    <path> one or more paths to \".ui\" files or folders." )
        print( "    If folders are selected they will be recursively searched for '.ui' and '.qrc' files." )
        raise SystemExit()
    
    # If we only have one command that's a directory we shall scan
    for command in commands:
        command = path.abspath( command ).strip()
        
        if command.endswith( "/." ):
            command = command[:-2]
        
        print( "command: [" + command + "]" )
        
        if path.isdir( command ):
            print( "qrc: " + config.extension_qrc )
            for file in file_helper.list_dir( command, config.extension_qrc, True ):
                process_resources( file )
            
            print( "ui: " + config.extension_ui )
            for file in file_helper.list_dir( command, config.extension_ui, True ):
                process_file( file )
            return
        elif path.isfile( command ):
            print( "file" )
            process_file( command )
        else:
            print( "rejected" )


def run_system( command: str ) -> None:
    result = os.system( command )
    
    if result:
        raise ValueError( "Unhappy result '{0}' from '{1}'".format( result, command ) )


def process_resources( qrc_file ):
    print( "PROCESS RESOURCES FILE: " + file_helper.get_filename( qrc_file ) )
    
    if not path.isfile( qrc_file ):
        raise FileNotFoundError( "The QRC file '{0}' does not exist.".format( qrc_file ) )
    
    # print("----COMPILE: {0}".format(get_filename(qrc_file)))
    out_file = qrc_file[:-len( config.extension_qrc )] + config.extension_qrc_py
    
    command = config.resource_command.format( qrc_file, out_file )
    
    run_system( command )
    
    content = file_helper.read_all_lines( qrc_file )
    
    result = []
    result.append( '"""Resources names. File automatically generated by PyGuiSignal. Modifications may be overwritten."""' )
    result.append( "from mhelper import ResourceIcon" )
    
    for line in content:
        line = line.strip()
        
        if line.startswith( "<file>" ) and line.endswith( "</file>" ):
            line = line[6:-7]
            line = file_helper.get_filename_without_extension( line )
            name = line.replace( "-", "_" )
            result.append( '{} = ResourceIcon( ":{}.svg" )'.format( name, line ) )
    
    file_helper.write_all_text( file_helper.replace_extension( qrc_file, ".py" ), "\n".join( result ) )


#
# Read in the arguments
#
def process_file( ui_file ):
    print( "PROCESS UI FILE: " + file_helper.get_filename( ui_file ) )
    window_file = ui_file[:-len( config.extension_ui )] + config.extension_ui_py
    code_file = ui_file[:-len( config.extension_ui )] + config.extension_ui_logic_py
    window_file = path.join( file_helper.get_directory( window_file ), config.prefix_ui_py + file_helper.get_filename( window_file ) )
    code_file = path.join( file_helper.get_directory( code_file ), config.prefix_ui_logic_py + file_helper.get_filename( code_file ) )
    
    print( "---- QT GUI FILE IS           : " + ui_file )
    print( "---- PYTHON WINDOW FILE IS    : " + window_file )
    print( "---- PYTHON CONTROLLER FILE IS: " + code_file )
    
    if not path.isfile( ui_file ):
        raise FileNotFoundError( "The UI file '{0}' does not exist.".format( ui_file ) )
    
    # Recompile the window file
    # print("----COMPILE: {0}".format(get_filename(ui_file)))
    command = config.ui_command.format( ui_file, window_file )
    
    run_system( command )
    
    # Check the file exists
    if not path.isfile( window_file ):
        raise FileNotFoundError( "The window file '{0}' does not exist.".format( window_file ) )
    
    # Read the input files
    with open( window_file, "r" ) as f:
        orig_py_window_content = f.read()
        py_window_content = orig_py_window_content
    
    if path.isfile( code_file ):
        with open( code_file, "r" ) as f:
            code = f.read()
            orig_code = code
    else:
        code = ""
        orig_code = None
    
    # Get rid of garbage in the GUI file
    for find, replace in config.garbage_regex:
        new_py_window_content = re.sub( find, replace, py_window_content )
        if new_py_window_content != py_window_content:
            print( "REPLACED IN PYTHON WINDOW FILE: '{}'".format( find ) )
            py_window_content = new_py_window_content
    
    # Find our widgets
    matches = re.finditer( config.widget_regex, py_window_content )
    
    new_handlers = []
    
    last_match = -1
    widget_names = set()
    
    for match in matches:
        widget_name = match.group( 1 )
        widget_type = match.group( 2 )
        
        widget_names.add( widget_name )
        
        if widget_type in config.handlers and not widget_name.endswith( "_" ):
            signals = config.handlers[widget_type]
            
            for signal in signals:
                signal_name = signal[0]
                signal_params = signal[1]
                signal_function = config.function_format.format( widget_name, widget_type, signal_name, signal_params )
                
                if signal_params:
                    formatted_params = "self, " + signal_params
                else:
                    formatted_params = "self"
                
                index = code.find( "def " + signal_function )
                found = index != -1
                
                if found:
                    # print( comment + "Okay" )
                    last_match = index
                else:
                    # print( comment + "Missing!" )
                    signal_command = config.command_format.format( widget_name, widget_type, signal_name, signal_params, signal_function, formatted_params )
                    new_handlers.append( signal_command )
    
    # Our last match is at "last_match", so use the indentation of this
    start_index = last_match
    
    if start_index != -1:
        while True:
            start_index -= 1
            if start_index == -1 or (start_index < len( code ) and code[start_index] != " "):
                break
    
    indentation = last_match - start_index - 1
    
    # Indent our handlers
    # print( "Indentation of last handler is " + str( indentation ) )
    
    for index, value in enumerate( new_handlers ):
        new_handlers[index] = " " * indentation + value
        # print( "HANDLER" )
        # print( new_handlers[ index ] )
    
    # Insert after our last function in the code (this is easiest)
    
    # We need to READ forward until we have a line with <= indentation to "indentation"
    
    index = last_match
    current_indent = 9999
    NEW_LINE = 1
    TEXT_CONTENT = 2
    stage = 0  # 1 = new line
    
    while True:
        index += 1
        
        if index == len( code ):
            break
        
        c = code[index]
        
        if c == "\n":
            stage = NEW_LINE
            # print( c + " is NEW LINE" )
            current_indent = 0
        elif stage == NEW_LINE:
            if c == " ":
                current_indent += 1
                # print( c + " INDENT " + str( current_indent ) )
            elif c == "#":
                stage = TEXT_CONTENT
                # print( c + " COMMENT " + str( current_indent ) )
            else:
                stage = TEXT_CONTENT
                # print( c + " CONTENT! " + str( current_indent ) )
                if current_indent <= indentation:
                    # We have our line but we now need to move back to the start of it
                    # print( "EXITING WITH " + str( current_indent ) )
                    while code[index] != "\n":
                        index -= 1
                    break
    
    new_code = "\n".join( new_handlers )
    
    code = code[:index] + new_code + code[index:]
    
    # Find rubbish in the code
    next_line = False
    find_handler_regex = re.compile( "on_(.*)_.*\\(" )
    lines = code.split( "\n" )
    
    for line in lines:
        if next_line:
            widget_name = find_handler_regex.findall( line )
            
            if len( widget_name ) != 0:
                widget_name = widget_name[0]
                if widget_name not in widget_names:
                    print( "WARNING: Bad handler - {}".format( line.strip() ) )
                    
                    if "BAD_HANDLER" not in line:
                        code = code.replace( line, line + " #TODO: BAD_HANDLER - The widget '{}' does not appear in the designer file.".format( widget_name ) )
            
            next_line = False
        elif "exqtSlot" in line or "pyqtSlot" in line:
            next_line = True
    
    if orig_code != code or orig_py_window_content != py_window_content:
        print( "--------Inserting " + str( len( new_handlers ) ) + " handlers at char number " + str( index ) + "." )
        
        # Write our new code
        with open( code_file, "w" ) as f:
            f.write( code )
        
        # Write our new window
        with open( window_file, "w" ) as f:
            f.write( py_window_content )
    else:
        print( "--------Nothing changed." )


if __name__ == "__main__":
    main()
