#lang racket/gui

(require "Matrices.rkt")
(require "Reglas-vecindad.rkt")
(require "Animat.rkt")
;;;;;;;;;;;;;;
;;Objetos según un tipo
(define cosa%
  (class object%
    (init-field
     (tipo 0)
     )
    (define/public get-tipo
      (lambda ()
        tipo ))
    (super-new)
    ))
;;;;;;;;;;;;
(define obstaculo
  (make-object cosa% 1))
(define comida
  (make-object cosa% 2))
(define blanco
  (make-object cosa% 0))
;;;;;;;;;;;;;;
(define entorno%
  (class object%
    (init-field
     (ff 0)
     (cc 0)
     )
    (define set-matriz
      (lambda (matriz)
        (let rows ((i 0)
                   (j ff))
          (if (= i j)
              matriz
              (let columnas ((m 0)
                             (n cc))
                (if (= m n)
                    (rows (add1 i) j)
                    (begin
                      (matriz-set! matriz i m blanco)
                      (columnas (add1 m) n))
                    )
                )
              )
          )
        ))
    (define/public set-configuracion
      (lambda ()
        (set! configuracion (es-configuracion))
        ))
    (define es-configuracion
      (lambda ()
        (map
         (lambda (fila)
           (map
            (lambda (obj)
              obj;
              )
            (vector->list fila))
           )
         (vector->list matriz-objetos)
         )
        ))
    (define/public set-locus!
      (lambda (f c obj)
        (matriz-set! matriz-objetos f c obj)
        #t
        ))
    (field
     (matriz-objetos
      (set-matriz
       (make-matriz ff cc)))
     (configuracion (es-configuracion))
     )
    (define/public iniciar-matriz
      (lambda ()
        (set! matriz-objetos
              (set-matriz
               (make-matriz ff cc)))
        ))
    (define/public get-matriz
      (lambda ()
         matriz-objetos ))
    (define/public get-dimensiones
      (lambda ()
        (cons ff cc) ))
    (define/public get-obj
      (lambda (x y)
        (matriz-ref matriz-objetos x y vector-ref)
        ))
    (define/public get-configuracion
      (lambda ()
        (es-configuracion) ))
    (define/public procesar!
      (lambda (iter)
        (printf "~nIteración_~a~n" iter)
        (let rows
          ((i 0)
           (j ff)
           )
          (if (= i j)
              (set! configuracion (es-configuracion))
              (let columns
                ((m 0)
                 (n cc)
                 )
                (if (= m n)
                    (rows (add1 i) j)
                    (let ((Obj (matriz-ref configuracion i m list-ref))
                          )
                      (cond
                        ((is-a? Obj animat%)
                         ;(printf "Proceso de Animat~n")
                         (let*-values
                             ([(v1 v2 v3 v4 v5 v6 v7 v8) (Moore i m (sub1 ff) (sub1 cc))]
                              [(obj-v1)
                               (matriz-ref 
                                configuracion (car v1) (cdr v1) list-ref)]
                              [(obj-v2)
                               (matriz-ref 
                                configuracion (car v2) (cdr v2) list-ref)]
                              [(obj-v3)
                               (matriz-ref 
                                configuracion (car v3) (cdr v3) list-ref)]
                              [(obj-v4)
                               (matriz-ref 
                                configuracion (car v4) (cdr v4) list-ref)]
                              [(obj-v5)
                               (matriz-ref 
                                configuracion (car v5) (cdr v5) list-ref)]
                              [(obj-v6)
                               (matriz-ref 
                                configuracion (car v6) (cdr v6) list-ref)]
                              [(obj-v7)
                               (matriz-ref 
                                configuracion (car v7) (cdr v7) list-ref)]
                              [(obj-v8)
                               (matriz-ref 
                                configuracion (car v8) (cdr v8) list-ref)]
                              [(vecinos-coord) (list v1 v2 v3 v4 v5 v6 v7 v8)]
                              [(vecinos-obj) (list obj-v1 obj-v2 obj-v3 obj-v4 obj-v5 obj-v6 obj-v7 obj-v8)]
                              [(clave-acto locus_vec)
                               (send Obj procesar! vecinos-coord vecinos-obj iter)]
                              [(obj_locus_vec)
                               (matriz-ref configuracion (car locus_vec) (cdr locus_vec) list-ref)]
                              [(tipo_obj_locus_vec)
                               (send obj_locus_vec get-tipo)]
                              )
                           (case
                               tipo_obj_locus_vec
                             ((0) 
                              (matriz-set! matriz-objetos (car locus_vec) (cdr locus_vec) Obj)
                              (matriz-set! matriz-objetos i m obj_locus_vec)
                              (send Obj set-exito 0 iter)
                              )
                             ((1) 
                              (send Obj set-exito -1 iter)
                              (printf "Intento de intercambiar de lugar con un obstáculo ~n")
                              )
                             ((2) 
                              (matriz-set! matriz-objetos (car locus_vec) (cdr locus_vec) blanco)
                              (send Obj set-exito 1 iter)
                              )
                             )
                           (rows j j)
                           ) )
                        (else
                         ;(printf "Se trata de otro objeto~n")
                         (columns (add1 m) n))
                        )
                      )
                    )
                )
              )
          )
        ))
    (super-new)
    ))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define M (make-object entorno% 50 50))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define ubica-objetos
  (lambda (dimensiones)
    (let rows ((i 0)
             (j (car dimensiones))
             )
      (if (>= i j)
          #t
          (let columns
            ((m 0)
             (n (cdr dimensiones))
             )
            (if (>= m n)
                (rows (+ 1 i) j)
                (let ((x (random 10)))
                  (cond
                    ((= x 1) (send M set-locus! i m obstaculo))
                    ((= x 2) (send M set-locus! i m comida))
                    (else (send M set-locus! i m blanco))
                    )
                  (columns (+ 1 m) n)
                  )
                )
            )
          )
      )
    ))
(define blancos-en
  (lambda (matriz dimensiones)
    (let rows
      ((i 0)
       (j (car dimensiones))
       (salida '())
       )
      (if (= i j)
          salida
          (let columns
            ((m 0)
             (n (cdr dimensiones))
             )
            (if (= m n)
                (rows (add1 i) j salida)
                (if (equal? 0
                            (send
                             (send M get-obj i m)
                             get-tipo))
                    (begin
                      (set! salida (cons (cons i m) salida))
                      (columns (add1 m) n)
                      )
                    (columns (add1 m) n))
                )
            )
          )
      )
    ))
(define ubica-animat
  (lambda (animat)
    (let* 
        ((hay-blancos
          (blancos-en (send M get-matriz) (send M get-dimensiones)))
         (azar (random (length hay-blancos)))
         (locus (list-ref hay-blancos azar))
         )
      (send M set-locus! (car locus) (cdr locus) animat)
      )
    ))
(ubica-objetos (send M get-dimensiones))

(ubica-animat A1)
;(ubica-animat A2)
                
(provide M A1 ubica-objetos ubica-animat)
