content: {-# LANGUAGE OverloadedStrings #-}
  
  module Knives.ExtIP where
  
  import CommandLine
  import Network.HTTP.Simple
  import Data.Aeson
  import Data.Bool (bool)
  import Control.Monad ((>=>))
  
  data RExtIP = RExtIP
    { ip :: String } deriving Show
  
  instance FromJSON RExtIP where
    parseJSON = withObject "RExtIP" $ \v -> RExtIP
      <$> v .: "ip"
  
  data RLoc = RLoc
    {status      :: String
    , country    :: String
    , countryCode:: String
    , region     :: String
    , regionName :: String
    , city       :: String
    , zip        :: String
    , lat        :: Float
    , lon        :: Float
    , timezone   :: String
    , isp        :: String
    , org        :: String
    , as         :: String
    , query      :: String
    } deriving Show
  
  instance FromJSON RLoc where
    parseJSON = withObject "RLoc" $ \v -> RLoc
      <$> v .: "status"
      <*> v .: "country"
      <*> v .: "countryCode"
      <*> v .: "region"
      <*> v .: "regionName"
      <*> v .: "city"
      <*> v .: "zip"
      <*> v .: "lat"
      <*> v .: "lon"
      <*> v .: "timezone"
      <*> v .: "isp"
      <*> v .: "org"
      <*> v .: "as"
      <*> v .: "query"
    
  knifeExtIP :: ExtIPOptions -> IO ()
  knifeExtIP optsE = do
    res <- (parseRequest >=> httpLBS) $ bool url_ipv4 url_ipv6 $ ipv6 optsE
    putStrLn $ ipaddr res
    if (not $ nolocation optsE)
      then do
        loc <- location
        putStrLn $ loc
      else return ()
  
    where
      url_ipv4 = "https://ipv4.jsonip.com"
      url_ipv6 = "https://ipv6.jsonip.com"
      url_geo  = "http://ip-api.com/json/"
      ipaddr res = do
        let ipresult = eitherDecode $ getResponseBody res :: Either String RExtIP
        case ipresult of
          Left err -> do
            "Failed to obtain IP: " ++ err
          Right extIP -> do
            ip extIP
  
      location :: IO String
      location = do
        request <- parseRequest url_geo
        res <- httpLBS request
        let locres = eitherDecode $ getResponseBody res :: Either String RLoc
        case locres of
          Left err -> do
            return $ "Failed to obtain location: " ++ err
          Right loc -> do
            let str_result = "Location: "
                  ++ city loc ++ ", "
                  ++ country loc
                  ++ " (" ++ countryCode loc ++ ")\n"
                  ++ "Timezone: " ++ timezone loc ++ "\n"
                  
            return str_result
  