{-# LANGUAGE OverloadedStrings #-}
module HTTP.ThirdParty.ProxyList(getProxyList, randomProxy) where

import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy.Char8 as LBS
import Control.Lens
import Network.Wreq
import Text.Regex.Posix
import Text.Regex.Base
import qualified Codec.Binary.Base64.String as B64
import System.Random

url :: String
url = "https://proxy-list.org/english/search.php"
    
myOptions = defaults & 
          (param "country" .~ ["any"]) &
          (param "type"    .~ ["any"]) & 
          (param "port"    .~ ["any"])

sslOptions :: Bool -> Options
sslOptions False =
    myOptions & 
    (param "search" .~ ["ssl-no"]) &
    (param "ssl" .~ ["no"])
sslOptions True =
    myOptions &
    (param "search" .~ ["ssl-yes"]) &
    (param "ssl" .~ ["yes"])

getProxyList :: Bool -> IO [(String, Int)]
getProxyList ssl = do
    let opts = sslOptions ssl
    resp <- getWith opts url
    let body = resp ^. responseBody
    let bodyString = T.unpack (TE.decodeUtf8 (toStrictString body))
    let base64Strings = getAllTextMatches (bodyString =~ base64Pat :: AllTextMatches [] String)
    let ipStrings = [decodeBase64 str | str <- base64Strings]
    return [transform str | str <- ipStrings]
    where base64Pat = "Proxy\\('([^']+)'\\)" :: String
          decodeBase64 :: String -> String
          decodeBase64 base64 =
              let _:str:_ = getAllTextSubmatches (base64 =~ base64Pat :: AllTextSubmatches [] String) in
                  B64.decode str  
          ipPortPat = "([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}):([0-9]+)" :: String
          transform :: String -> (String, Int)
          transform str =
              let _:ip:port:_ = getAllTextSubmatches (str =~ ipPortPat :: AllTextSubmatches [] String) in
                  (ip, (read port :: Int))
      
randomProxy :: [(String, Int)] -> IO (String, Int)
randomProxy list = do
    let len = (length list)
    gOld <- getStdGen
    let (x, gNew) = next gOld
    setStdGen gNew
    let f:_ = drop (x `mod` len) list
    return f

toStrictString = BS.concat . LBS.toChunks