I found that is possible to customize the behavior of the function display-buffer by setting the variable display-buffer-function.
This function seems to do what I want (surely it needs some cleanup but it works):
(defun my-display-buffer (BUFFER-OR-NAME &optional NOT-THIS-WINDOW)
(catch 'return
;; search the buffer in the existing windows
(let ((windows (window-list)))
(while (first windows)
;; found
(if (eq (window-buffer (first windows)) BUFFER-OR-NAME)
(throw 'return
;; if minibuffer doesn't select the window
(if (eq (selected-window) (minibuffer-window))
(first windows)
(select-window (first windows)))))
;; next window
(setq windows (rest windows))))
;; otherwise use current
(if (eq (selected-window) (minibuffer-window))
;; if minubuffer
(progn
(select-window (minibuffer-selected-window))
(switch-to-buffer BUFFER-OR-NAME)
(select-window (minibuffer-window)))
;; if regular window
(progn
(switch-to-buffer BUFFER-OR-NAME)
(selected-window)))))