-- -----------------------------------------------------------------------------
-- | 
-- Module      :  Text.IPv6Addr
-- Copyright   :  (c) Michel Boucey 2011-2013
-- License     :  BSD-style
-- Maintainer  :  michel.boucey@gmail.com
-- Stability   :  provisional
--
-- Dealing with IPv6 address text representations,
-- canonization and manipulations.
--
-- -----------------------------------------------------------------------------

module Text.IPv6Addr
    (
      IPv6Addr
    , maybeIPv6Addr
    , maybePureIPv6Addr
    , maybeFullIPv6Addr
    , getIPv6AddrOf
    , ip6arpa
    , randIPv6Addr
    ) where

import Control.Monad (replicateM)
import Data.Char (intToDigit,isDigit,isHexDigit,toLower)
import Data.Function (on)
import Data.List (group,isSuffixOf,elemIndex,elemIndices,intersperse)
import Data.Maybe (catMaybes,fromJust,isJust)
import qualified Data.Text as T
import Data.Text.Read (decimal)
import Numeric (showIntAtBase)

import Text.IPv6Addr.Internal
import Text.IPv6Addr.Manip (sixteenBitsArbToken,partialRandAddr)
import Text.IPv6Addr.Types

-- | Returns 'Just' the text representation of a canonized
-- 'IPv6Addr' in conformation with RFC 5952, or 'Nothing'.
--
-- > maybeIPv6Addr "0:0::FFFF:192.0.2.128" == Just (IPv6Addr "::ffff:192.0.2.128")
--
maybeIPv6Addr :: T.Text -> Maybe IPv6Addr
maybeIPv6Addr t = maybeTokIPv6Addr t >>= ipv6TokensToIPv6Addr

-- | Returns 'Just' a pure 'IPv6Addr', or 'Nothing'.
--
-- > maybePureIPv6Addr "::ffff:192.0.2.128" == Just (IPv6Addr "::ffff:c000:280")
--
maybePureIPv6Addr :: T.Text -> Maybe IPv6Addr
maybePureIPv6Addr t = maybeTokPureIPv6Addr t >>= ipv6TokensToIPv6Addr

-- | Returns 'Just' a pure and expanded 'IPv6Addr', or 'Nothing'.
--
-- > maybeFullIPv6Addr "::ffff:192.0.2.128" == Just (IPv6Addr "0000:0000:0000:0000:0000:ffff:c000:0280")
--
maybeFullIPv6Addr :: T.Text -> Maybe IPv6Addr
maybeFullIPv6Addr t =
   maybeTokPureIPv6Addr t >>= \m -> ipv6TokensToIPv6Addr $ expandTokens $ fromDoubleColon m

-- | Returns 'Just' the reverse lookup domain name corresponding of the given IPv6 address,
-- as define in RFC 3596 Section 2.5, or 'Nothing'.
--
-- > ip6arpa "4321:0:1:2:3:4:567:89ab" == Just "b.a.9.8.7.6.5.0.4.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.0.0.0.0.1.2.3.4.ip6.arpa."
--
ip6arpa :: T.Text -> Maybe T.Text
ip6arpa t =
    case maybeFullIPv6Addr t of
         Just (IPv6Addr a) -> Just $ revaddr a T.empty
         Nothing           -> Nothing 
  where
    revaddr i o =
        if i == T.empty then o `T.append` T.pack "ip6.arpa."
        else do let c = T.last i
                revaddr (T.init i)
                        (if c /= ':' then o `T.append` T.pack [c] `T.append` T.pack "." else o)

-- | Returns 'Just' the canonized 'IPv6Addr' of the given network interface,
-- or Nothing.
--
-- > getIPv6AddrOf "eth0"
--
getIPv6AddrOf :: String -> IO (Maybe IPv6Addr)
getIPv6AddrOf s = do
     l <- networkInterfacesIPv6AddrList
     case lookup s l of
         Just a  -> return $ maybeIPv6Addr $ T.pack $ show a
         Nothing -> return Nothing

-- | Returns a random 'IPv6Addr'
randIPv6Addr :: IO IPv6Addr
randIPv6Addr = partialRandAddr 8 >>= \p -> return $ IPv6Addr $ ipv6TokensToText p