{-# language BangPatterns #-}

module Language.Asn.ObjectIdentifier
  ( fromList
  , suffixSingleton
  , appendSuffix
  , isPrefixOf
  , stripPrefix
  , encodeString
  , encodeByteString
  , encodeText
  )
  where

import Control.Monad.ST (runST)
import Language.Asn.Types
import Data.Maybe (isJust)
import Data.Text (Text)
import Data.ByteString (ByteString)
import Data.Word (Word32)
import Data.Primitive (PrimArray(..))
import qualified Data.Text as Text
import qualified Data.ByteString as ByteString
import qualified Data.ByteString.Char8 as BC8
import qualified Data.Vector.Primitive as PV
import qualified Data.List as List
import qualified Data.Primitive as PM
import qualified GHC.Exts as E

fromList :: [Word] -> ObjectIdentifier
fromList = ObjectIdentifier . E.fromList

suffixSingleton :: Word -> ObjectIdentifierSuffix
suffixSingleton = ObjectIdentifierSuffix . singletonPrimArray

appendSuffix :: ObjectIdentifier -> ObjectIdentifierSuffix -> ObjectIdentifier
appendSuffix (ObjectIdentifier a) (ObjectIdentifierSuffix b) = ObjectIdentifier (mappend a b)

isPrefixOf :: ObjectIdentifier -> ObjectIdentifier -> Bool
isPrefixOf a b = isJust (stripPrefix a b)

stripPrefix :: ObjectIdentifier -> ObjectIdentifier -> Maybe ObjectIdentifierSuffix
stripPrefix (ObjectIdentifier a) (ObjectIdentifier b) =
  let lenA = PM.sizeofPrimArray a
   in if (lenA <= PM.sizeofPrimArray b) && (upgradeArray a == PV.take lenA (upgradeArray b))
        then Just (ObjectIdentifierSuffix (unsafeDropPrimArray lenA b))
        else Nothing

upgradeArray :: PM.Prim a => PrimArray a -> PV.Vector a
upgradeArray b@(PrimArray a) = PV.Vector 0 (PM.sizeofPrimArray b) (PM.ByteArray a)

singletonPrimArray :: PM.Prim a => a -> PrimArray a
singletonPrimArray a = runST $ do
  m <- PM.newPrimArray 1
  PM.writePrimArray m 0 a
  PM.unsafeFreezePrimArray m

unsafeDropPrimArray :: PM.Prim a => Int -> PrimArray a -> PrimArray a
unsafeDropPrimArray !n !xs = runST $ do
  m <- PM.newPrimArray (PM.sizeofPrimArray xs - n)
  PM.copyPrimArray m 0 xs n (PM.sizeofPrimArray xs - n)
  PM.unsafeFreezePrimArray m

encodeString :: ObjectIdentifier -> String
encodeString = List.intercalate "." . map show . E.toList . getObjectIdentifier

encodeByteString :: ObjectIdentifier -> ByteString
encodeByteString = BC8.pack . encodeString

encodeText :: ObjectIdentifier -> Text
encodeText = Text.pack . encodeString