# 
# Tomar archivo C# generardo por Swagger con modelo de objeto json y lo
# simplifica con uso de record type. 
#
import re

# --- Formato de la función de valida_xxx
# Entrada: tupla con 
#   es_ajustado : True si ha sido ajustado el texto, con lo cual el resto de funciones no tienen que procesarlo.
#   texto : texto a validar
#   flags : lista de strings con flags usados por las funciones de validación para controlara su operación entre líneas.
#       continua - flag que indica que se debe ignorar la linea actual y continuar con la siguiente. 
#
#
def valida_using(es_ajustado: bool, linea: str, flags: [str]) -> (bool, str, [str]):
    """Elimina las líneas con sentencia 'using'
       Activa el flag 'continua' que indica que se debe ignorar la línea \
        (no aparece en el archivo de salida).
    """
    # Si la línea ya fue ajustada, no se procesa mas. 
    if es_ajustado:
        return (es_ajustado, linea, flags)
    
    # \b marca limite de palabra, valida que el patrón no es un fragmento dentro de una palabra mas grande.
    # [] conjunto de caracteres.
    # \s espacio en blanco y  [\t\n\r\f\v]
    # + uno o mas caracteres que le preceden. 
    # \w identifica palabra.
    p = r'^using[\s]+\w+'
    rex = re.compile(p)
    if rex.match(linea):
        print("using identificado.")
        flags.append('continua')
        return (True, xlinea, flags)
    return (False, linea, flags)

def valida_namespace(es_ajustado: bool, linea: str, flags: [str]) -> (bool, str, [str]):
    """Si la linea tiene la definición de namespace, la ajusta.
    """

    # Si la línea ya fue ajustada, no se procesa mas. 
    if es_ajustado:
        return (es_ajustado, linea, flags)
    
    # namespace a aplicar.
    xnamespace = "POSGkSwaggerModel"
    # \b marca limite de palabra, valida que el patrón no es un fragmento dentro de una palabra mas grande.
    # [] conjunto de caracteres.
    # \s espacio en blanco y  [\t\n\r\f\v]
    # + uno o mas caracteres que le preceden. 
    # ( ) indica un grupo dentro de la expresión de búsqueda, contando desde 1 cada grupo.
    p = r'^namespace[\s]+\bIO.Swagger.Model\b'
    rex = re.compile(p)
    if rex.match(linea):
        xlinea = linea.replace("IO.Swagger.Model", xnamespace)
        print("namespace ajustado.")
        return (True, xlinea, flags)
    return (False, linea, flags)

def valida_anotaciones(es_ajustado: bool, linea: str, flags: [str]) -> (bool, str, [str]):
    """ Elimina las lineas que tiene anotaciones tales como: 
         [DataContract]
         [JsonConstructorAttribute]
         [DataMember(Name="businessUnitGroupID", EmitDefaultValue=false)]
        En el caso de las antociones, son la única entrada en la línea, es decir, \
        la línea de texto tiene la forma <blancos>[...texto...]<blancos>
    """
    # Si la línea ya fue ajustada, no se procesa mas. 
    if es_ajustado:
        return (es_ajustado, linea, flags)
    
    # [] conjunto de caracteres.
    # \s espacio en blanco y  [\t\n\r\f\v]
    # * cero uno o mas caracteres que le preceden. 
    # \w identifica palabra.
    # El grupo [^[\]]+ indica todos los caracteres que no son [ ni ]
    p = r'\[[^[\]]+\]'
    rex = re.compile(p)
    if rex.match(linea.strip()):
        print("anotación [...] identificada.")
        flags.append('continua')
        return (True, xlinea, flags)
    return (False, linea, flags)


def valida_clase(es_ajustado: bool, linea: str, flags: [str]) -> (bool, str, [str]):
    """Si la linea tiene la definición de una clase la cambia por definición de un record.
    """

    # Si la línea ya fue ajustada, no se procesa mas. 
    if es_ajustado:
        return (es_ajustado, linea, flags)
    
    # \s espacio en blanco y  [\t\n\r\f\v]
    # + uno o mas caracteres que le preceden. 
    # ( ) indica un grupo dentro de la expresión de búsqueda, contando desde 1 cada grupo.
    # busca coincidencias con patrón "public class" o "public partial class", donde "partial" es opcional.
    p = r'(public\s+(partial)*\s+class)'
    r = "public record"
    rex = re.compile(p)
    if rex.search(linea):
        xlinea = re.sub(p, r, linea)
        print("clase ajustada.")
        return (True, xlinea, flags)
    return (False, linea, flags)

def valida_propiedad(es_ajustado: bool, linea: str, flags: [str]) -> (bool, str, [str]):
    """Si la linea tiene la definición de una propiedad, la ajusta.
       Ejm: 
        Original:
            public string BusinessUnitGroupID { get; set; }
        Ajustada: 
            public string BusinessUnitGroupID { get; init; }

        Activa el flag de 'propiedad' lo que significa que de aquí en adelante, las lineas que 
        no son propiedades serán ignoradas, hasta encontrar el } de cierre de la clase o record.
    """
    # Si la línea ya fue ajustada, no se procesa mas. 
    if es_ajustado:
        return (es_ajustado, linea, flags)

    # \s espacio en blanco y  [\t\n\r\f\v]
    # + uno o mas caracteres que le preceden. 
    # ( ) indica un grupo dentro de la expresión de búsqueda, contando desde 1 cada grupo.
    # busca coincidencias con patrón "public class" o "public partial class", donde "partial" es opcional.
    p = r'{ get; set; }'
    r = '{ get; init; }'
    rex = re.compile(p)
    if rex.search(linea):
        xlinea = re.sub(p, r, linea)
        print("propiedad ajustada.")
        flags.append('propiedad')
        return (True, xlinea, flags)
    return (False, linea, flags)

def continua(flags: [str]) -> bool:
    """Retorna True si la lista de flags incluye 'continua'.
        Elimina el flag de la lista.
    """
    for s in flags:
        if s == 'continua':
            flags.remove('continua')
            return True
    return False

def propiedad(flags: [str]) -> bool:
    """Retorna True si la lista de flags incluye 'propiedad'.
    """
    for f in flags:
        if f == 'propiedad':
            return True
    return False

# 
# --- Cuerpo principal del programa.
#
carpeta_entrada = "C:/jht/scogateway/dotnet/csharp-client-swagger/src/IO.Swagger/Model/"
carpeta_salida = "C:/jht/scogateway/dotnet/api-gateway-chec/gatewayUtilPython/salida/"
archivo = "ComGkSoftwareGkrApiTxpoolDtoTransactionKey.cs"

# Lee archivo en una lista de lineas.
entrada = carpeta_entrada + archivo
print(f"archivo entrada {entrada}")
ptr_archivo = open(entrada, 'r', encoding='utf8')
lineas_txt = ptr_archivo.readlines()
ptr_archivo.close()

# lista de funciones de validacion
validaciones = [valida_namespace, valida_clase, valida_propiedad]

# Recorre lineas del archivo y aplica ajustes.
lineas_txt_ajustadas = []
flags = []
for linea in lineas_txt:
    es_ajustado = False
    for v in validaciones:
        es_ajustado, xlinea, flags = v(False, linea, flags)
        # Si ya una función valido positivamente, las demás no aplican.
        if es_ajustado and not continua(flags):
            # Solo tienen en cuenta las lineas con algun ajuste
            lineas_txt_ajustadas.append(xlinea)
            break
    # # Si está el flag de continua, se ignora la linea y se apaga el flag.
    # if continua(flags):
    #     print("linea ignorada.")
    #     continue

    # # Si flag de propiedad esta activo y no aplica ninguna validación, ignora la linea.
    # if propiedad(flags) and not es_ajustado:
    #     print("linea ignorada.")
    #     continue

    
    

# Escribe archivo salida.
salida = carpeta_salida + archivo
ptr_archivo = open(salida, 'w', encoding='utf8')
ptr_archivo.writelines(lineas_txt_ajustadas)
ptr_archivo.close()
print(f"archivo salida creado {salida}")