#!/usr/bin/env python
# -*- coding: utf-8 -*-

from os.path import dirname as nombre_directorio
from os.path import abspath as path_absoluto
from os.path import exists as path_existe
from re import compile as RECompile
from re import MULTILINE
import datetime
from json import loads as json_decodificar

class PaisesMundoContinentes:

    __path_raiz = nombre_directorio(path_absoluto(__file__))
    __configuracion_por_defecto = {
        "archivo_paises_continentes" : "/Data/continent.json", 
        "archivo_paises_mundo" : "/Data/World map.svg", 
        "archivo_respuesta" : __path_raiz + "/../Data/world-map-continented.svg", 
        "autoiniciar" : True, 
        "formato_terminal" : "[{tipo}] {yyyy}{mm}{dd} {hh}{ii}{ss}: {mensaje}", 
        "texto_por_defecto" : ""
    }
    __es_dos = '\\' in __path_raiz
    __slash = '\\' if __es_dos else '/'

    re_arreglar_path = RECompile(r'[\\\\\/]+')
    re_string_variables = RECompile(r'\{([^\{\}]+)\}')
    re_id_paises = RECompile(r'^\t<(path|g) id="([^"]+)"', MULTILINE)

    def __init__(self, entradas = None):
        
        self.__configuracion = {}
        self.__entradas = entradas
        self.__tipos_terminal = [
            ["unkn", "unknown", "desc", "desconocido"], 
            ["info", "information", "informacion"], 
            [" ok ", "ok", "yes", "y", "si", "s"], 
            ["warn", "warning", "avis", "aviso"], 
            ["erro", "error", "no", "n", "wrong", "bad"], 
            ["exce", "exception", "excepcion"]
        ]
        self.__textos = {
            "espanol" : {
                "pmc_construyendo" : "La herramienta 'PaisesMundoContinentes' se está construyendo...", 
                "pmc_construido" : "La herramienta 'PaisesMundoContinentes' se construyó completamente.", 
                "pmc_iniciando" : "La herramienta 'PaisesMundoContinentes' se está iniciando...", 
                "pmc_iniciado" : "La herramienta 'PaisesMundoContinentes' se inició completamente."
            }
        }
        self.__idioma = "espanol"
        self.__idioma_por_defecto = "espanol"
        self.__formato_terminal = self.configuracion("formato_terminal")

        self.imprimir("info", "pmc_construyendo")

        self.__paths_raiz = ["", self.__path_raiz + "/..", self.__path_raiz]
        self.__iniciado = False

        self.imprimir("ok", "pmc_construido")

        self.configuracion("autoiniciar") and self.iniciar()

    def iniciar(self, callback = None):

        self.imprimir("info", "pmc_iniciando")

        def terminar(estado):
            self.imprimir(*(("ok", "pmc_iniciado") if estado else ("warn", "pmc_ya_iniciado")))
            callable(callback) and callback(estado)

        if self.__iniciado:
            terminar(False)
            return False
        self.__iniciado = True

        terminar(True)

        diccionario_paises_continentes = self.leer_archivo(self.configuracion("archivo_paises_continentes"))
        paises_mundo = self.leer_archivo(self.configuracion("archivo_paises_mundo"))

        try:
            diccionario_paises_continentes = json_decodificar(diccionario_paises_continentes)
        except:
            diccionario_paises_continentes = None

        if diccionario_paises_continentes != None:

            def callback(matches):

                codigo = matches.group(2).upper()
                fragmento = matches.group(0)
                
                if codigo in diccionario_paises_continentes:
                    fragmento += ' data-continente="' + diccionario_paises_continentes[codigo] + '"'

                codigo == "NZ" and print(fragmento)

                return fragmento

            self.escribir_archivo(self.configuracion("archivo_respuesta"), self.re_id_paises.sub(callback, paises_mundo))

        return True

    def cerrar(self, callback = None):

        self.imprimir("info", "pmc_cerrando")

        def terminar(estado):
            self.imprimir(*(("ok", "pmc_cerrado") if estado else ("warn", "pmc_ya_cerrado")))
            callable(callback) and callback(estado)

        if not self.__iniciado:
            terminar(False)
            return False
        self.__iniciado = False

        terminar(True)

        return True

    def nulos(self, nulos = None):
        return nulos if isinstance(nulos, bool) else self.configuracion("nulos", None, False, False)
    
    def por_defecto(self, por_defecto = None, nulos = None):
        return por_defecto if por_defecto != None or self.nulos(nulos) else self.configuracion("por_defecto", None, None, True)

    def configuracion(self, claves, entradas = None, por_defecto = None, nulos = None):

        claves = [clave.strip() for clave in (claves if isinstance(claves, (list, tuple)) else [claves]) if isinstance(clave, str)]
        claves = [clave for i, clave in enumerate(claves) if clave and clave not in claves[:i]]

        if len(claves):

            nulos = self.nulos(nulos)
            
            for entrada in (
                list(entradas) if isinstance(entradas, (list, tuple)) else [entradas]
            ) + [self.__entradas, self.__configuracion, self.__configuracion_por_defecto]:
                if isinstance(entrada, dict):
                    for clave in claves:
                        if clave in entrada and (nulos or entrada[clave] != None):
                            return entrada[clave]
        return self.por_defecto(por_defecto, nulos)
    
    @classmethod
    def string_variables(self, string, variables = None, por_defecto = None):

        variables = [conjunto for conjunto in (
            variables if isinstance(variables, (list, tuple)) else [variables]
        ) if isinstance(conjunto, dict)]

        def callback(matches):
            clave = matches.group(1)
            for conjunto in variables:
                if clave in conjunto:
                    return str(conjunto[clave])
            return por_defecto if por_defecto != None else matches.group(0)

        return self.re_string_variables.sub(callback, string)
    
    def texto_por_defecto(self, por_defecto = None):
        return str(por_defecto if isinstance(por_defecto, str) else self.configuracion("texto_por_defecto"))
    
    def __coger_i18n(self, claves, por_defecto):

        if not isinstance(claves, (list, tuple)):
            claves = [claves]
        claves = [clave.strip() for clave in claves if isinstance(clave, str)]
        claves = [clave for i, clave in enumerate(claves) if clave not in claves[:i]]

        if len(claves):

            idiomas = [self.__idioma, self.__idioma_por_defecto] + list(self.__textos.keys())

            for i, idioma in enumerate(idiomas):
                if idioma and idioma not in idiomas[:i] and isinstance(idioma, str) and idioma in self.__textos:
                    for clave in claves:
                        if clave in self.__textos[idioma]:
                            return str(self.__textos[idioma][clave])
            return claves[0]
        return self.texto_por_defecto(por_defecto)
    
    def i18n(self, claves, variables = None, por_defecto = None):
        return self.string_variables(self.__coger_i18n(claves, por_defecto), variables, por_defecto)
    
    def coger_tipo_terminal(self, tipo):

        tipo = tipo.lower()

        for conjunto in self.__tipos_terminal:
            if tipo in conjunto:
                return conjunto[0]
        return conjunto[0][0]
    
    def imprimir(self, tipo, mensaje, variables = None):

        conjunto = {
            **(
                variables if isinstance(variables, dict) else 
                {clave : valor for conjunto in [
                    conjunto for conjunto in variables if isinstance(conjunto, dict)
                ] for clave, valor in conjunto.items()} if isinstance(variables, (list, tuple)) else 
            {}), 
            "tipo" : self.coger_tipo_terminal(tipo).upper(), 
            "tipo_crudo" : tipo, 
            "i18n" : mensaje
        }
        fecha = datetime.datetime.now()

        for clave in ("year", "month", "day", "hour", "minute", "second"):

            k = clave[0] if clave != "minute" else "i"

            conjunto[clave] = getattr(fecha, clave)
            conjunto[k] = conjunto[clave] / 100
            conjunto[k + k] = ("00" + str(conjunto[k]))[-2:]
        
        conjunto["yyyy"] = conjunto["year"]
        conjunto["mensaje"] = self.i18n(mensaje, conjunto)

        print(self.string_variables(self.__formato_terminal, conjunto))
    
    @classmethod
    def arreglar_path(self, path):
        return self.re_arreglar_path.sub(self.__slash, path)
    
    def coger_path_absoluto(self, path):
        for path_raiz in self.__paths_raiz:
            path_absoluto = self.arreglar_path(path_raiz + self.__slash + path)
            if path_existe(path_absoluto):
                return path_absoluto
        return None

    def leer_archivo(self, path):
        path = self.coger_path_absoluto(path)
        if path:
            try:
                with open(path, 'r') as archivo:
                    return archivo.read()
            except Exception as excepcion:
                pass
        return None
    
    def escribir_archivo(self, path, contenido):
        try:
            with open(path, 'w') as archivo:
                archivo.write(contenido)
        except Exception as excepcion:
            pass

paises_mundo_continentes = PaisesMundoContinentes()