;;; emneko.el --- neko on Emacs -*- lexical-binding: nil -*-
;;;
;;; Version: 2025/05/01
;;;
;;; Install
;;;  copy emneko.el and bitmaps/* to /usr/share/emacs/site-lisp/emneko/.
;;;
;;; Setting
;;;  add to .emacs
;;;  ----
;;;  (add-to-list 'load-path "/usr/share/emacs/site-lisp/emneko")
;;;  (require 'emneko)
;;;  ----
;;;
;;; Start and Stop
;;;  M-x emneko-start
;;;  M-x emneko-stop
;;;

;;; Code:

(defvar emneko-bitmap-dir
  (concat (file-name-directory (or load-file-name (buffer-file-name)))
          "bitmaps/"))

(defvar emneko-cur-x  0)
(defvar emneko-cur-y  0)
(defvar emneko-cur-vx 0)
(defvar emneko-cur-vy 0)
(defvar emneko-cur-tired 0)
(defvar emneko-cur-claws 0)
(defvar emneko-cur-motion nil)
(defvar emneko-patlist '( (  0 . "mati2.xbm")
                          (  1 . "mati3.xbm")
                          (  2 . "awake.xbm")
                          (  3 . "jare2.xbm")
                          (  4 . "kaki1.xbm")
                          (  5 . "kaki2.xbm")
                          ( 10 . "up1.xbm")
                          ( 11 . "up2.xbm")
                          ( 20 . "down1.xbm")
                          ( 21 . "down2.xbm")
                          ( 40 . "left1.xbm")
                          ( 41 . "left2.xbm")
                          ( 80 . "right1.xbm")
                          ( 81 . "right2.xbm")
                          ( 90 . "upright1.xbm")
                          ( 91 . "upright2.xbm")
                          ( 50 . "upleft1.xbm")
                          ( 51 . "upleft2.xbm")
                          (100 . "dwright1.xbm")
                          (101 . "dwright2.xbm")
                          ( 60 . "dwleft1.xbm")
                          ( 61 . "dwleft2.xbm")
                          (210 . "utogi1.xbm")
                          (211 . "utogi2.xbm")
                          (220 . "dtogi1.xbm")
                          (221 . "dtogi2.xbm")
                          (240 . "ltogi1.xbm")
                          (241 . "ltogi2.xbm")
                          (280 . "rtogi1.xbm")
                          (281 . "rtogi2.xbm")
                          (300 . "sleep1.xbm")
                          (301 . "sleep2.xbm")
                          ))


(defvar emneko-sequence '(( 0 . (list 0 0 0 0 0 0 0 0 0 3 3 0 0 3 3 0 0 0 0 0 0) )
                          ( 1 . (list 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0) )
                          ( 2 . (list 0 0 0 0 0 0 0 0 0 4 5 4 5 4 5 4 5 0 0 0 0) )
                          ( 3 . (list 0 1 1 1 1 1 1 0 0 0 0 300 300 301 301 300 300 301 301 300 300 301 301 300 300 301 301 300 300 301 301 300 300 301 301 300 300 301 301 300 300 301 301 300 300 301 301 2 2 2 2)) ))

(defvar emneko-buffername "*emneko*"
  "neko buffer name")

(defvar emneko-timer nil
  "neko timer")

(defvar emneko-mode-line-format
  '("    "
    mode-line-frame-identification
    mode-line-buffer-identification))

(defvar emneko-local-map nil)

(defun emneko-start (&optional othwin)
  "start neko"
  (interactive)
  (if (eq (window-system) nil)
      (message "Sorry it can't display in no-window-system")
    (progn
      (if emneko-timer (cancel-timer emneko-timer))
      (setq emneko-timer (run-with-timer t 0.3 'emneko))
      (let ((selected))
        (setq selected (selected-window))
        (if othwin
            (switch-to-buffer-other-window emneko-buffername)
          (switch-to-buffer emneko-buffername))
        (setq emneko-local-map (make-keymap))
        (define-key emneko-local-map "q"  'emneko-stop)
        (use-local-map emneko-local-map)
        (select-window selected)))))

(defun emneko-start-other-window ()
  "start neko"
  (interactive)
  (emneko-start t))

(defun emneko-stop ()
  "stop neko"
  (interactive)
  (if emneko-timer
      (progn
        (cancel-timer emneko-timer)
        (setq emneko-timer nil)
        (kill-buffer emneko-buffername))))

(defun emneko-draw-neko (x y pat)
  (let ((i 0)
        (width (/ (- (frame-pixel-width) 16) 25)))
    (goto-char (point-min))
    (forward-line y)
    (forward-char x)
    (delete-char 1)
    (insert-image (create-image (concat emneko-bitmap-dir "/" pat)
                                'xbm nil :margin 1 :ascent 'center)
                  (format ":%s:" (substring pat 0 -4)))
    (goto-char (point-min))
    ))

(defun emneko-build-motion (x y w h tired claws)
  (let ((state (random 2)))
    (cond
     ((>  tired 100)
      (cdr (cdr (assoc 3 emneko-sequence))))
     ((eq state 0)
      (cdr (cdr (assoc (random 3) emneko-sequence))))
     ((eq state 1)
      (progn
        (if (and (> claws 200)
                 (or (<= x 0) (<= y 0) (>= x w) (>= y h)))
            (progn
              (let ((v) (sharpenseq))
                (if (<= x 0) (push 240 v))
                (if (<= y 0) (push 210 v))
                (if (>= x w) (push 280 v))
                (if (>= y h) (push 220 v))
                (setq v (nth (random (length v)) v))
                (dotimes (n 10)
                  (setq sharpenseq (append sharpenseq (list (+ v (% (+ n 1) 2))))))
                sharpenseq))
          (progn
            (let ((v (list 10 20 40 80 50 90 60 100))
                  (runseq))
              (if (<= x 0)
                  (progn (delete 40 v) (delete 50 v) (delete 60 v)))
              (if (<= y 0)
                  (progn (delete 10 v) (delete 50 v) (delete 90 v)))
              (if (>= x w)
                  (progn (delete 80 v) (delete 90 v) (delete 100 v)))
              (if (>= y h)
                  (progn (delete 20 v) (delete 60 v) (delete 100 v)))
              (setq v (nth (random (length v)) v))
              (dotimes (n (+ (* (/ (random 20) 2) 2) 6))
                (setq runseq (append runseq (list (+ v (% (+ n 1) 2))))))
              runseq)))))
     (t nil))
    ))

(defun emneko ()
  (let* ((field-w (- (window-width  (get-buffer-window emneko-buffername t)) 5))
         (field-h (- (window-height (get-buffer-window emneko-buffername t)) 3))
         (patno))
    (if (<= field-w 0) (setq field-w 1))
    (if (<= field-h 0) (setq field-h 1))

    (with-current-buffer (get-buffer-create emneko-buffername)
      (setq truncate-lines t)
      (setq cursor-type nil)
      (read-only-mode 0)
      (buffer-disable-undo)
      (erase-buffer)

      ;; draw field
      (let ((i) (line) (field))
        (dotimes (n field-w)
          (setq line (concat line " ")))
        (setq line (concat line "\n"))
        (dotimes (n field-h)
          (setq field (concat field line)))
        (insert field))

      ;; move control
      (if (eq emneko-cur-motion nil)
          (setq emneko-cur-motion
                (emneko-build-motion
                 emneko-cur-x emneko-cur-y
                 (- field-w 1)
                 (- field-h 1)
                 emneko-cur-tired
                 emneko-cur-claws)))
      (setq patno (car emneko-cur-motion))
      (setq emneko-cur-motion (cdr emneko-cur-motion))

      (if (and (<  patno 10) (< emneko-cur-claws 5000)) (setq emneko-cur-claws (+ emneko-cur-claws 1)))
      (if (and (>= patno 210) (< patno 300)) (setq emneko-cur-claws 0))
      (if (or (eq patno 300) (eq patno 301)) (setq emneko-cur-tired 0))

      (if (and (>= patno 10) (< patno 200))
          (progn
            (setq emneko-cur-vx
                  (cond
                   ((not (eq (logand (/ patno 10) 4) 0)) -2)
                   ((not (eq (logand (/ patno 10) 8) 0))  2)
                   (t 0)))
            (setq emneko-cur-vy
                  (cond
                   ((not (eq (logand (/ patno 10) 2) 0))  1)
                   ((not (eq (logand (/ patno 10) 1) 0)) -1)
                   (t 0)))

            (setq emneko-cur-x (+ emneko-cur-x emneko-cur-vx))
            (setq emneko-cur-y (+ emneko-cur-y emneko-cur-vy))
            (if (< emneko-cur-tired 5000) (setq emneko-cur-tired (+ emneko-cur-tired 1)))
            ))

      (if (< emneko-cur-x  0)
          (progn  (setq emneko-cur-x 0) (setq emneko-cur-motion nil) (setq patno 0)))
      (if (< emneko-cur-y  0)
          (progn  (setq emneko-cur-y 0) (setq emneko-cur-motion nil) (setq patno 0)))
      (if (>= emneko-cur-x field-w)
          (progn (setq emneko-cur-x (- field-w 1)) (setq emneko-cur-motion nil) (setq patno 0)))
      (if (>= emneko-cur-y field-h)
          (progn (setq emneko-cur-y (- field-h 1)) (setq emneko-cur-motion nil) (setq patno 0)))

      (emneko-draw-neko  emneko-cur-x emneko-cur-y (cdr (assoc patno emneko-patlist)))

      (setq mode-line-format emneko-mode-line-format)
      (read-only-mode 1)
      )))

(provide 'emneko)
