     @data = ''
     @resetTerminal()
     @render()
-    @compilePatterns()
-
-  noop: ->
-
-  # handleC0ControlSet: (data) ->
-  #   console.log 'handling C0'
+    # @compilePatterns()
+
+  handleData: (data) ->
+    if data.match(/^\x1b[\x00-\x1f]/)
+      @handleControlCharacter(data[1])
+      return 2
+
+    else if match = data.match(/^(\x1b\x5d|\x9d).*?(\x1b\\\\|\x9c|\x07)/)
+      # OSC seq
+      return match[0].length
+
+    else if match = data.match(/^(\x1b[PX_^]|[\x90\x98\x9e\x9f]).*?(\x1b\\\\|\x9c)/)
+      # DCS/SOS/PM/APC seq
+      return match[0].length
+
+    else if match = data.match(/^(?:\x1b\x5b|\x9b)([\x30-\x3f]*?)[\x20-\x2f]*?[\x40-\x7e]/)
+      # Control sequences
+      @handleControlSequence(match[0], match[1], match)
+      return match[0].length
+
+    else if match = data.match(/^\x1b[\x20-\x2f]*?[\x30-\x3f]/)
+      @handlePrivateEscSeq(match[0])
+      return match[0].length
+
+    else if match = data.match(/^\x1b[\x20-\x2f]*?[\x40-\x5a\x5c-\x7e]/) # excluding \x5b "["
+      @handleStandardEscSeq(match[0])
+      return match[0].length
+
+    else if data.match(/^\x1b\x7f/) # DELETE
+      return 2
+
+    else if data.match(/^[\x00-\x1a\x1c-\x1f]/) # excluding \x1b "ESC"
+      @handleControlCharacter(data[0])
+      return 1
+
+    else if match = data.match(/^([\x20-\x7e]|\xe2..|[\xa1-\xfe])+/)
+      @handlePrintableCharacters(match[0])
+      return match[0].length
+
+    else if data[0] is "\x7f"
+      # DELETE, always and everywhere ignored
+      return 1
+
+    else if data.match(/^[\x80-\x9f]/)
+      @handleControlCharacter(data[0])
+      return 1
+
+    else if data[0] is "\xa0"
+      # Same as SPACE (\x20)
+      @handlePrintableCharacters(' ')
+      return 1
+
+    else if data[0] is "\xff"
+      # Same as DELETE (\x7f)
+      return 1
+
+    else
+      return 0
+
+  handleControlCharacter: (char) ->
+    switch char
+      when "\x07"
+        @bell()
+      when "\x08"
+        @backspace()
+      when "\x09"
+        @buffer.goToNextHorizontalTabStop()
+        # @tab()
+      when "\x0a"
+        @lineFeed()
+      when "\x0b"
+        @verticalTab()
+      when "\x0c"
+        @formFeed()
+      when "\x0d"
+        @carriageReturn()
+
+      when "\x84"
+        @index() # "D"
+      when "\x85"
+        @newLine() # "E"
+      when "\x88"
+        @setHorizontalTabStop() # "H"
+      when "\x8d"
+        @reverseIndex() # "M"
+
 
   handlePrintableCharacters: (text) ->
-
-  # handleC1ControlSet: (data) ->
-
-  # handleControlSequence: (data) ->
-
-  _C0_PATTERNS:
-    # C0 set of 7-bit control characters
-    "[\x00-\x1f]":
-      # bell
-      "\x07": (data) -> @bell()
-
-      # backspace
-      "\x08": (data) -> @backspace()
-
-      # Move the cursor to the next tab stop
-      "\x09": (data) ->
-
-      "\x0a": (data) -> @lineFeed()
-
-      "\x0d": (data) -> @carriageReturn()
-
-      "\x0e": (data) ->
-
-      "\x0f": (data) ->
-
-      # Reserved (?)
-      "\x82": (data) ->
-
-      # Cancel Character, ignore previous character
-      "\x94": (data) ->
-
-      # Escape sequence
-      "\x1b": _.extend({}, @_C0_PATTERNS, {
-
-        # Control sequence
-        "\x1b\\[": @_CS_PATTERNS
-      })
-
-  _CS_PATTERNS:
-    "sth": 1
-
-  _PATTERNS: _.extend({}, @_C0_PATTERNS, {
-    # Printable characters
-    "([\x20-\x7e])+": @handlePrintableCharacters
-
-    # "Delete", always and everywhere ignored
-    "[\x7f\xff]": @noop
-
-    # C1 control set
-    "[\x80-\x9f]":
-
-      # Control sequence
-      "\x9b": @_CS_PATTERNS
-
-    # G1 Displayable, 94 additional displayable characters
-    "[\xa1-\xfe]": @handlePrintableCharacters
-
-    # Always and everywhere a blank space
-    "\xa0": -> @handlePrintableCharacters(' ')
-
-  })
-
-  PATTERNS:
-    "\x00": (data) ->
-    "\x07": (data) -> @bell()
-    "\x08": (data) -> @backspace()
-    "\x09": (data) -> @buffer.goToNextHorizontalTabStop()
-    "\x0a": (data) -> @lineFeed()
-    "\x0b": (data) -> @verticalTab()
-    "\x0c": (data) -> @formFeed()
-    "\x0d": (data) -> @carriageReturn()
-    "\x0e": (data) ->
-    "\x0f": (data) ->
-    "\x82": (data) -> # Reserved (?)
-    "\x85": (data) -> @setHorizontalTabStop()
-    "\x94": (data) -> # Cancel Character, ignore previous character
-
-    # 20 - 7e
-    "([\x20-\x7e]|\xe2..|[\xc2\xc4\xc5].)+": (data, match) ->
-      @buffer.print match[0]
-
-    "\x1b\\(B": (data) -> # SCS (Set G0 Character SET)
-
-    "\x1b\\[([0-9;]*)([\x40-\x7e])": (data, match) ->
-      if match[1].length == 0
-        @params = []
-      else
-        @params = _(match[1].split(';')).map (n) -> if n is '' then undefined else parseInt(n)
-
-      @n = @params[0]
-      @m = @params[1]
-      @handleCS match[2]
-
-    # private standards
-    "\x1b\\[\\?([\x30-\x3f]+)([hlsr])": (data, match) ->
-      # h = Sets DEC/xterm specific mode (http://ttssh2.sourceforge.jp/manual/en/about/ctrlseq.html#decmode)
-      # l = Resets mode (http://ttssh2.sourceforge.jp/manual/en/about/ctrlseq.html#mode)
-      # 1001 + s = ?
-      # 1001 + r = ?
-      modes = match[1].split(";")
-      action = match[2]
-      mode = undefined
-
-      for mode in modes
-        if mode is "1"
-          # 1 + h / l = cursor keys stuff
-        else if mode is "5"
-          # Reverse/normal video - ignoring
-        else if mode is "7"
-          # Enables/disables autowrap mode
-        else if mode is "12"
-          if action is "h"
-            # blinking cursor
-          else action is "l"
-            # steady cursor
-        else if mode is "25"
-          if action is "h"
-            @showCursor()
-          else if action is "l"
-            @hideCursor()
-        else if mode is "47"
-          if action is "h"
-            @switchToAlternateBuffer()
-          else if action is "l"
-            @switchToNormalBuffer()
-        else if mode is "1000"
-          # Enables/disables normal mouse tracking
-        else if mode is "1001"
-          # pbly sth with mouse/keys...
-        else if mode is "1002"
-          # 2002 + h / l = mouse tracking stuff
-        else if mode is "1049"
-          if action is "h"
-            # Save cursor position, switch to alternate screen buffer, and clear screen.
-            @switchToAlternateBuffer()
-            @clearScreen()
-          else if action is "l"
-            # Clear screen, switch to normal screen buffer, and restore cursor position.
-            @clearScreen()
-            @switchToNormalBuffer()
-        else
-          throw "unknown mode: " + mode + action
-
-    "\x1b\x3d": (data) -> # DECKPAM - Set keypad to applications mode (ESCape instead of digits)
-
-    "\x1b\x3e": (data) -> # DECKPNM - Set keypad to numeric mode (digits intead of ESCape seq)
-
-    "\x1b\\\\": -> # String Terminator (VT125 exits graphics)
-
-    "\x1b\x5d[012]\x3b.*?\x07": (data, match) -> # OSC - Operating System Command (terminal title)
-
-    "\x1b\\[>c": (data) -> # Secondary Device Attribute request (?)
-
-    "\x1bc": -> @resetTerminal()
-    "\x1bP([^\\\\])*?\\\\": (data) -> # DCS, Device Control String
-    "\x1bD": -> @index()
-    "\x1bE": -> @newLine()
-    "\x1bH": -> @setHorizontalTabStop()
-    "\x1bM": -> @reverseIndex()
-    "\x1bk": -> # NAPLPS lock-shift G1 to GR
-
-    "\x1b7": (data) -> # save cursor pos and char attrs
-      @saveTerminalState()
-
-    "\x1b8": (data) -> # restore cursor pos and char attrs
-      @restoreTerminalState()
-
-  handleCS: (term) ->
+    @buffer.print text
+
+  handleStandardEscSeq: (data) ->
+    last = data[data.length - 1]
+
+    if last.match(/[\x40-\x5f]/)
+      # convert to C1
+      char = String.fromCharCode(last.charCodeAt(0) + 0x40)
+      @handleControlCharacter(char)
+    else
+      switch last
+        when "c"
+          @resetTerminal()
+
+  handlePrivateEscSeq: (data) ->
+    last = data[data.length - 1]
+
+    switch last
+      when "7"
+        @saveTerminalState()
+      when "8"
+        @restoreTerminalState()
+
+  handleControlSequence: (data, params, match) ->
+    if params and params.match(/^[\x3c-\x3f]/)
+      @handlePrivateControlSequence(data, params)
+    else
+      @handleStandardControlSequence(data, params)
+
+  handleStandardControlSequence: (data, params) ->
+    term = data[data.length - 1]
+
+    numbers = @parseParams(params)
+    n = numbers[0]
+    m = numbers[1]
+
     switch term
       when "@"
-        @buffer.insertCharacters @n
+        @buffer.insertCharacters n
       when "A"
-        @buffer.priorRow @n
+        @buffer.priorRow n
       when "B"
-        @buffer.nextRow @n
+        @buffer.nextRow n
       when "C"
-        @buffer.nextColumn @n
+        @buffer.nextColumn n
       when "D"
-        @buffer.priorColumn @n
+        @buffer.priorColumn n
       when "E"
-        @buffer.nextRowFirstColumn @n
+        @buffer.nextRowFirstColumn n
       when "F"
-        @buffer.priorRowFirstColumn @n
+        @buffer.priorRowFirstColumn n
       when "G"
-        @buffer.goToColumn @n
+        @buffer.goToColumn n
       when "H"
-        @buffer.goToRowAndColumn @n, @m
+        @buffer.goToRowAndColumn n, m
       when "I"
-        @buffer.goToNextHorizontalTabStop @n
+        @buffer.goToNextHorizontalTabStop n
       when "J"
-        if @n is 2
+        if n is 2
           @buffer.eraseScreen()
-        else if @n is 1
+        else if n is 1
           @buffer.eraseFromScreenStart()
         else
           @buffer.eraseToScreenEnd()
       when "K"
-        if @n is 2
+        if n is 2
           @buffer.eraseRow()
-        else if @n is 1
+        else if n is 1
           @buffer.eraseFromRowStart()
         else
           @buffer.eraseToRowEnd()
       when "L"
-        @buffer.insertLine @n or 1
+        @buffer.insertLine n or 1
       when "M"
-        @buffer.deleteLine @n or 1
+        @buffer.deleteLine n or 1
       when "P" # DCH - Delete Character, from current position to end of field
-        @buffer.deleteCharacters @n or 1
+        @buffer.deleteCharacters n or 1
       when "S"
-        @buffer.scrollUp @n
+        @buffer.scrollUp n
       when "T"
-        @buffer.scrollDown @n
+        @buffer.scrollDown n
       when "X"
-        @buffer.eraseCharacters @n
+        @buffer.eraseCharacters n
       when "Z"
-        @buffer.goToPriorHorizontalTabStop @n
+        @buffer.goToPriorHorizontalTabStop n
       when "b"
-        @buffer.repeatLastCharacter @n
+        @buffer.repeatLastCharacter n
       when "d" # VPA - Vertical Position Absolute
-        @buffer.goToRow @n
+        @buffer.goToRow n
       when "f"
-        @buffer.goToRowAndColumn @n, @m
+        @buffer.goToRowAndColumn n, m
       when "g"
-        if !@n or @n is 0
+        if !n or n is 0
           @buffer.clearHorizontalTabStop()
-        else if @n is 3
+        else if n is 3
           @buffer.clearAllHorizontalTabStops()
       when "l" # l, Reset mode
-        console.log "(TODO) reset: " + @n
+        console.log "(TODO) reset: " + n
       when "m"
-        @handleSGR @params
+        @handleSGR numbers
       when "n"
         @reportRowAndColumn()
       when "r" # Set top and bottom margins (scroll region on VT100)
-        @setScrollRegion @n or 0, @m or @lines - 1
-      when "^"
-        # reserved
-        # Privacy Message (password verification), terminated by ST
-        # TODO
+        @setScrollRegion n or 0, m or @lines - 1
       else
         throw "no handler for CSI term: " + term
+
+
+  handlePrivateControlSequence: (data, params) ->
+    action = data[data.length - 1]
+    modes = @parseParams(params)
+
+    console.log "private: #{@formattedData(data)}, action: #{action}, modes: #{modes}"
+    for mode in modes
+      if mode is 25
+        if action is "h"
+          @showCursor()
+        else if action is "l"
+          @hideCursor()
+      else if mode is 47
+        if action is "h"
+          @switchToAlternateBuffer()
+        else if action is "l"
+          @switchToNormalBuffer()
+      else if mode is 1049
+        if action is "h"
+          # Save cursor position, switch to alternate screen buffer, and clear screen.
+          console.log 'switching'
+          @switchToAlternateBuffer()
+          @clearScreen()
+        else if action is "l"
+          # Clear screen, switch to normal screen buffer, and restore cursor position.
+          console.log 'switching back'
+          @clearScreen()
+          @switchToNormalBuffer()
+      # else
+      #   throw "unknown mode: " + mode + action
+
+  parseParams: (params) ->
+    if params.length is 0
+      numbers = []
+    else
+      numbers = _(params.replace(/[^0-9;]/, '').split(';')).map (n) -> if n is '' then undefined else parseInt(n)
+
+    numbers
+
+#   _C0_PATTERNS:
+#     default: (data) ->
+#       console.log "no handler for: #{@formattedData(data)}"
+
+#     # C0 set of 7-bit control characters
+#     "\x1b?[\x00-\x1f]":
+#       # bell
+#       "\x07": (data) -> @bell()
+
+#       # backspace
+#       "\x08": (data) -> @backspace()
+
+#       # Move the cursor to the next tab stop
+#       "\x09": (data) ->
+
+#       "\x0a": (data) -> @lineFeed()
+
+#       "\x0d": (data) -> @carriageReturn()
+
+#       "\x0e": (data) ->
+
+#       "\x0f": (data) ->
+
+#       # Reserved (?)
+#       "\x82": (data) ->
+
+#       # Cancel Character, ignore previous character
+#       "\x94": (data) ->
+
+#       # # Escape sequence
+#       # "\x1b": _.extend({}, @_C0_PATTERNS, {
+
+#       # Control sequence
+#       "\x1b\\[": @_CS_PATTERNS
+
+#   _CS_PATTERNS:
+#     "sth": 1
+
+#   _PATTERNS: _.extend({}, @_C0_PATTERNS, {
+#     # Printable characters
+#     "([\x20-\x7e])+": @handlePrintableCharacters
+
+#     # "Delete", always and everywhere ignored
+#     "[\x7f\xff]": @noop
+
+#     # C1 control set
+#     "[\x80-\x9f]":
+
+#       # Control sequence
+#       "\x9b": @_CS_PATTERNS
+
+#     # G1 Displayable, 94 additional displayable characters
+#     "[\xa1-\xfe]": @handlePrintableCharacters
+
+#     # Always and everywhere a blank space
+#     "\xa0": -> @handlePrintableCharacters(' ')
+
+#   })
+
+  # PATTERNS:
+    # "\x00": (data) ->
+    # "\x07": (data) -> @bell()
+    # "\x08": (data) -> @backspace()
+    # "\x09": (data) -> @buffer.goToNextHorizontalTabStop()
+    # "\x0a": (data) -> @lineFeed()
+    # "\x0b": (data) -> @verticalTab()
+    # "\x0c": (data) -> @formFeed()
+    # "\x0d": (data) -> @carriageReturn()
+    # "\x0e": (data) ->
+    # "\x0f": (data) ->
+
+    # # 20 - 7e
+    # "([\x20-\x7e]|\xe2..|[\xc2\xc4\xc5].)+": (data, match) ->
+    #   @buffer.print match[0]
+
+    # "\x1b\\(B": (data) -> # SCS (Set G0 Character SET)
+
+    # "\x1b\\[([0-9;]*)([\x40-\x7e])": (data, match) ->
+    #   if match[1].length == 0
+    #     @params = []
+    #   else
+    #     @params = _(match[1].split(';')).map (n) -> if n is '' then undefined else parseInt(n)
+
+    #   @n = @params[0]
+    #   @m = @params[1]
+    #   @handleCS match[2]
+
+#     # private standards
+#     "\x1b\\[\\?([\x30-\x3f]+)([hlsr])": (data, match) ->
+#       # h = Sets DEC/xterm specific mode (http://ttssh2.sourceforge.jp/manual/en/about/ctrlseq.html#decmode)
+#       # l = Resets mode (http://ttssh2.sourceforge.jp/manual/en/about/ctrlseq.html#mode)
+#       # 1001 + s = ?
+#       # 1001 + r = ?
+#       modes = match[1].split(";")
+#       action = match[2]
+#       mode = undefined
+
+#       for mode in modes
+#         if mode is "1"
+#           # 1 + h / l = cursor keys stuff
+#         else if mode is "5"
+#           # Reverse/normal video - ignoring
+#         else if mode is "7"
+#           # Enables/disables autowrap mode
+#         else if mode is "12"
+#           if action is "h"
+#             # blinking cursor
+#           else action is "l"
+#             # steady cursor
+#         else if mode is "25"
+#           if action is "h"
+#             @showCursor()
+#           else if action is "l"
+#             @hideCursor()
+#         else if mode is "47"
+#           if action is "h"
+#             @switchToAlternateBuffer()
+#           else if action is "l"
+#             @switchToNormalBuffer()
+#         else if mode is "1000"
+#           # Enables/disables normal mouse tracking
+#         else if mode is "1001"
+#           # pbly sth with mouse/keys...
+#         else if mode is "1002"
+#           # 2002 + h / l = mouse tracking stuff
+#         else if mode is "1049"
+#           if action is "h"
+#             # Save cursor position, switch to alternate screen buffer, and clear screen.
+#             @switchToAlternateBuffer()
+#             @clearScreen()
+#           else if action is "l"
+#             # Clear screen, switch to normal screen buffer, and restore cursor position.
+#             @clearScreen()
+#             @switchToNormalBuffer()
+#         else
+#           throw "unknown mode: " + mode + action
+
+    # "\x1b\x3d": (data) -> # DECKPAM - Set keypad to applications mode (ESCape instead of digits)
+
+    # "\x1b\x3e": (data) -> # DECKPNM - Set keypad to numeric mode (digits intead of ESCape seq)
+
+    # "\x1b\\\\": -> # String Terminator (VT125 exits graphics)
+
+    # "\x1b\x5d[012]\x3b.*?\x07": (data, match) -> # OSC - Operating System Command (terminal title)
+
+    # "\x1b\\[>c": (data) -> # Secondary Device Attribute request (?)
+
+    # "\x1bc": -> @resetTerminal()
+    # "\x1bP([^\\\\])*?\\\\": (data) -> # DCS, Device Control String
+    # "\x1bD": -> @index()
+    # "\x1bE": -> @newLine()
+    # "\x1bH": -> @setHorizontalTabStop()
+    # "\x1bM": -> @reverseIndex()
+    # "\x1bk": -> # NAPLPS lock-shift G1 to GR
+
+    # "\x1b7": (data) -> # save cursor pos and char attrs
+    #   @saveTerminalState()
+
+    # "\x1b8": (data) -> # restore cursor pos and char attrs
+    #   @restoreTerminalState()
+
+  # handleCS: (term) ->
+  #   switch term
+  #     when "@"
+  #       @buffer.insertCharacters @n
+  #     when "A"
+  #       @buffer.priorRow @n
+  #     when "B"
+  #       @buffer.nextRow @n
+  #     when "C"
+  #       @buffer.nextColumn @n
+  #     when "D"
+  #       @buffer.priorColumn @n
+  #     when "E"
+  #       @buffer.nextRowFirstColumn @n
+  #     when "F"
+  #       @buffer.priorRowFirstColumn @n
+  #     when "G"
+  #       @buffer.goToColumn @n
+  #     when "H"
+  #       @buffer.goToRowAndColumn @n, @m
+  #     when "I"
+  #       @buffer.goToNextHorizontalTabStop @n
+  #     when "J"
+  #       if @n is 2
+  #         @buffer.eraseScreen()
+  #       else if @n is 1
+  #         @buffer.eraseFromScreenStart()
+  #       else
+  #         @buffer.eraseToScreenEnd()
+  #     when "K"
+  #       if @n is 2
+  #         @buffer.eraseRow()
+  #       else if @n is 1
+  #         @buffer.eraseFromRowStart()
+  #       else
+  #         @buffer.eraseToRowEnd()
+  #     when "L"
+  #       @buffer.insertLine @n or 1
+  #     when "M"
+  #       @buffer.deleteLine @n or 1
+  #     when "P" # DCH - Delete Character, from current position to end of field
+  #       @buffer.deleteCharacters @n or 1
+  #     when "S"
+  #       @buffer.scrollUp @n
+  #     when "T"
+  #       @buffer.scrollDown @n
+  #     when "X"
+  #       @buffer.eraseCharacters @n
+  #     when "Z"
+  #       @buffer.goToPriorHorizontalTabStop @n
+  #     when "b"
+  #       @buffer.repeatLastCharacter @n
+  #     when "d" # VPA - Vertical Position Absolute
+  #       @buffer.goToRow @n
+  #     when "f"
+  #       @buffer.goToRowAndColumn @n, @m
+  #     when "g"
+  #       if !@n or @n is 0
+  #         @buffer.clearHorizontalTabStop()
+  #       else if @n is 3
+  #         @buffer.clearAllHorizontalTabStops()
+  #     when "l" # l, Reset mode
+  #       console.log "(TODO) reset: " + @n
+  #     when "m"
+  #       @handleSGR @params
+  #     when "n"
+  #       @reportRowAndColumn()
+  #     when "r" # Set top and bottom margins (scroll region on VT100)
+  #       @setScrollRegion @n or 0, @m or @lines - 1
+  #     when "^"
+  #       # reserved
+  #       # Privacy Message (password verification), terminated by ST
+  #       # TODO
+  #     else
+  #       throw "no handler for CSI term: " + term
 
   handleSGR: (numbers) ->
     @buffer.setBrush @sgrInterpreter.buildBrush(@buffer.brush, numbers)

    @view.visualBell()
    # @trigger('bell')

  compilePatterns: ->
    @COMPILED_PATTERNS = ([new RegExp("^" + re), f] for re, f of @PATTERNS)

  feed: (data) ->
    @data += data

    while @data.length > 0
      match = null

      for pattern in @COMPILED_PATTERNS
        match = pattern[0].exec(@data)

        if match
          handler = pattern[1]
          handler.call(this, @data, match)
          @data = @data.slice(match[0].length)
          break

      break unless match

    @render()

    if @data.length > 500
      head = @data.slice(0, 100)
      hex = ("0x#{c.charCodeAt(0).toString(16)}" for c in head)
      console.log "failed matching: '" + Utf8.decode(head) + "' (" + hex.join() + ")" # [pos: " + (@dataIndex - count) + "]"

    @data.length is 0

  render: ->
    @view.render(@buffer.changes(), @buffer.cursorX, @buffer.cursorY)

  reportRowAndColumn: ->

# http://www.shaels.net/index.php/propterm/documents
# http://manpages.ubuntu.com/manpages/lucid/man7/urxvt.7.html
