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

# This file is part of Pymetrick.  The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.

"""Modulo de gestion de rutas"""

try:
    from pymetrick.version import __author__, __version__, __license__, __copyright__
except ImportError:
    raise ImportError(
        'Failed to find (autogenerated) version.py '
    )

__date__ = '2012-09-21'
__credits__ = ''
__text__ = 'Gestion de rutas'
__file__ = 'routing.py'

#--- CHANGES ------------------------------------------------------------------
# 2012-09-21 v0.01 PL: - First version
# 2017-10-02 v0.43 PL: - Migrar a PYTHON 3.6


"""
    He aquí un ejemplo simple que crea un mapa de URL para una aplicación con
    dos subdominios (www y kb) y algunas reglas URL:

    >>> m = Map([
    ...     # Static URLs
    ...     ('/', endpoint='static/index'),
    ...     ('/about', endpoint='static/about'),
    ...     ('/help', endpoint='static/hcd
    ...     Subdomain('kb', [
    ...         Rule('/', endpoint='kb/index'),
    ...         Rule('/browse/', endpoint='kb/browse'),
    ...         Rule('/browse/<int:id>/', endpoint='kb/browse'),
    ...         Rule('/browse/<int:id>/<int:page>', endpoint='kb/browse')
    ...     ])
    ... ], default_subdomain='www')



"""
import sys
# sin bytecode
sys.dont_write_bytecode = True
import os
import re
import logging
import logging.handlers

# PY3
long = int

'''
DEBUG - debug message
INFO - info message
WARNING - warn message
ERROR - error message
CRITICAL - critical message
'''
if str(os.environ.get('PYMETRICK_LOG_LEVEL',None)).upper() in ('DEBUG','INFO','WARNING','ERROR','CRITICAL'):
    LOG_LEVEL = eval('.'.join(['logging',str(os.environ.get('PYMETRICK_LOG_LEVEL')).upper()]))
else:
    LOG_LEVEL = eval('logging.WARNING')
LOG_FILENAME = '-'.join([os.path.abspath(__file__).split(os.sep)[len(os.path.abspath(__file__).split(os.sep))-1],])[:-3]
LOG = logging.getLogger(LOG_FILENAME)

if 'LD_LIBRARY_PATH' in list(os.environ.keys()):
    # CGI environment
    sys.stdout = sys.stderr
    logging.basicConfig(stream = sys.stderr, level=LOG_LEVEL, format='%(filename)s[line:%(lineno)d] - %(levelname)s - %(message)s')
else:
    # not CGI environment
    logging.basicConfig(stream=sys.stderr)
    hdlr = logging.handlers.RotatingFileHandler(filename=LOG_FILENAME+'.log',mode='a', encoding='utf-8', maxBytes=1048576, backupCount=3)
    formatter = logging.Formatter('%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s - %(message)s')
    hdlr.setFormatter(formatter)
    LOG.addHandler(hdlr)
    LOG.setLevel(LOG_LEVEL)

class Map(object):
    """Map relaciona las rutas con los controladores a ejecutar.
    existen dos parametros esenciales :
    default_subdomain : Dominio por defecto www
    redirect_default  : Cuando no existe la ruta solicitada, se redirige a una ruta por defecto.
    url_map = Map([
                ('/', subdomain='{username}', endpoint='user/homepage'),
                ('/stats', subdomain='{username}', endpoint='user/stats')
            ])
    Para incorporar nuevas rutas utiliza add, si una ruta ya existiera, se sustituira por la incorporada.
    add('/ideas/{idea}', controller = 'idea')
    add('/users/{usuario}', controller = 'user')
    add('/tags/{etiquetas}', controller = 'tag')

    _rules  es el conjunto de rutas de validacion, cada elemento de _rules se compone de una ruta y un controlador
            Cada ruta puede ser estatica p.e. : /login o dinámica p.e. : /articulos/{año}/{dia}
    """

    def __init__(self, default_subdomain='', redirect_defaults=True):
        try:
            self._rules = {}
            self.default_subdomain = default_subdomain
            self.redirect_defaults = redirect_defaults
        except Exception as e:
            tb = sys.exc_info()[2]
            LOG.error('Error en add <%s> en linea %s !!!' % (repr(e),tb.tb_lineno))


    def add(self, rule='', controller=''):
        """Incorporar una nueva ruta dirigiendo la accion a un controlador
        _rules  reglas de validacion

        """
        try:
            if rule and controller:
                """Incorporar nueva regla o sustituir si existe"""
                self._rules[rule]=controller
        except Exception as e:
            tb = sys.exc_info()[2]
            LOG.error('Error en add <%s> en linea %s !!!' % (repr(e),tb.tb_lineno))

    def match(self, path):
        """Compruebe si la regla coincide con una ruta determinada"""

        _match = []
        _match_number = 0
        # Comprobar si la ruta contiene datos del tipo ?nombre_variable=dato y extrae solo informacion de ruta
        if re.search(r'\?',path):
            path = path[:path.find(r"?")]
        # Si es un path dinamico debemos comprobarlo minuciosamente
        _dict_dinamico = dict()
        try:

            _path = path.strip("/").split("/")
            for n,m in list(self._rules.items()):
                _n = n.strip("/").split("/")
                _match_number = 0
                for i in range(0,len(_n)):
                    if len(_path)>=i+1:
                        if _n[i].startswith('{') and _n[i].endswith('}') and len(_path)>=i+1:
                            # Los parametros en la ruta aparecen entre {}
                            # crear un diccionario emparejando parametros con valor
                            _dict_dinamico[_n[i].strip("{}")]=_path[i]
                            _match_number +=1
                        elif _path[i]==_n[i]:
                            # Cuenta coincidencias entre ruta y valor
                            _match_number += 1
                        if len(_path)==_match_number and _match_number>0:
                            if len(_path)==_match_number and len(_n)==_match_number:
                                # Prepara resultado : ruta, controlador, coincidencia
                                if len(_dict_dinamico)>0:
                                    # Crear la funcion con parametros
                                    m = "%s(**%s)" % (m,_dict_dinamico)
                                _match.append((n,m,_match_number))

            if not _match and self.redirect_defaults:
                """404	NOT FOUND. El servidor no halló nada en la dirección especificada"""
                """Devuelve resultado como ruta, controlador y coincidencia"""
                _match.append(('/','404',0))
            """Devuelve solo la funcion a evaluar"""
            return _match[0][1]
        except Exception as e:
            tb = sys.exc_info()[2]
            LOG.error('Error en Map.match <%s> en linea %s !!!' % (repr(e),tb.tb_lineno))

    def __del__(self):
        '''Eliminar objeto y objetos dependientes'''
        LOG.debug("del ha eliminado el objeto")

if __name__ == "__main__":
    print ('''copyright {0}'''.format( __copyright__))
    print ('''license {0}'''.format( __license__))
    print ('''version {0}'''.format( __version__))
    if len(sys.argv) < 2:
        sys.stderr.write("For help use -h o --help")
    elif sys.argv[1]=='-h' or sys.argv[1]=='--help':
        print ('''
        from pymetrick.routing import *

        Primero debe crear una ruta al subdominio de la siguiente forma :

        rules_web = Map(default_subdomain='www',redirect_defaults='/')

        Las rutas a páginas se crean de la siguiente forma :

        rules_web.add(rule='/login',controller='login')
        rules_web.add(rule='/login/nada',controller='login')
        rules_web.add(rule='/login/nada/todo',controller='login')
        rules_web.add(rule='/registro',controller='registro')
        rules_web.add(rule='/prueba',controller='prueba')

        Cuando busque una ruta debe indicarlo como :

        rules_web.match('login')
        rules_web.match('/login/nada/todo')
        rules_web.match('/varios')

        esto ejecutara el controlador parametrizado.

        Si la ruta obtenida del cliente, contiene datos del tipo
        '/auth?user=javier&passw=tonto'
        se eliminaran para evaluar correctamente la ruta

        Y si no existe la ruta, devolvera 404 y podremos redirigirlo
        if rules_web.match('/no_existe')=="404":
           rules_web.match('/')
        ''')
