#lang scheme
(require scheme/class)

(provide interfaz% agente-abrir-cc% agente-ingresar-cc% agente-sacar-cc% agente-saldo-cc%);agente% agente-concurrente% 

(define agente% 
  (class object%
;;;;propiedades
    (init-field
     (parte-de-sistema 'object)
     )
;;;;métodos de acceso y cambio
    (define get-sistema
      (lambda ()
        parte-de-sistema))
    (define set-sistema
      (lambda (obj)
        (set! parte-de-sistema obj) ))
;;;;métodos de procesamiento
    (define entrada
      (lambda (mensa)
        "cada tipo de agente ha de establecer este método"))
    (define tarea
      (lambda (datos)
        "cada tipo de agente ha de establecer este método"))
    (define dinamismo 
      (lambda () 
        "cada tipo de agente ha de establecer este método" ))
    (public get-sistema set-sistema entrada tarea dinamismo)
    (super-new) ))
;;;;;;;
(define interfaz%
  (class agente%
    (init-field 
     (palabras-clave 'lista-palabras))
    (inherit-field parte-de-sistema)
    (field
     (historia-frases '()))
    ;;:
    (define/public get-historia-frases
      (lambda ()
        historia-frases))
    ;;;
    (define string->lista-palabras
      (lambda (str)
        (letrec
            ((aux
              (lambda (str i)
                (if (= (string-length str) i)
                    (list str)
                    (if (char=? (string-ref str i) #\space)
                        (cons
                         (substring str 0 i)
                         (aux (substring str (add1 i) (string-length str)) 0))
                        (aux str (add1 i)))
                    )))
             )
          (aux str 0))))
    (define orden
      (lambda (entrada claves)
        (let ((orden #f))
          (for-each
           (lambda (clave)
             (for-each
              (lambda (palabra)
                (when (string=? palabra clave)
                  (set! orden clave)))
              entrada))
           claves)
          (string->symbol orden))))
    (define datos-para-orden;obliga a convertir el string en simbolo
      (lambda (orden)
        (case orden
          ((abrir) (list 'nombre 'dni 'cantidad))
          ((ingresar) (list 'num-cc 'dni 'cantidad))
          ((sacar) (list 'nun-cc 'dni 'cantidad))
          ((saldo) (list 'num-cc 'dni)) )))
    
    (define solicitar-dato
      (lambda (identificador)
        (printf "~nEscribe, por favor, tu ~a: " identificador)
        (case identificador
          ((nombre) (read-line))
          (else (string->number (read-line))))))
    
    (define pedir-datos;chapuza
      (lambda (lista-datos)
        (letrec ((lst '())
                 (aux
                  (lambda (lista)
                    (if (null? lista)
                        lst
                        (cons
                         (solicitar-dato (car lista))
                         (pedir-datos (cdr lista)))) ))
                 )
          (if (= (length lista-datos) 3)
              (set! lst '())
              (set! lst (list 0)))
          (aux lista-datos) )
        ))
    
    (define establecer-datos
      (lambda (orden)
        (let* ((segun-lista (datos-para-orden orden))
               (datos (pedir-datos segun-lista)) )
          datos)))
    ;;;proceso
    (override dinamismo)
    (define dinamismo
      (lambda (mensa)
        (case mensa
          ((input) (accion-input))
          ((output) (accion-output)))))
    (define accion-input
      (lambda ()
        (printf "Son posibles operaciones: abrir, ingresar, sacar y saldo~n")
        (printf "Escribe una orden ")
        (let* ((frase (read-line))
               (lst-palabras (string->lista-palabras frase))
               (operacion (orden lst-palabras palabras-clave))
               (datos (establecer-datos operacion)) 
               (mensaje (list operacion datos))
               )
          (set! historia-frases (cons frase historia-frases));para estadística
          mensaje)))
    (define accion-output
      (lambda ()
        (letrec ((datos (send parte-de-sistema salida-respuesta))
                 (pprint
                  (lambda (lst)
                    (if (null? lst)
                        (newline)
                        (begin
                          (display (car lst))
                          (pprint (cdr lst))) )))
               )
          (pprint datos) )))
          
          
    ;;;
    ;(public accion)
    (super-new)))
;;;;;;;;;;;;;;;;AGENTES CONCURRENTES;;;;;;;;;;;;;;;;;;;
(define agente-concurrente% 
  (class agente%
    (inherit-field parte-de-sistema)
    (inherit entrada tarea)
;;;;
    (define/public aux
      (lambda (reg-p lst-c)
        (if (null? lst-c)
            '()
            (let* ((reg-cc (car lst-c))
                   (id-cc (vector-ref reg-cc 0))
                   (id-per (vector-ref reg-p 0))
                   (per-id-cc (vector-ref reg-p 3))
                   )
              (if (= id-cc per-id-cc); los datos coinciden
                  (cons
                   (list reg-p reg-cc)
                   (aux reg-p (cdr lst-c)) )
                  (aux reg-p (cdr lst-c))) ) ) ))
    (define/public extrae-pares
      (lambda (lista-c lista-p)
        (if
         (or (null? lista-p) (null? lista-c))
         '()
         (let ((reg-per (car lista-p)) )
           (append
            (aux reg-per lista-c)
            (extrae-pares lista-c (cdr lista-p)) )) ) ))
;;;;
    (override dinamismo)
    (define dinamismo
      (lambda (mensaje)
        (let* ((input-clave (car mensaje))
               (input-datos (cadr mensaje))
               (concernido (entrada input-clave)) )
          (if concernido
              (tarea input-datos)
              #f) ) ))
    (super-new) ))
;;;;;;;
(define agente-abrir-cc%
  (class agente-concurrente%
    (inherit-field parte-de-sistema)
    (inherit aux extrae-pares)
    (override entrada tarea)
;;;;;;;
    (define entrada;interpretacion-abrir
      (lambda (clave-operacion)
        (if (equal? clave-operacion 'abrir)
            #t
            #f) ))
    (define tarea;tarea-de-abrir
      (lambda (datos)
        (let* ((nombre (car datos))
               (dni (cadr datos))
               (cant (caddr datos))
               (id-cc (send parte-de-sistema tabla-new 'cuentas))
               (id-per (send parte-de-sistema tabla-new 'personas))
               (reg-cc (vector id-cc cant id-per))
               (reg-per (vector id-per nombre dni id-cc))
               (respuesta (list "creada cuenta con nº " id-cc " saldo " cant))
               )
          (send parte-de-sistema set-pila respuesta)
          (send parte-de-sistema tabla-add! 'cuentas reg-cc)
          (send parte-de-sistema tabla-add! 'personas reg-per)
          #t
          ) ))
    (super-new) ))
;;;;;;;
(define agente-ingresar-cc%
  (class agente-concurrente%
    (inherit-field parte-de-sistema)
    (inherit aux extrae-pares)
    (override entrada tarea)
;;;;;;;
    (define entrada;;interpretacion-ingresar
      (lambda (clave-operacion)
        (if (equal? clave-operacion 'ingresar)
            #t
            #f) ))
    (define tarea;tarea-de-ingresar
      (lambda (datos)
        (letrec ((cc (car datos))
                 (dni (cadr datos))
                 (cantidad (caddr datos))
                 (lista-regs-cc (send parte-de-sistema tabla-match 'cuentas 0 cc))
                 (lista-regs-per (send parte-de-sistema tabla-match 'personas 2 dni))
                 (lst-cuentas #f)
                 (respuesta #f)
                 )
          (if (and lista-regs-cc lista-regs-per)
              (begin
                (set! lst-cuentas (extrae-pares lista-regs-cc lista-regs-per))
                (if (null? lst-cuentas)
                    (begin
                      (set! respuesta (list "la cuenta " cc " no es de " dni))
                      (send parte-de-sistema set-pila respuesta) )
                    (let* ((pareja (car lst-cuentas))
                           (per (car pareja))
                           (c/c (cadr pareja))
                           (id-cc (vector-ref c/c 0))
                           (id-per (vector-ref per 0))
                           (saldo (vector-ref c/c 1))
                           (new-saldo (+ saldo cantidad))
                           (new-reg-cc (vector id-cc new-saldo id-per)) )
                      (set! respuesta (list cantidad " = cantidad ingresada en c/c: " id-cc " nuevo saldo = " new-saldo))
                      (send parte-de-sistema set-pila respuesta)
                      (send parte-de-sistema tabla-mod! 'cuentas id-cc new-reg-cc) ) ))
              (begin
                (set! respuesta (list "la cuenta " cc " no es de " dni))
                (send parte-de-sistema set-pila respuesta) ) )
          #t) ))
    (super-new) ))
;;;;;;;
(define agente-sacar-cc%
  (class agente-concurrente%
    (inherit-field parte-de-sistema)
    (inherit aux extrae-pares)
    (override entrada tarea)
;;;;;;;
    (define entrada;;interpretacion-sacar
      (lambda (clave-operacion)
        (if (equal? clave-operacion 'sacar)
            #t
            #f) ))
    (define tarea;tarea-de-sacar
      (lambda (datos)
        (letrec ((cc (car datos))
                 (dni (cadr datos))
                 (cantidad (caddr datos))
                 (lista-regs-cc (send parte-de-sistema tabla-match 'cuentas 0 cc))
                 (lista-regs-per (send parte-de-sistema tabla-match 'personas 2 dni))
                 (lst-cuentas #f)
                 (respuesta #f)
                 )
          (if (and lista-regs-cc lista-regs-per)
              (begin
                (set! lst-cuentas (extrae-pares lista-regs-cc lista-regs-per))
                (if (null? lst-cuentas)
                    (begin
                      (set! respuesta (list "la cuenta " cc " no es de " dni))
                      (send parte-de-sistema set-pila respuesta) )
                    (let* ((pareja (car lst-cuentas))
                           (per (car pareja))
                           (c/c (cadr pareja))
                           (id-cc (vector-ref c/c 0))
                           (id-per (vector-ref per 0))
                           (saldo (vector-ref c/c 1))
                           (new-saldo (- saldo cantidad))
                           (new-reg-cc (vector id-cc new-saldo id-per)) )
                      (if (> saldo cantidad)
                          (begin
                            (set! respuesta (list cantidad " = cantidad retirada de c/c: " id-cc " nuevo saldo = " new-saldo))
                            (send parte-de-sistema set-pila respuesta)
                            (send parte-de-sistema tabla-mod! 'cuentas id-cc new-reg-cc) )
                          (begin
                            (set! respuesta (list "fondos insuficientes en " cc))
                            (send parte-de-sistema set-pila respuesta) )))))
              (begin
                (set! respuesta (list "la cuenta " cc " no es de " dni))
                (send parte-de-sistema set-pila respuesta) ))
          #t) ))
    (super-new) ))
;;;;;;;
(define agente-saldo-cc%
  (class agente-concurrente%
    (inherit-field parte-de-sistema)
    (inherit aux extrae-pares)
    (override entrada tarea)
;;;;;;;
    (define entrada;interpretacion-saldo
      (lambda (clave-operacion)
        (if (equal? clave-operacion 'saldo)
            #t
            #f) ))
    (define tarea;tarea-de-saldo
      (lambda (datos)
        (letrec ((cc (car datos))
                 (dni (cadr datos))
                 (cantidad (caddr datos))
                 (lista-regs-cc (send parte-de-sistema tabla-match 'cuentas 0 cc))
                 (lista-regs-per (send parte-de-sistema tabla-match 'personas 2 dni))
                 (lst-cuentas #f)
                 (respuesta #f)
                 )
          (if (and lista-regs-cc lista-regs-per)
              (begin
                (set! lst-cuentas (extrae-pares lista-regs-cc lista-regs-per))
                (if (null? lst-cuentas)
                    (begin
                      (set! respuesta (list "la cuenta " cc " no es de " dni))
                      (send parte-de-sistema set-pila respuesta) )
                    (let* ((pareja (car lst-cuentas))
                           (per (car pareja))
                           (c/c (cadr pareja))
                           (id-cc (vector-ref c/c 0))
                           (id-per (vector-ref per 0))
                           (saldo (vector-ref c/c 1)) )
                      (set! respuesta (list "el saldo de la cuenta " id-cc " es " saldo))
                      (send parte-de-sistema set-pila respuesta) )))
              (begin
                (set! respuesta (list "la cuenta " cc " no es de " dni))
                (send parte-de-sistema set-pila respuesta) ))
          #t) ))
    (super-new) ))

