{-# LANGUAGE DeriveGeneric              #-}
{-# LANGUAGE DuplicateRecordFields      #-}
{-# LANGUAGE InstanceSigs               #-}
{-# LANGUAGE OverloadedStrings          #-}

module HaskellWorks.Data.Network.Ip.Ip
  ( IpBlock(..)
  , isValidIpBlock
  , firstAddress
  , lastAddress
  ) where

import Control.Applicative
import Control.Monad
import Data.Char
import Data.Maybe
import Data.Word
import GHC.Generics
import HaskellWorks.Data.Bits.BitWise
import Text.Read

import qualified Data.Attoparsec.Text                  as AP
import qualified Data.Bits                             as B
import qualified Data.Text                             as T
import qualified HaskellWorks.Data.Network.Ip.Internal as I
import qualified HaskellWorks.Data.Network.Ip.Ipv4     as I4
import qualified HaskellWorks.Data.Network.Ip.Ipv6     as I6
import qualified Text.ParserCombinators.ReadPrec       as RP

data IpBlock = IpBlockV4 I4.Ipv4Block | IpBlockV6 I6.Ipv6Block
  deriving (Eq, Ord, Generic)

instance Show IpBlock where
  showsPrec _ (IpBlockV4 a) = shows a
  showsPrec _ (IpBlockV6 a) = shows a

instance Read IpBlock where
  readsPrec _ s =
    case readMaybe s :: Maybe I4.Ipv4Block of
      Just ipv4 -> [(IpBlockV4 ipv4, "")]

      Nothing ->
        case readMaybe s :: Maybe I6.Ipv6Block of
          Just ipv6 -> [(IpBlockV6 ipv6, "")]
          Nothing   -> []

isValidIpBlock :: IpBlock -> Bool
isValidIpBlock (IpBlockV4 b) = I4.isValidIpv4Block b
isValidIpBlock (IpBlockV6 b) = I6.isValidIpv6Block b

firstAddress :: IpBlock -> (Word32, Word32, Word32, Word32)
firstAddress (IpBlockV4 i4b)                 = firstAddress (IpBlockV6 (I6.ipv4BlockToMappedIpv6Block i4b))
firstAddress (IpBlockV6 (I6.Ipv6Block ip _)) = I6.words ip

lastAddress :: IpBlock -> (Word32, Word32, Word32, Word32)
lastAddress (IpBlockV4 ib) = (0, 0, 0xFFFF, I4.word (I4.lastIpv4Address ib))
lastAddress (IpBlockV6 (I6.Ipv6Block ip (I6.Ipv6NetMask msk))) =
    let (w1, w2, w3, w4) = I6.words ip
        lt = I6.masksIpv6 $ fromIntegral msk
        w1' = w1 .|. (lt !! 0)
        w2' = w2 .|. (lt !! 1)
        w3' = w3 .|. (lt !! 2)
        w4' = w4 .|. (lt !! 3) in
      (w1', w2', w3', w4')