from tkinter import *
from datetime import datetime, timedelta
import tkinter as tk
from tkinter import Entry, Label, StringVar, ttk, Checkbutton, Button, messagebox
import numpy as np
import pandas as pd


def generarCodigo(texto):
    sumar = 0
    codigo = texto[:3]
    if texto[len(texto) // 2] == " ":
        sumar = 1
    codigo += texto[len(texto) // 2 + sumar : len(texto) // 2 + 2 + sumar]
    codigo += texto[len(texto) - 1]
    codigo += str(len(texto))
    return codigo


def moda(lista):
    repetido = lista[0]
    for i in lista:
        if lista.count(i) > lista.count(repetido):
            repetido = i
    return repetido


def nombreIncorrecto(texto):
    invalidos = '1234567890!#$%&/()=?¡¿´*{¨]}[-_.:;,<>|°'
    for i in texto:
        if i in invalidos:
            return True
    return False


class Tabla:
    def __init__(self, root, dataFrame, anchos, fechas, bgColor, posX, posY):
        self.anchos = anchos
        self.fechas = fechas
        self.nuevoDatos = []
        self.componentes = []
        cont = 0
        self.df = dataFrame
        self.frm = ttk.Frame(root)
        for k in dataFrame:
            tmp = Entry(
                self.frm,
                width=anchos[cont],
                bg=bgColor,
                fg="black",
                font=("Arial", 12),
                highlightthickness=1,
                highlightbackground="#000000",
                highlightcolor="#000000",
            )
            tmp.grid(row=0, column=cont)
            tmp.insert(INSERT, k)
            cont += 1
        self.lista = list(dataFrame.to_records(index=False))
        self.filas = len(self.lista)
        self.columnas = cont
        for i in range(self.filas):
            row = []
            for j in range(self.columnas):
                aux = Entry(
                    self.frm,
                    width=anchos[j],
                    fg="black",
                    font=(
                        "Arial",
                        12,
                    ),
                    highlightthickness=1,
                    highlightbackground="#000000",
                    highlightcolor="#000000",
                )
                aux.grid(row=i + 1, column=j)
                if len(fechas) == 0:
                    aux.insert(INSERT, self.lista[i][j])
                else:
                    if j in fechas:
                        aux.insert(
                            INSERT,
                            pd.to_datetime(self.lista[i][j])
                            .date()
                            .strftime("%d/%m/%y"),
                        )
                    else:
                        aux.insert(INSERT, self.lista[i][j])
                aux.configure(state="readonly")
                row.append(aux)
            self.componentes.append(row)
        self.frm.pack()
        self.frm.place(x=posX, y=posY)

    def ingresarDatos(self, datos):
        self.lista.append(datos)
        for i in range(self.columnas):
            aux = Entry(
                self.frm,
                width=self.anchos[i],
                fg="black",
                font=(
                    "Arial",
                    12,
                ),
                highlightthickness=1,
                highlightbackground="#000000",
                highlightcolor="#000000",
            )
            aux.grid(row=self.filas + 1, column=i)
            aux.insert(INSERT, datos[i])
            aux.configure(state="readonly")
        self.df.loc[self.df.shape[0]] = datos
        self.filas += 1
        return

    def borrarUltimaFila(self):
        if self.filas < 1:
            messagebox.showerror(
                title="ERROR", message="No hay datos que puedan ser borrados"
            )
            return
        cont = 0
        for i in self.frm.winfo_children():
            if cont >= self.columnas * self.filas:
                i.destroy()
            cont += 1
        self.df = self.df[:-1]
        self.lista.pop()
        self.filas -= 1


class FrmIngresoDeLibros:
    def __init__(self, master, regresar):
        self.frm = ttk.Frame(master)
        self.nombreLibro = StringVar()
        self.cantidadLibro = StringVar()
        self.tabla = None
        self.agregarComponentes(master, regresar)

    def agregarComponentes(self, master, regresar):
        hoy = datetime.today().strftime("%d/%m/%y")
        Label(
            text="INGRESO DE LIBROS",
            font=("Arial", 24, "bold"),
            bg="#315E7A",
            fg="white",
            width="500",
            height="2",
        ).pack()
        Label(
            text=hoy,
            font=("Arial", 12),
            bg="#00E0FF",
            fg="white",
            width="20",
            height="1",
        ).pack()
        Label(text="Libro", font=("Arial", 12, "bold")).place(x=150, y=150)
        Entry(
            textvariable=self.nombreLibro,
            width="25",
            font=("Arial", 12),
            highlightthickness=2,
            highlightbackground="#000000",
            highlightcolor="#000000",
        ).place(x=250, y=150)
        Label(text="Cant", font=("Arial", 12, "bold")).place(x=520, y=150)
        Entry(
            textvariable=self.cantidadLibro,
            width="25",
            font=("Arial", 12),
            highlightthickness=2,
            highlightbackground="#000000",
            highlightcolor="#000000",
        ).place(x=590, y=150)
        Button(
            text="Borrar",
            font=("Arial", 12),
            width="20",
            bg="#D0A9F5",
            height="2",
            command=self.borrar,
        ).place(x=150, y=250)
        Button(
            text="Ingresar",
            font=("Arial", 12),
            width="20",
            bg="#D0A9F5",
            height="2",
            command=lambda: self.ingresar(master),
        ).place(x=400, y=250)
        Button(
            text="Regresar",
            font=("Arial", 12),
            width="20",
            bg="#D0A9F5",
            height="2",
            command=regresar,
        ).place(x=650, y=250)
        self.mostrarTabla(master)

    def borrar(self):
        self.tabla.borrarUltimaFila()
        archivo = self.tabla.df
        archivo2 = pd.read_excel("EstadoLibros.xlsx", sheet_name="Hoja1")
        archivo2 = archivo2[:-1]
        archivo.to_excel("Libros.xlsx", sheet_name="Hoja1", index=False)
        archivo2.to_excel("EstadoLibros.xlsx", sheet_name="Hoja1", index=False)

    def mostrarTabla(self, master):
        archivo = pd.read_excel("Libros.xlsx", sheet_name="Hoja1")
        anchos = [5, 40, 20, 20, 5]
        fechas = [2]
        self.tabla = Tabla(master, archivo, anchos, fechas, "#154673", 100, 350)

    def ingresar(self, master):
        n = len(self.tabla.lista) + 1
        nombre = self.nombreLibro.get()
        if nombre == "":
            messagebox.showerror(
                title="ERROR", message="El nombre ingresado es incorrecto"
            )
            return
        fecha = datetime.now().date().strftime("%d/%m/%y")
        try:
            stock = int(self.cantidadLibro.get())
        except ValueError:
            messagebox.showerror(
                title="ERROR", message="La cantidad ingresada es incorrecta"
            )
            return
        if stock <= 0:
            messagebox.showerror(
                title="ERROR", message="Debe ingresar una cantidad mayor a 0"
            )
            return
        if len(self.tabla.df[self.tabla.df["Nombre del Libro"] == nombre]) > 0:
            index = self.tabla.df.index[
                self.tabla.df["Nombre del Libro"] == nombre
            ].tolist()[0]
            valores = self.tabla.df[self.tabla.df["Nombre del Libro"] == nombre].values[
                0
            ]
            valores[3] += stock
            self.tabla.df.loc[index] = valores
            self.tabla.frm.destroy()
            archivo = self.tabla.df
            archivo.to_excel("Libros.xlsx", sheet_name="Hoja1", index=False)
            archivo2 = pd.read_excel("EstadoLibros.xlsx", sheet_name="Hoja1")
            valores2 = archivo2[archivo2["Nombre del Libro"] == nombre].values[0]
            valores2[5] += stock
            valores2[4] = valores2[5] - valores2[3]
            archivo2.loc[index] = valores2
            archivo2.to_excel("EstadoLibros.xlsx", sheet_name="Hoja1", index=False)
            self.mostrarTabla(master)
            messagebox.showinfo(
                message="El libro se ha actualizado correctamente",
                title="LIBRO ACTUALIZADO",
            )
            self.nombreLibro.set("")
            self.cantidadLibro.set("")
            return

        datos = (n, nombre, fecha, stock)
        self.nombreLibro.set("")
        self.cantidadLibro.set("")
        self.tabla.ingresarDatos(datos)
        archivo = self.tabla.df
        archivo.to_excel("Libros.xlsx", sheet_name="Hoja1", index=False)
        archivo2 = pd.read_excel("EstadoLibros.xlsx", sheet_name="Hoja1")
        archivo2.loc[archivo2.shape[0]] = [n, nombre, "Disponible", 0, stock, stock]
        archivo2.to_excel("EstadoLibros.xlsx", sheet_name="Hoja1", index=False)


class FrmRegistroEstudiante:
    def __init__(self, master, regresar):
        self.frm = Frame(master)
        self.nombre = StringVar()
        self.apellido = StringVar()
        self.lectorDelMes = StringVar()
        self.libroMasSolicitado = StringVar()
        self.cbxOperacion = None
        self.cbxLibro = None
        self.tabla = None
        self.ultimaOperacion = ""
        self.ultimoLibro = ""
        self.hallarDatos()
        self.agregarComponentes(master, regresar)

    def hallarDatos(self):
        excel = pd.read_excel("HistorialLibros.xlsx", sheet_name="Hoja1")
        nombres = excel["Nombre"]
        apellidos = excel["Apellido"]
        nombreCompleto = []
        for i in range(len(nombres)):
            nombreCompleto.append(nombres[i] + " " + apellidos[i])
        self.lectorDelMes.set(moda(nombreCompleto))
        libros = excel["Nombre del Libro"]
        self.libroMasSolicitado.set(moda(list(libros)))

    def agregarComponentes(self, master, regresar):
        hoy = datetime.today().strftime("%d/%m/%y")
        Label(
            text="REGISTRO DEL ESTUDIANTE",
            font=("Arial", 24, "bold"),
            bg="#DF7401",
            fg="white",
            width="500",
            height="2",
        ).pack()
        Label(
            text=hoy,
            font=("Arial", 12),
            bg="#F5DA81",
            fg="white",
            width="25",
            height="1",
        ).pack()
        Label(text="Nombre", font=("Arial", 12, "bold")).place(x=150, y=150)
        Entry(
            textvariable=self.nombre,
            width="20",
            font=("Arial", 12),
            highlightthickness=2,
            highlightbackground="#000000",
            highlightcolor="#000000",
        ).place(x=250, y=150)
        Label(text="Apellido", font=("Arial", 12, "bold")).place(x=150, y=200)
        Entry(
            textvariable=self.apellido,
            width="20",
            font=("Arial", 12),
            highlightthickness=2,
            highlightbackground="#000000",
            highlightcolor="#000000",
        ).place(x=250, y=200)
        Label(text="Operacion", font=("Arial", 12, "bold")).place(x=520, y=150)
        self.cbxOperacion = ttk.Combobox(
            state="readonly",
            values=["Retiro", "Devolucion"],
            width=15,
            font=("Arial", 12),
        )
        self.cbxOperacion.place(x=630, y=150)
        Label(text="Libro", font=("Arial", 12, "bold")).place(x=520, y=200)
        self.cbxLibro = ttk.Combobox(values=["a"], width=20, font=("Arial", 12))
        self.cbxLibro.place(x=630, y=200)
        Button(
            text="Borrar",
            font=("Arial", 12),
            width="20",
            bg="#F7BE81",
            height="2",
            command=lambda: self.borrar(master),
        ).place(x=150, y=260)
        Button(
            text="Aceptar",
            font=("Arial", 12),
            width="20",
            bg="#F7BE81",
            height="2",
            command=lambda: self.aceptar(master),
        ).place(x=400, y=260)
        Button(
            text="Regresar",
            font=("Arial", 12),
            width="20",
            bg="#F7BE81",
            height="2",
            command=regresar,
        ).place(x=650, y=260)
        Label(text="Lector del mes", font=("Arial", 12, "bold")).place(x=50, y=350)
        Entry(
            textvariable=self.lectorDelMes,
            width="25",
            font=("Arial", 12),
            highlightthickness=2,
            highlightbackground="#000000",
            highlightcolor="#000000",
            state="readonly",
        ).place(x=180, y=350)
        Label(text="Libro mas solicitado", font=("Arial", 12, "bold")).place(
            x=450, y=350
        )
        Entry(
            textvariable=self.libroMasSolicitado,
            width="30",
            font=("Arial", 12),
            highlightthickness=2,
            highlightbackground="#000000",
            highlightcolor="#000000",
            state="readonly",
        ).place(x=620, y=350)
        self.mostrarTabla(master)
        self.cbxOperacion.current(0)
        self.cbxLibro.configure(values=list(self.tabla.df["Nombre del Libro"]))
        self.cbxLibro.current(0)

    def mostrarTabla(self, master):
        archivo = pd.read_excel("EstadoLibros.xlsx", sheet_name="Hoja1")
        anchos = [5, 40, 20, 10, 10, 10]
        fechas = []
        self.tabla = Tabla(master, archivo, anchos, fechas, "#F5DA81", 50, 400)

    def borrar(self, master):
        if len(self.ultimaOperacion) == 0:
            messagebox.showerror(title='ERROR', message='No hay registros anteriores para borrar')
            return
        excel = self.tabla.df
        index = self.tabla.df.index[self.tabla.df["Nombre del Libro"] == self.ultimoLibro].tolist()[0]
        valores = self.tabla.df[self.tabla.df["Nombre del Libro"] == self.ultimoLibro].values[0]
        if self.ultimaOperacion == "Retiro":
            valores[4] += 1
            valores[3] -= 1
            if valores[4] > 0:
                valores[2] = 'Disponible'
            historial = pd.read_excel("HistorialLibros.xlsx", sheet_name="Hoja1")
            historial = historial[:-1]
            historial.to_excel("HistorialLibros.xlsx", sheet_name="Hoja1", index=False)
        else:
            valores[3] += 1
            valores[4] -= 1
            if valores[4] == 0:
                valores[2] = 'No Disponible'
        excel.loc[index] = valores
        excel.to_excel("EstadoLibros.xlsx", sheet_name="Hoja1", index=False)
        self.tabla.frm.destroy()
        self.mostrarTabla(master)
        self.hallarDatos()
        self.ultimaOperacion = ""
        self.ultimoLibro = ""

    def aceptar(self, master):
        nombre = self.nombre.get()
        apellido = self.apellido.get()
        operacion = self.cbxOperacion.get()
        libro = self.cbxLibro.get()
        excel = self.tabla.df
        mensaje = ""
        if len(nombre) == 0:
            mensaje += "Debe ingresar el nombre del alumno\n"
        if len(apellido) == 0:
            mensaje += "Debe ingresar el apelldio del alumno\n"
        if len(mensaje) > 0:
            messagebox.showerror(title="ERROR", message=mensaje)
            return
        mensaje = ""
        if nombreIncorrecto(nombre) is True:
            mensaje += 'El nombre del alumno es incorrecto\n'
        if nombreIncorrecto(apellido) is True:
            mensaje += 'El apellido del alumno es incorrecto\n'
        if len(mensaje) > 0:
            messagebox.showerror(title='ERROR', message=mensaje)
            return
        if len(self.tabla.df[self.tabla.df["Nombre del Libro"] == libro]) > 0:
            index = self.tabla.df.index[
                self.tabla.df["Nombre del Libro"] == libro
            ].tolist()[0]
            valores = self.tabla.df[self.tabla.df["Nombre del Libro"] == libro].values[
                0
            ]
            if operacion == "Retiro":
                if valores[4] > 0:
                    valores[3] += 1
                    valores[4] -= 1
                    if valores[4] == 0:
                        valores[2] = 'No Disponible'
                    historial = pd.read_excel(
                        "HistorialLibros.xlsx", sheet_name="Hoja1"
                    )
                    n = len(list(historial.to_records(index=False))) + 1
                    codigo = generarCodigo(libro)
                    hoy = datetime.today()
                    entrega = timedelta(7)
                    datos = [
                        n,
                        nombre,
                        apellido,
                        libro,
                        codigo,
                        hoy.strftime("%d/%m/%y"),
                        datetime.date(hoy + entrega).strftime("%d/%m/%y"),
                    ]
                    historial.loc[historial.shape[0]] = datos
                    historial.to_excel(
                        "HistorialLibros.xlsx", sheet_name="Hoja1", index=False
                    )
                    self.nombre.set("")
                    self.apellido.set("")
                    messagebox.showinfo(
                        title="RETIRO EXITOSO",
                        message="El libro ha sido retirado satisfactoriamente",
                    )
                else:
                    messagebox.showerror(
                        title="ERROR", message="No quedan mas libros disponibles"
                    )
            else:
                if valores[4] < valores[5]:
                    valores[4] += 1
                    valores[3] -= 1
                    if valores[4] > 0:
                        valores[2] = 'Disponible'
                    self.nombre.set("")
                    self.apellido.set("")
                    messagebox.showinfo(
                        title="DEVOLUCION EXITOSA",
                        message="El libro ha sido devuelto satisfactoriamente",
                    )
                else:
                    messagebox.showerror(
                        title="ERROR", message="No existen devoluciones pendientes"
                    )
            self.ultimaOperacion = operacion
            self.ultimoLibro = libro
            excel.loc[index] = valores
            excel.to_excel("EstadoLibros.xlsx", sheet_name="Hoja1", index=False)
            self.tabla.frm.destroy()
            self.mostrarTabla(master)
            self.hallarDatos()
        else:
            messagebox.showerror(
                title="ERROR", message="El libro que estas solicitando no existe"
            )


class FrmRetirosDevoluciones:
    def __init__(self, master, regresar):
        self.cbxLibro = None
        self.tabla = None
        self.agregarComponentes(master, regresar)

    def agregarComponentes(self, master, regresar):
        Label(text="Libro", font=("Arial", 12, "bold")).place(x=50, y=40)
        self.cbxLibro = ttk.Combobox(values=["a"], width=30, font=("Arial", 12))
        self.cbxLibro.place(x=150, y=40)
        Button(
            text="Buscar",
            font=("Arial", 12),
            width="20",
            bg="#6C3483",
            height="2",
            command=lambda: self.actualizarTabla(master),
        ).place(x=500, y=20)
        Button(
            text="Regresar",
            font=("Arial", 12),
            width="20",
            bg="#6C3483",
            height="2",
            command=regresar,
        ).place(x=750, y=20)
        excel = pd.read_excel("Libros.xlsx", sheet_name="Hoja1")
        self.cbxLibro.configure(values=list(excel["Nombre del Libro"]))

    def actualizarTabla(self, master):
        if self.tabla != None:
            self.tabla.frm.destroy()
        libro = self.cbxLibro.get()
        if len(libro) == 0:
            messagebox.showerror(title='ERROR', message='Debe ingresar el nombre del libro que desea consultar')
            return
        excel = pd.read_excel("HistorialLibros.xlsx", sheet_name="Hoja1")
        if len(excel[excel["Nombre del Libro"] == libro]) > 0:
            filtrado = excel[excel["Nombre del Libro"] == libro]
            anchos = [5, 15, 15, len(libro), 10, 13, 13]
            fechas = []
            self.tabla = Tabla(
            master, filtrado, anchos, fechas, "#A569BD", 43 - len(libro), 100
            )
        else:
            messagebox.showerror(title='ERROR', message='No existen registros del libro ingresado')


class BibliotecaEscolar:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("Biblioteca Escolar")
        screen_width = self.root.winfo_screenwidth()
        screen_height = self.root.winfo_screenheight()
        w = 1000
        h = 600
        x = (screen_width/2) - (500)
        y = (screen_height/2) - (300)
        self.root.geometry('%dx%d+%d+%d' % (w, h, x, y))
        self.root.resizable(False, False)
        self.agregarComponentes()
        self.formulario = None
        self.root.mainloop()

    def regresar(self):
        for widget in self.root.winfo_children():
            widget.destroy()
        self.agregarComponentes()

    def limpiarFormulario(self, frm):
        for widget in frm.winfo_children():
            widget.destroy()

    def agregarComponentes(self):
        hoy = datetime.today().strftime("%d-%m-%y")
        Label(
            text="BIBLIOTECA ESCOLAR",
            font=("Arial", 24, "bold"),
            bg="#27AE60",
            fg="white",
            width="500",
            height="2",
        ).pack()
        Label(
            text=hoy,
            font=("Arial", 12),
            bg="#82E0AA",
            fg="black",
            width="25",
            height="1",
        ).pack()
        Button(
            text="Registrar Libro",
            font=("Arial", 16),
            width="20",
            bg="#315E7A",
            height="4",
            fg="white",
            command=self.abrirFrmRegistrar,
        ).place(x=150, y=230)
        Button(
            text="Solicitudes Libro",
            font=("Arial", 16),
            width="20",
            bg="#DF7401",
            height="4",
            fg="white",
            command=self.abrirFrmSolicitud,
        ).place(x=600, y=230)
        Button(
            text="Salir del programa",
            font=("Arial", 16),
            width="20",
            bg="#A93226",
            height="4",
            fg="white",
            command=self.cerrarPrograma,
        ).place(x=600, y=400)
        Button(
            text="Retiros y devoluciones",
            font=("Arial", 16),
            width="20",
            bg="#5B2C6F",
            height="4",
            fg="white",
            command=self.abrirFrmRetirosDevoluciones,
        ).place(x=150, y=400)

    def abrirFrmRegistrar(self):
        self.limpiarFormulario(self.root)
        self.formulario = FrmIngresoDeLibros(self.root, self.regresar)

    def abrirFrmSolicitud(self):
        self.limpiarFormulario(self.root)
        self.formulario = FrmRegistroEstudiante(self.root, self.regresar)

    def abrirFrmRetirosDevoluciones(self):
        self.limpiarFormulario(self.root)
        self.formulario = FrmRetirosDevoluciones(self.root, self.regresar)

    def cerrarPrograma(self):
        self.root.destroy()


a = BibliotecaEscolar()
