content: -- | Miscellaneous functions that don't fit the project directly
  module Web.Tweet.Utils (
      hits
    , hits'
    , getTweets
    , displayTimeline
    , displayTimelineColor
    , lineByKey
    , bird
    , getConfigData
    , filterQuotes
    , filterReplies
    , filterRTs
    ) where
  
  import           Control.Composition
  import qualified Data.ByteString        as BS2
  import qualified Data.ByteString.Char8  as BS
  import           Data.List
  import           Data.List.Extra
  import           Data.Void
  import           Lens.Micro.Extras
  import           Text.Megaparsec
  import           Web.Tweet.Parser
  import           Web.Tweet.Types
  import           Web.Tweet.Utils.Colors
  
  -- | filter out retweets, and sort by most successful.
  hits :: Timeline -> Timeline
  hits = sortTweets . filterRTs
  
  -- | Filter out retweets and replies, and sort by most sucessful.
  hits' :: Timeline -> Timeline
  hits' = hits . filterReplies
  
  -- | Filter out retweets
  filterRTs :: Timeline -> Timeline
  filterRTs = filter ((/="RT @") . take 4 . view text)
  
  -- | Filter out replies
  filterReplies :: Timeline -> Timeline
  filterReplies = filter ((/="@") . take 1 . view text)
  
  -- | Filter out quotes
  filterQuotes :: Timeline -> Timeline
  filterQuotes = filter ((==Nothing) . view quoted)
  
  -- | Get a list of tweets from a response, returning author, favorites, retweets, and content.
  getTweets :: BS2.ByteString -> Either (ParseErrorBundle String Void) Timeline
  getTweets = parse parseTweet "" . BS.unpack
  
  -- | Display Timeline without color
  displayTimeline :: Timeline -> String
  displayTimeline (TweetEntity content _ u sn idTweet _ Nothing rts fave:rest) = concat [u
      , " ("
      , sn
      , ")"
      ,":\n    "
      ,fixNewline content
      ,"\n    "
      , "💜"
      -- , "♥ "
      ,show fave
      , " \61561  "
      -- ," ♺ "
      ,show rts
      , "  "
      , show idTweet
      ,"\n\n"
      ,displayTimeline rest]
  displayTimeline (TweetEntity content _ u sn idTweet _ (Just q) rts fave:rest) = concat [u
      , " ("
      , sn
      , ")"
      , ":\n    "
      , fixNewline content
      , "\n    "
      , "💜"
      , show fave
      , " \61561  "
      , show rts
      , "  "
      , show idTweet
      , "\n    "
      , _name q
      , " ("
      , _screenName q
      , ")"
      , ": "
      , _text q
      , "\n\n"
      , displayTimeline rest]
  displayTimeline [] = []
  
  bird :: String
  bird = toPlainBlue "🐦\n"
  
  -- | Display Timeline in color
  displayTimelineColor :: Timeline -> String
  displayTimelineColor (TweetEntity content _ u sn idTweet _ Nothing rts fave:rest) = concat [toYellow u
      , " ("
      , sn
      , ")"
      , ":\n    "
      , fixNewline content
      , "\n    "
      , toRed "💜"
      , " "
      , show fave
      , toGreen " \61561  " -- ♺ "
      , show rts
      , "  "
      , toBlue (show idTweet)
      , "\n\n"
      , displayTimelineColor rest]
  displayTimelineColor (TweetEntity content _ u sn idTweet _ (Just q) rts fave:rest) = concat [toYellow u
      , " ("
      , sn
      , ")"
      , ":\n    "
      , fixNewline content
      , "\n    "
      , toRed "💜"
      , " "
      , show fave
      , toGreen " \61561  "
      , show rts
      , "  "
      , toBlue (show idTweet)
      , "\n    "
      , toYellow $ _name q
      , " ("
      , _screenName q
      , ")"
      , ": "
      , _text q
      , "\n\n"
      , displayTimelineColor rest]
  displayTimelineColor [] = []
  
  -- | When displaying, newlines should include indentation.
  fixNewline :: String -> String
  fixNewline = replace "\n" "\n    "
  
  -- | sort tweets by most successful
  sortTweets :: Timeline -> Timeline
  sortTweets = sortBy compareTweet
      where compareTweet (TweetEntity _ _ _ _ _ _ _ r1 f1) (TweetEntity _ _ _ _ _ _ _ r2 f2) = compare (2*r2 + f2) (2*r1 + f1)
  
  -- | helper function to get the key as read from a file
  keyLinePie :: String -> String
  keyLinePie = takeWhile (/=':')
  
  -- | Pick out a key value from a key
  lineByKey :: BS.ByteString -> [(BS.ByteString, BS.ByteString)] -> BS.ByteString
  lineByKey = snd .* head .* (filter . (fst .@ (==)))
  
  -- | Filter a line of a file for only the actual data and no descriptors
  filterLine :: String -> String
  filterLine = reverse . takeWhile (not . (`elem` (" :" :: String))) . reverse
  
  -- | Get pairs of "key" to search for and actual values
  getConfigData :: FilePath -> IO [(BS.ByteString, BS.ByteString)]
  getConfigData filepath = zip <$> keys <*> content
      where content = map (BS.pack . filterLine) . lines <$> file
            keys    = map (BS.pack . keyLinePie) . lines <$> file
            file    = readFile filepath
  