;;;
;;; resource-db.lisp
;;;
;;; Resource database
;;;
;;; Copyright (C) 2005, Alastair Bridgewater
;;;

(in-package :clxs)

(defconstant +resource-mask+ #x3ffff)

(defvar *resource-client-table* nil)
(defvar *resource-range-table* nil)
(defvar *next-resource-range* 1)

(defun reset-resource-database ()
  "Purpose: This function is called to reset the Resource
Database to its initial state. That is, it destroys any and
all existing resources and clears the table of assigned
ranges."
  ;; Create database tables.
  (setf *resource-client-table* (make-array 4096 :initial-element nil))
  (setf *resource-range-table* (make-array 4096 :initial-element nil))
  (setf *next-resource-range* 1)

  ;; Initialize server resource range.
  (setf (aref *resource-range-table* 0) (make-hash-table))
  (setf (aref *resource-client-table* 0) :server)
  nil)

(defun find-free-resource-range ()
  "Find the next free resource range, or NIL if no range is free.
Starts from *next-resource-range* and wraps when reaches the end
of the range space, skipping range 0."
  (dotimes (i 4096)
    (let ((range (logand 4095 (+ *next-resource-range* i))))
      (when (and (not (zerop range))
		 (not (aref *resource-range-table* range)))
	(setf *next-resource-range* (1+ range))
	(return range)))))

(defun assign-resource-range (client)
  "Purpose: Assigns a resource range to a newly-connected
client. Sets the client-state-resource-base and
client-state-resource-mask on the client, and associates
the client with said resource range."
  (declare (type client-state client))
  (let ((resource-range (find-free-resource-range)))
    (if (eq resource-range nil)
	(values nil)
	(progn
	  (setf (aref *resource-client-table* resource-range) client)
	  (setf (aref *resource-range-table* resource-range) (make-hash-table))
	  (setf (slot-value client 'resource-id-base)
		(ash resource-range 18))
	  (setf (slot-value client 'resource-id-mask)
		+resource-mask+)
	  (values t)))))

(defun x-resource (resource-id)
  ;; FIXME: We should return NIL if someone passes an out-of-range resource-id.
  (let* ((resource-range (ash resource-id -18))
	 (resource-index (logand resource-id +resource-mask+))
	 (resource-hash (aref *resource-range-table* resource-range)))
    (and resource-hash (gethash resource-index resource-hash nil))))

(defun (setf x-resource) (object resource-id)
  (let* ((resource-range (ash resource-id -18))
	 (resource-index (logand resource-id +resource-mask+))
	 (resource-hash-table (aref *resource-range-table* resource-range)))
    (setf (gethash resource-index resource-hash-table) object)))

(defun destroy-resources-for-range (resource-id)
  (declare (ignorable resource-id))
  ;; FIXME: Implement.
  )

(defun resource-client (resource-id)
  (let ((resource-range (ash resource-id -18)))
    (aref *resource-client-table* resource-range)))

;;; EOF
