/**
  * Reads a single line from the user.
  *
  * @param string $prompt You may specify a string with which to prompt the user.
  *
  * @return Returns a single string from the user.
  */
 public function readline($prompt = null)
 {
     $line = null;
     $this->_reset();
     // Output prompt
     if ($prompt !== null) {
         $this->_prompt = $prompt;
         echo $prompt;
     }
     while (1) {
         $c = self::readKey();
         switch ($c) {
             // Unrecognised character
             case null:
                 Terminal::bell();
                 break;
                 // TAB
             // TAB
             case chr(9):
                 // If autocompletion is registered, then do it
                 if ($this->_autocomplete_callback !== null) {
                     $autocomplete_text = $this->_doAutocomplete($this->_buffer);
                     if (!empty($autocomplete_text)) {
                         $this->_insert($autocomplete_text);
                     } else {
                         Terminal::bell();
                     }
                     // Otherwise, TAB will insert spaces
                 } else {
                     $this->_insert("        ");
                 }
                 break;
                 // CTRL-A (Home) - move the cursor all the way to the left
             // CTRL-A (Home) - move the cursor all the way to the left
             case chr(1):
                 $this->_cursorLeft($this->_buffer_position);
                 break;
                 // CTRL-E (End) - move cursor all the way to the end
             // CTRL-E (End) - move cursor all the way to the end
             case chr(5):
                 $this->_cursorRight(mb_strlen($this->_buffer) - $this->_buffer_position);
                 break;
                 // Line-delete - backspace from current position to beginning of line
             // Line-delete - backspace from current position to beginning of line
             case chr(21):
                 $this->_backspace($this->_buffer_position);
                 break;
                 // Word-delete (CTRL-W)
             // Word-delete (CTRL-W)
             case chr(23):
                 // Get previous word position
                 $prev_word_pos = $this->_buffer_position - $this->_getPreviousWordPos();
                 // Delete word, unless we're at the start of the line, then bell
                 if ($prev_word_pos > 0) {
                     $this->_backspace($this->_buffer_position - $this->_getPreviousWordPos());
                 } else {
                     Terminal::bell();
                 }
                 break;
                 // CTRL-LEFT
             // CTRL-LEFT
             case chr(27) . chr(91) . chr(53) . chr(68):
                 $this->_cursorLeft($this->_buffer_position - $this->_getPreviousWordPos());
                 break;
                 // CTRL-RIGHT
             // CTRL-RIGHT
             case chr(27) . chr(91) . chr(53) . chr(67):
                 $this->_cursorRight($this->_getNextWordPos() - $this->_buffer_position);
                 break;
                 // CTRL-C
             // CTRL-C
             case chr(3):
                 $line = $this->_buffer . $c;
                 break;
                 // CTRL-D
             // CTRL-D
             case chr(4):
                 // Return current line immediately on CTRL-D
                 if (mb_strlen($this->_buffer) === 0) {
                     $line = $this->_buffer . $c;
                 } else {
                     Terminal::bell();
                 }
                 break;
             case UP:
                 // Move backwards in the history
                 if (!$this->_historyMovePosition(-1)) {
                     Terminal::bell();
                 }
                 break;
             case DOWN:
                 // Move forward in the history
                 if (!$this->_historyMovePosition(1)) {
                     Terminal::bell();
                 }
                 break;
             case LEFT:
                 // Move left, or beep if we're already at the beginning
                 if (!$this->_cursorLeft()) {
                     Terminal::bell();
                 }
                 break;
             case RIGHT:
                 // Move right, or beep if we're already at the end
                 if (!$this->_cursorRight()) {
                     Terminal::bell();
                 }
                 break;
                 // Backspace key
             // Backspace key
             case chr(8):
                 // Delete
             // Delete
             case chr(127):
                 if (!$this->_backspace()) {
                     Terminal::bell();
                 }
                 break;
                 // Enter key
             // Enter key
             case chr(10):
                 // Set the $line variable so we return below
                 $line = $this->_buffer;
                 break;
                 // Normal character key
             // Normal character key
             default:
                 // Ignore unknown control characters
                 if (ord($c[0]) === 27) {
                     Terminal::bell();
                     continue;
                 }
                 // Insert this character into the buffer and move on
                 $this->_insert($c);
         }
         // If line has been set, we're ready to do something with this command
         if ($line !== null) {
             // Firstly check for internal commands
             if ($this->_runInternalCommand(trim($line))) {
                 // It it was an internal command, don't return, just reset and pretend
                 // nothing happened...
                 $this->addHistory($line);
                 $line = null;
                 $this->_reset();
             }
             // Remove temp history item
             array_pop($this->_history_tmp);
             return $line;
         }
     }
 }