# -*- coding: utf-8 -*-
'''
Gestor de nodos para LaOtraRed La Paz - El Alto

 Copyright (C) 2017  Rodrigo Garcia

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as
 published by the Free Software Foundation, either version 3 of the
 License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU Affero General Public License for more details.

 You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
'''
import datetime, time
from collections import deque

from flask import render_template, jsonify, abort, Markup, escape, request #, make_response
from flask import Blueprint
from flask import flash, g, session
from flask import redirect, send_from_directory, safe_join, send_file
from flask import url_for

from werkzeug import secure_filename

from werkzeug import secure_filename
from jinja2 import Environment, PackageLoader

from gestor.main import app
from gestor import configs

from database.database import init_db, db_session
from database.database import engine
from sqlalchemy.exc import IntegrityError
from .models import Nodo, Ubicacion

from gestor import utils
from gestor import utils_red
from gestor import utils_firmware

gestor = Blueprint("gestor", __name__, url_prefix='')

@gestor.before_request
def before_request():
    g.nodo = None
    if 'nodo_id' in session:
        g.nodo = Nodo.query.get(session['nodo_id'])
        print ("sesion: "+str(g.nodo))

    g.estado_construccion = 'No'

@gestor.route('/')
def hello_world():
    return  render_template("index.html", p_actual="inicio", \
                            nodo=session.get('nodo_id', None))
    #return "DATABASE = "+str(configs.DATABASE_NAME)+"\n"+\
        #"DB: "+str(engine)

@gestor.route('/agregar', methods=['GET'])
def get_agregar_nodos(errors=None):
    contexto = {}
    contexto['modelos_posibles'] = utils.listas_routers()
    if errors:
        return render_template("agregar_nodo.html", p_actual="agregar",\
                               contexto=contexto, errors=errors,
                               nodo=session.get('nodo_id', None))

    return render_template("agregar_nodo.html", p_actual="agregar",\
                           contexto=contexto,\
                           nodo=session.get('nodo_id', None))

@gestor.route('/nuevo', methods=['POST'])
def post_agregar_nodos():
    if request.method == "POST":
        errors = utils.comprobar_postAgregarNodo(request, errores_detalle=True)
        
        # comprobando nuevo nodo en la base de datos
        #nodo_similar = utils.comprobar_responsableNodo(request)
        nodo_similar = utils.comprobar_nodoSimilar(request)
        #if nodo_similar is not None:
        if nodo_similar != False:
            errors['nodo_similar'] = u"Un nodo tiene registrado el email <"+\
                                     str(request.form['email'].lower())+\
                                         "> y el nombre, apellido son distintos a "+\
                                         " los que has introducido."
            errors['id_nodo_similar'] = str(nodo_similar)
        
        if len(errors) > 0:
            contexto = {}
            contexto['modelos_posibles'] = utils.listas_routers()

            contexto['errors'] = errors
            print ("Errores en formulario: "+str(errors))
            return (get_agregar_nodos(errors=errors))
            return render_template("agregar_nodo.html", p_actual="agregar",\
                                   contexto=contexto, errors=errors, \
                                   nodo=session.get('nodo_id', None) )
        
        # insertando en la base de datos
        db_session.rollback()
        
        compartir_ubicacion = request.form.get("compartir_ubicacion")

        # creando nodo con ubicacion dummy
        if compartir_ubicacion == "SI":
            id_ubicacion_dummy = utils.crear_ubicacionDummy(is_public=True)
        elif compartir_ubicacion == "NO":
            id_ubicacion_dummy = utils.crear_ubicacionDummy(is_public=False)

        # obteniendo bloques de red
        cidr_publica = utils_red.get_cidrIpV4(request.form['ips_publicas'])
        cidr_privada = utils_red.get_cidrIpV4(request.form['ips_privadas'])

        bloque_ipv4_publico = utils_red.\
                              siguiente_bloqueIpv4Disponible(cidr_publica)
        bloque_ipv4_privado = utils_red.\
                              siguiente_bloqueIpv4Disponible(cidr_privada, publico=False)

        bloque_ipv6 = utils_red.siguiente_bloqueIpv6Disponible()
        
        clave_edicion = utils.hash_claveEdicion(request.form['clave'])
    
        nombre_nodo = request.form.get('nombre_nodo', bloque_ipv4_publico)
        fecha_actual = datetime.datetime.now().\
                       strftime("%Y-%m-%d %H:%M:%S")

        # creacion de nuevo registro
        nodo = Nodo(ubicacion_id = id_ubicacion_dummy,\
                    nombre = nombre_nodo,\
                    descripcion = request.form['descripcion'],\
                    clave_edicion = clave_edicion,\
                    
                    bloque_ipv4 = bloque_ipv4_publico+"/"+str(cidr_publica) , \
                    ipv4_network_addr = bloque_ipv4_publico, \
                    ipv4_cidr = cidr_publica,\
                    bloque_ipv4_privado = bloque_ipv4_privado, \
                    ipv4_cidr_privado = cidr_privada, \

                    bloque_ipv6 = bloque_ipv6,\
                    ipv6_network_addr = bloque_ipv6.split("/")[0],\
                    ipv6_cidr = 128,\

                    url_descarga=utils.get_randomToken(), \
                    
                    nombres_responsable = request.form['nombres'],\
                    apellidos_responsable = request.form['apellidos'],\
                    email = request.form['email'],\
                    fecha_creacion = fecha_actual
                )

        try:
            db_session.add(nodo)
            db_session.commit()
        except IntegrityError:
            print ('"IntegrityError" con "Nodo" detectado')
            return u"Error Interno, datos duplicados en la base de datos"

        # generacion de firmware
        #TODO: Antes de comenzar a genera la imagen de firmware,
        # mostrar en la pagina actual una notificacion (javascript) de que
        # se esta procesando la peticion y que se redijira automaticamente
        # cuando el proceso termine y tambien se enviara una notificacion
        # por email con el directorio para descarga del firmware.

        # marcando variable que indica estado de construccion
        g.estado_construccion = 'construyendo'
        
        r = utils_firmware.\
            crear_imagenFirmware(request.form['modelos'],\
                                 bloque_ipv4_publico,
                                 cidr_publica
            )
            
        if r == "Error":
            return render_template("generando_firmware.html", error = "1", \
                                   nombre_nodo=nombre_nodo,\
                                   email=request.form['email'],\
                                   bloque_ipv4=bloque_ipv4_publico+"/"+str(cidr_publica),\
                                   modelo_enrutador=request.form["modelos"],\
                                       
                                   nombres_responsable = request.form['nombres'],\
                                   apellidos_responsable = request.form['apellidos'],\
                                   #TODO: quitar lo siguiente
                                   resultado = r,\
                                   ## 
                                   nodo=session.get('nodo_id', None),\
                                   p_actual="nodos"
                               )
        else:
            #TODO: enviar email indicando que el proceso ha terminado
            #...
            # enviar senyal ajax indicando que el proceso ha terminado
            #...
            g.estado_construccion = 'terminado'

            time.sleep(10)
            
            return render_template("generando_firmware.html",
                                   nombre_nodo=nombre_nodo,\
                                   email=request.form['email'],\
                                   bloque_ipv4=bloque_ipv4_publico+"/"+str(cidr_publica),\
                                   modelo_enrutador=request.form["modelos"],\
                                       
                                   nombres_responsable = request.form['nombres'],\
                                   apellidos_responsable = request.form['apellidos'],\

                                   url_descarga = nodo.url_descarga,\
                                   #TODO: quitar lo siguiente
                                   resultado = r, \
                                   ##
                                   nodo=session.get('nodo_id', None)
                               )
            
    return "no autorizado"
    
@gestor.route('/nodos', methods=['GET'])
def lista_nodos():
    lista_activos = deque([])
    lista_proyectados = deque([])
    lista = []
    for nodo in db_session.query(Nodo).order_by(Nodo.id).all():
        dict = {}
        dict['id'] = nodo.id
        dict['nombre'] = nodo.nombre
        dict['bloque_ipv4'] = nodo.bloque_ipv4
        dict['bloque_ipv6'] = nodo.bloque_ipv6
        dict['descripcion'] = nodo.descripcion
        dict['ubicacion_id'] = nodo.ubicacion_id

        dict['tupla_ubicacion'] = utils.obtener_ubicacionResumida(nodo.ubicacion_id)
        if nodo.is_active == True:
            lista_activos.append(dict)
        else:
            lista_proyectados.append(dict)
    
    for row in db_session.query(Nodo).all():
        lista.append(str(row))
    for row in db_session.query(Ubicacion).all():
        lista.append(str(row))

    flash("Obteniendo lista", 'warning')
    return render_template("nodos.html", p_actual="nodos",\
                           rows=lista, \
                           lista_proyectados=lista_proyectados,\
                           lista_activos=lista_activos,
                           num_activos=utils.num_nodosActivos(),\
                           num_proyectados=utils.num_nodosProyectados(), \
                           nodo=session.get('nodo_id', None))

@gestor.route('/nodos/<nodo_id>', methods=['GET'])
def info_nodo(nodo_id):
    if not nodo_id.isdigit():
        return render_template("404.html"),  404

    nodo = db_session.query(Nodo).\
           filter(Nodo.id == int(nodo_id)).first()
    if nodo is None:
        return render_template("404.html"), 404
    
    ubicacion = utils.obtener_ubicacionDetallada(nodo.ubicacion_id)
    return render_template("nodo.html",\
                           nombre = nodo.nombre, \
                           bloque_ipv4 = nodo.bloque_ipv4, \
                           ipv4_network_addr = nodo.ipv4_network_addr,\
                           ipv4_cidr = nodo.ipv4_cidr,\
                           bloque_ipv6 = nodo.bloque_ipv6,\
                           ipv6_network_addr = nodo.ipv6_network_addr, \
                           is_active = nodo.is_active,\
                           is_confirmed  =  nodo.is_confirmed, \
                           url_descarga = nodo.url_descarga,\
                           fecha_creacion  =  nodo.fecha_creacion, \
                           descripcion = nodo.descripcion, \
                           nombres = nodo.nombres_responsable, \
                           apellidos = nodo.apellidos_responsable, \
                           email = nodo.email, \
                           telf = nodo.telf,
                           ubicacion = ubicacion, \
                           nodo=session.get('nodo_id', None)
                           )

@gestor.route('/descargas/<url>', methods  = ['GET'])
def descargas_firmware(url):

    # probando que exista un nodo con la url asociada
    nodo = db_session.query(Nodo).\
           filter(Nodo.url_descarga == url).first()
    if nodo is None:
        return render_template("404.html") , 404

    if not g.nodo:
        # password de edicion
        return render_template("descarga.html",\
                               nodo=session.get('nodo_id', None),\
                               nodo_id=nodo.id,\
                               nombre=nodo.nombre,\
                               url=url)
    else:
        # comprobando que la sesion iniciada sea correspondiente a este nodo
        if g.nodo.url_descarga != url:
            return render_template("descarga.html",\
                                   incorrecto=True,\
                                   nodo=None,\
                                   nodo_id=nodo.id,\
                                   nombre=nodo.nombre,\
                                   url=url)
            
        # retornar template con los archivos para descargar
        carpetas = utils_firmware.carpetas_descargaFirmware(nodo.bloque_ipv4)

        return render_template("descarga.html",\
                               nodo=session['nodo_id'],\
                               nodo_id=nodo.id,\
                               nombre=nodo.nombre,\
                               url=url,
                               carpetas=carpetas)
        
    return "Falta implementar"

@gestor.route('/descargas/<url>/<carpeta>', methods=['GET'])
def descargas_firmwareCarpetas(url, carpeta=None):
    nodo = db_session.query(Nodo).\
           filter(Nodo.url_descarga == url).first()
    if nodo is None:
        return render_template("404.html") , 404

    if not g.nodo:
        # password de edicion
        return render_template("descarga.html",\
                               nodo=session.get('nodo_id', None),\
                               nodo_id=nodo.id,\
                               nombre=nodo.nombre,\
                               url=url)
    else:
        # comprobando que la sesion iniciada sea correspondiente a este nodo
        if g.nodo.url_descarga != url:
            return render_template("descarga.html",\
                                   incorrecto=True,\
                                   nodo=None,\
                                   nodo_id=nodo.id,\
                                   nombre=nodo.nombre,\
                                   url=url)

        l = utils_firmware.listar_carpetaFirmware(carpeta=carpeta)
        dirs = l[0]
        archs = l[1]

        return render_template("descarga.html",\
                               nodo=session['nodo_id'],\
                               nodo_id=nodo.id,\
                               nombre=nodo.nombre,\
                               url=url,\
                               carpeta=carpeta,\
                               directorios=dirs,\
                               archivos=archs)

@gestor.route('/descargas/<url>/login', methods=['POST'])
def login_descargas_firmware(url):
    nodo = db_session.query(Nodo).\
           filter(Nodo.url_descarga == url).first()
    if nodo is None:
        return render_template("404.html") , 404
    
    if g.nodo and session['nodo_id'] == nodo.id:
        print ("ruta:"+url_for('gestor.descargas_firmware', url=url))
        return redirect(url_for('gestor.descargas_firmware', url=url))
    else:
        # comprobando clave de edicion
        if utils.hash_claveEdicion(request.form['password']) == \
           nodo.clave_edicion:
            print ("--- Descarga de firmware ---")
            print ("nodo:"+str(nodo))
            print ("clave de edicion correcta :)")
            print ("ruta:"+url_for('gestor.descargas_firmware',url=url))
            print ("----------------------------")
            session['nodo_id'] = nodo.id
            return redirect(url_for("gestor.descargas_firmware",url=url))
        else:
            print ("--- Descarga de firmware ---")
            print ("nodo:"+str(nodo))
            print ("clave de edicion INCORRECTA")
            print ("ruta:"+url_for('gestor.descargas_firmware',url=url))
            print ("----------------------------")
            return redirect(url_for("gestor.descargas_firmware",url=url))

@gestor.route('/logout', methods=['GET'])
def logout_descargas_firmware():
    session.pop('nodo_id', None)
    return redirect(url_for('gestor.hello_world'))

# respuestas con json
@gestor.route('/nuevo/_construccion')
def estado_construccion():
    print
    print ("AJAX construccion")
    print
    return jsonify(estado=g.estado_construccion)

@gestor.route('/_cons')
def prueba_json():
    a = 123+321
    return jsonify(r=a)

# errores
@gestor.errorhandler(404)
def page_not_found(error):
    return render_template( "404.html" ), 404
