print :: B.ByteString -> Quake ()
print txt = do
    initialized <- use $ globals.gCon.cInitialized

    when initialized $ do
      let (mask, txtpos) = if txt `B.index` 0 == 1 || txt `B.index` 0 == 2
                             then (128, 1)
                             else (0, 0)

      printTxt txtpos mask

  where printTxt :: Int -> Int -> Quake ()
        printTxt txtpos mask
          | txtpos >= B.length txt = return ()
          | otherwise = do
              console <- use $ globals.gCon

              let c = txt `BC.index` txtpos
                  len = findWordLen (console^.cLineWidth) txtpos 0

              -- word wrap
              when (len /= (console^.cLineWidth) && (console^.cX) + len > (console^.cLineWidth)) $
                globals.gCon.cX .= 0

              use (clientGlobals.cgCR) >>= \v ->
                when (v /= 0) $ do
                  globals.gCon.cCurrent -= 1
                  clientGlobals.cgCR .= 0

              use (globals.gCon) >>= \console' ->
                when ((console'^.cX) == 0) $ do
                  lineFeed
                  -- mark time for transparent overlay
                  when ((console'^.cCurrent) >= 0) $ do
                    realTime <- use $ globals.gCls.csRealTime
                    globals.gCon.cTimes.ix ((console'^.cCurrent) `mod` Constants.numConTimes) .= fromIntegral realTime

              case c of
                '\n' -> do
                  globals.gCon.cX .= 0
                  printTxt (txtpos + 1) mask

                '\r' -> do
                  globals.gCon.cX .= 0
                  clientGlobals.cgCR .= 1
                  printTxt (txtpos + 1) mask

                _ -> -- display character and advance
                  use (globals.gCon) >>= \console' -> do
                    let y = (console'^.cCurrent) `mod` (console'^.cTotalLines)
                        idx = y * (console'^.cLineWidth) + (console'^.cX)
                        b = (ord c) .|. mask .|. (console'^.cOrMask)
                    io $ MSV.write (console'^.cText) idx (chr b)
                    globals.gCon.cX += 1
                    when ((console'^.cX) + 1 >= (console'^.cLineWidth)) $
                      globals.gCon.cX .= 0
                    printTxt (txtpos + 1) mask

        findWordLen :: Int -> Int -> Int -> Int
        findWordLen lineWidth txtpos idx =
          if idx >= lineWidth || idx >= (B.length txt - txtpos)
            then idx
            else if txt `BC.index` (idx + txtpos) <= ' '
                   then idx
                   else findWordLen lineWidth txtpos (idx + 1)
print :: B.ByteString -> Quake ()
print txt = do
    initialized <- use $ globals.gCon.cInitialized

    when initialized $ do
      let (mask, txtpos) = if txt `B.index` 0 == 1 || txt `B.index` 0 == 2
                             then (128, 1)
                             else (0, 0)

      printTxt txtpos mask

  where printTxt :: Int -> Int -> Quake ()
        printTxt txtpos mask
          | txtpos >= B.length txt = return ()
          | otherwise = do
              console <- use $ globals.gCon

              let c = txt `BC.index` txtpos
                  len = findWordLen (console^.cLineWidth) txtpos 0

              -- word wrap
              when (len /= (console^.cLineWidth) && (console^.cX) + len > (console^.cLineWidth)) $
                globals.gCon.cX .= 0

              use (clientGlobals.cgCR) >>= \v ->
                when (v /= 0) $ do
                  globals.gCon.cCurrent -= 1
                  clientGlobals.cgCR .= 0

              use (globals.gCon) >>= \console' ->
                when ((console'^.cX) == 0) $ do
                  lineFeed
                  -- mark time for transparent overlay
                  when ((console'^.cCurrent) >= 0) $ do
                    realTime <- use $ globals.gCls.csRealTime
                    globals.gCon.cTimes.ix ((console'^.cCurrent) `mod` Constants.numConTimes) .= fromIntegral realTime

              case c of
                '\n' -> do
                  globals.gCon.cX .= 0
                  printTxt (txtpos + 1) mask

                '\r' -> do
                  globals.gCon.cX .= 0
                  clientGlobals.cgCR .= 1
                  printTxt (txtpos + 1) mask

                _ -> -- display character and advance
                  use (globals.gCon) >>= \console' -> do
                    let y = (console'^.cCurrent) `mod` (console'^.cTotalLines)
                        idx = y * (console'^.cLineWidth) + (console'^.cX)
                        b = (ord c) .|. mask .|. (console'^.cOrMask)
                    io $ MSV.write (console'^.cText) idx (chr b)
                    globals.gCon.cX += 1
                    when ((console'^.cX) + 1 >= (console'^.cLineWidth)) $
                      globals.gCon.cX .= 0
                    printTxt (txtpos + 1) mask

        findWordLen :: Int -> Int -> Int -> Int
        findWordLen lineWidth txtpos idx =
          if idx >= lineWidth || idx >= (B.length txt - txtpos)
            then idx
            else if txt `BC.index` (idx + txtpos) <= ' '
                   then idx
                   else findWordLen lineWidth txtpos (idx + 1)
print txt = do
    initialized <- use $ globals.gCon.cInitialized

    when initialized $ do
      let (mask, txtpos) = if txt `B.index` 0 == 1 || txt `B.index` 0 == 2
                             then (128, 1)
                             else (0, 0)

      printTxt txtpos mask

  where printTxt :: Int -> Int -> Quake ()
        printTxt txtpos mask
          | txtpos >= B.length txt = return ()
          | otherwise = do
              console <- use $ globals.gCon

              let c = txt `BC.index` txtpos
                  len = findWordLen (console^.cLineWidth) txtpos 0

              -- word wrap
              when (len /= (console^.cLineWidth) && (console^.cX) + len > (console^.cLineWidth)) $
                globals.gCon.cX .= 0

              use (clientGlobals.cgCR) >>= \v ->
                when (v /= 0) $ do
                  globals.gCon.cCurrent -= 1
                  clientGlobals.cgCR .= 0

              use (globals.gCon) >>= \console' ->
                when ((console'^.cX) == 0) $ do
                  lineFeed
                  -- mark time for transparent overlay
                  when ((console'^.cCurrent) >= 0) $ do
                    realTime <- use $ globals.gCls.csRealTime
                    globals.gCon.cTimes.ix ((console'^.cCurrent) `mod` Constants.numConTimes) .= fromIntegral realTime

              case c of
                '\n' -> do
                  globals.gCon.cX .= 0
                  printTxt (txtpos + 1) mask

                '\r' -> do
                  globals.gCon.cX .= 0
                  clientGlobals.cgCR .= 1
                  printTxt (txtpos + 1) mask

                _ -> -- display character and advance
                  use (globals.gCon) >>= \console' -> do
                    let y = (console'^.cCurrent) `mod` (console'^.cTotalLines)
                        idx = y * (console'^.cLineWidth) + (console'^.cX)
                        b = (ord c) .|. mask .|. (console'^.cOrMask)
                    io $ MSV.write (console'^.cText) idx (chr b)
                    globals.gCon.cX += 1
                    when ((console'^.cX) + 1 >= (console'^.cLineWidth)) $
                      globals.gCon.cX .= 0
                    printTxt (txtpos + 1) mask

        findWordLen :: Int -> Int -> Int -> Int
        findWordLen lineWidth txtpos idx =
          if idx >= lineWidth || idx >= (B.length txt - txtpos)
            then idx
            else if txt `BC.index` (idx + txtpos) <= ' '
                   then idx
                   else findWordLen lineWidth txtpos (idx + 1)