packages feed

matsuri-0.0.4: Buffers.hs

-- | Buffers.hs
-- A module with defenition of buffer structures (structures which
-- contain text and info about opened rooms and privates).

module Buffers where

import Utils

import Network.XMPP
import Network.XMPP.MUC
import Control.Concurrent (ThreadId)
import Graphics.Vty (Event)
import qualified Data.Map as M
import Data.List
import Data.List.Split
import Data.Maybe


type Buffers = M.Map Key Buffer
type Key = String
data Buffer = BufAccount Account
            | BufChat Chat
            | BufGroup Group
            | BufRoom Room
            | BufUnknown -- ^ fallback buffer

-- show instances
instance Show Buffer where
    show (BufAccount acc) =
      case connection acc of
        OK _ _ -> c:" [o] "++accName acc
        NoConnection -> c:" [_] "++accName acc
        Trying -> c:" [!] "++accName acc
      where c = if accCollapsed acc then '+' else '-'
    show (BufGroup grp) = coll++name (grpName grp)
      where coll = if grpCollapsed grp then "  +++ "
                                       else "  --- "
    show (BufChat chat) = "  ["++(show $ status chat)++
                          "] "++(itemJid $ item chat)
    show (BufRoom room)
      = " "++mark occ++" "++name (roomName room)
      where
        -- replace +{o} to +{C}
        fix (c:_:cs) = (reverse cs)++"C"++[c]
        -- do conf mark (+{C}, -[C], +<C>, etc)
        mark (Just occ') = fix $ reverse $ roomBrackets occ' True
        mark _           = " [C]"
        occ = M.lookup (roomNick room) (roomOccupants room)

-- tmp
name = drop 1 . dropWhile (/='|')

instance Show Status where
    show (Status StatusOnline  _) = "o"
    show (Status StatusAway    _) = "a"
    show (Status StatusChat    _) = "c"
    show (Status StatusDND     _) = "d"
    show (Status StatusXA      _) = "n"
    show (Status StatusOffline _) = "_"

instance Show Occupant where
    show occ = br ++ nick
      where
        br = roomBrackets occ False
        nick | null br   = occNick occ
             | otherwise = " " ++ occNick occ
-- | Draw room brackets (<>, {} or []).
roomBrackets :: Occupant
             -> Bool -- ^ draw or not default status ([o])
             -> String
roomBrackets occ always =
    role++(aff (occAffiliation occ))
  where
    role = role' (occRole occ)
    role' RModerator    = "+"
    role' RVisitor      = "-"
    role' _ | always    = " "
            | otherwise = ""
    ---
    s = head $ show (occStatus occ)
    ---
    aff AOwner = '<':s:'>':[]
    aff AAdmin = '{':s:'}':[]
    aff _ | always || (not $ null role) = '[':s:']':[]
          | otherwise                 = ""
-- | Draw all room occupants (like /names in irssi).
showOccupants :: Occupants -> Int -> String
showOccupants occs w =
    "Names:\n"++
    (intercalate "\n" $ map concat $ chunk row occs')
  where
    occs' = map drawOcc (M.elems occs)
    drawOcc occ
      = (take' (len-1) ' ' $ roomBrackets occ True
                           ++ " " ++ occNick occ)
        ++ " "
    row = (w - offset) `div` len
    len = 23
    offset = 9

-- | Show room list (like /names, using many columns).
showRoomList :: [(String, Maybe String)] -> Int -> String
showRoomList list w =
    "List:\n"++
    (intercalate "\n" $ map concat $ chunk row list')
  where
    list' = map drawElem list
    drawElem (l, _) = take' (len-1) ' ' l ++ " "
    row = (w - offset) `div` len
    len = 23
    offset = 9


-- GDAT

data Account = Account
    { accName :: String
    , username :: String
    , server :: String
    , password :: String
    , resource :: String
    , priority :: Integer
    , defaultNick :: String
    , connection :: Connection
    , accCollapsed :: Bool
    , accContents :: [Content]
    }

data Connection = OK TCPConnection ThreadId
                | NoConnection
                | Trying

-- TODO: check for connection?
getC :: Account -> TCPConnection
getC acc = let OK c _ = connection acc
           in c

data Group = Group
    { grpName :: String
    , grpCollapsed :: Bool
    , grpContents :: [Content]
    , grpItems :: [String]
    }

data Chat = Chat
    { item :: RosterItem
    , status :: Status
    , chatName :: String
    , chatContents :: [Content]
    }

data Room = Room
    { roomName :: String
    , roomContents :: [Content]
    , roomNick :: String
    , roomSubject :: String
    , roomOccupants :: Occupants
    }
type Occupants = M.Map String Occupant

-- | New contents goes at top for more eaiser insert.
data Content = Msg String
             | MyMsg String
             | HistoryMsg String
             | InfoMsg String
             | ErrorMsg String

-- | Event which pushed/popped from/to MVar.
data MEvent = VtyEvent Event
            | InsBuffer Key Buffer
            | NewMsg Key Content
            | NewStatus Key Status
            | NewRoomMsg Key (String, String, Maybe String, String, Bool)
            | RoomPresence Key (GroupchatPresence, Occupant)
            | RoomList Key [(String, Maybe String)]


insElem :: Key -> Buffer -> Buffers -> Buffers
insElem k = M.insert k

getAcc :: Key -> Buffers -> Account
getAcc k buffers =
    case M.lookup k buffers of
      Just (BufAccount acc) -> acc

getBuf :: Key -> Buffers -> Buffer
getBuf k buffers =
    case M.lookup k buffers of
      Just buf -> buf
      _ -> BufUnknown

isBuf :: Key -> Buffers -> Bool
isBuf k = isJust . M.lookup k

-- | Kill room should run leaveGroupchat
killBuffers :: String -> Buffers -> Buffers
killBuffers startswith =
    M.filterWithKey (\k _ -> not $ startswith `isPrefixOf` k)