packages feed

agentx-0.1.0.5: src/Network/Protocol/Snmp/AgentX/MIBTree/Tree.hs

{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
module Network.Protocol.Snmp.AgentX.MIBTree.Tree 
( Zippers(..)
, Contexted(..)
, Tree(..)
, Zipper
, hasNext
, hasLevel
, goClosest
, Move(..)
, regPair
)
where

import Data.Maybe (fromJust)
import Data.Monoid hiding (First)
import Network.Protocol.Snmp.AgentX.Packet (Context)
import Network.Protocol.Snmp (OID)
import Data.Algorithm.Diff
import Data.List (sort)
import Prelude 

data Move b a = Next (b a)
              | Level (b a)
              deriving Eq

instance (Show a, Show (b a)) => Show (Move b a) where
    show (Next x) = "\nNext " ++ show x
    show (Level x) = "\nLevel " ++ show x

type Moving b a = [Move b a]

type Zipper b a = (b a, Moving b a)

class Zippers b where
    toZipper :: b a -> Zipper b a
    attach :: b a -> Zipper b a -> Zipper b a
    goNext :: Zipper b a -> Maybe (Zipper b a)
    goLevel :: Zipper b a -> Maybe (Zipper b a)
    goBack  :: Zipper b a -> Maybe (Zipper b a)
    goUp    :: Zipper b a -> Maybe (Zipper b a)
    top     :: Zipper b a -> Zipper b a
    oid     :: Contexted a => Zipper b a -> OID
    cursor  :: Contexted a => Zipper b a -> Maybe (Integer, Maybe Context)
    setCursor :: Contexted a => OID -> Maybe Context -> Zipper b a -> Maybe (Zipper b a)

data Tree a = Node a (Tree a) (Tree a)
            | Empty
            deriving (Functor, Eq)

class Contexted a where
    index :: a -> Integer
    context :: a -> Maybe Context
    withValue :: a -> Bool

instance Contexted a => Monoid (Tree a) where
    mempty = Empty
    mappend a Empty = a
    mappend Empty a = a
    mappend (Node v next link) x@(Node v1 next1 link1)
       | index v == index v1 && context v == context v1 = Node v (next <> next1) (link <> link1) 
       | otherwise = Node v (next <> x) link 


instance (Contexted a, Show a) => Show (Tree a) where
    show f = unlines $ drawLevel f
      where
        drawLevel Empty = []
        drawLevel (Node v next link) = (show (index v) <> " " <> show v <> " ") : (drawSubtree next link)

        drawSubtree next link = (shift "`- " " | " (drawLevel link)) <> drawLevel next
    
        shift first rest = zipWith (++) (first : repeat rest)

-- testTree :: Tree (Integer, Maybe String)
-- testTree = Node (0, Just "first") (Node (1, Just "second") Empty Empty) (Node (10, Just "third") Empty Empty)

instance Contexted (Integer, Maybe String) where
    index = fst
    context = undefined
    withValue (_, Nothing) = False
    withValue _ = True

instance Zippers Tree where
    toZipper t = (t, [])

    attach t (Empty, bs) = (t, bs)
    attach t (Node v next _, bs) = (Node v next t, bs)

    goNext (Empty, _) = Nothing
    goNext (Node _ Empty _, _) = Nothing
    goNext (Node x  next link, bs) = Just (next, Next (Node x   Empty link):bs)

    goLevel (Empty, _) = Nothing
    goLevel (Node _ _ Empty, _) = Nothing
    goLevel (Node x next link, bs) = Just (link, Level (Node x next Empty):bs)

    goBack (_, []) = Nothing
    goBack (t, Next  (Node x  Empty  link):bs) = Just (Node x  t    link, bs)
    goBack (t, Level (Node x  next  Empty):bs) = Just (Node x  next t   , bs)
    goBack _ = Nothing

    goUp (_, []) = Nothing
    goUp (t, Next  (Node x  Empty link ):bs) = goUp (Node x  t    link, bs)
    goUp (t, Level (Node x  next  Empty):bs) = Just (Node x  next t   , bs)
    goUp _ = Nothing

    top (t,[]) = (t,[])  
    top z = top (fromJust $ goBack z)

    oid (z, m) = foldl fun [gi z] m
      where 
        fun xs (Next{}) = xs
        fun xs (Level x) = gi x : xs
        gi :: Contexted a => Tree a -> Integer
        gi (Node x _ _) = index x
        gi _ = error "oid"

    setCursor [] _ z = Just (top z)
    setCursor ys c z = walk ys (top z)
      where
      giz (Node x _ _  , _) = (index x, context x)
      giz _ = error "setCursor: giz Empty Tree"
      walk [] t = Just t
      walk (x : []) t
        | (x, c) == giz t = Just t
        | otherwise = goNext t >>= walk (x : []) 
      walk (x : xs) t 
        | x == fst (giz t) = goLevel t >>= walk xs 
        | otherwise = goNext  t >>= walk (x : xs) 

    cursor ((Node v _ _), _) = Just (index v, context v)
    cursor (Empty       , _) = Nothing

hasLevel :: Zipper Tree a -> Bool
hasLevel (Node _ _ Empty, _) = False
hasLevel _ = True

hasNext :: Zipper Tree a -> Bool
hasNext (Node _ Empty _, _) = False
hasNext _ = True

goClosest :: Contexted a => OID -> Maybe Context -> Zipper Tree a -> Zipper Tree a
goClosest [] _ z = top z
goClosest ys c z = walk ys (top z)
  where
  giz (Node x _ _, _) = (index x, context x)
  giz _ = error "goClosest: giz Empty Tree"
  walk [] z' = z'
  walk (x : []) z'
    | (x,c) == giz z' = z'
    | otherwise = maybe z' (walk (x : [])) (goNext z')
  walk (x : xs) z'
    | x == fst (giz z') = maybe z' (walk xs) (goLevel z')
    | otherwise = maybe z' (walk (x : xs)) (goNext z')

type RegType = [(OID, Maybe Context)]
type ToRegistrate = [(OID, Maybe Context)]
type ToUnRegistrate = [(OID, Maybe Context)]
                      
toVList :: Contexted a => (OID, Tree a) -> RegType
toVList (path, Node a Empty Empty)
  | withValue a = [(index a : path, context a)]
  | otherwise = []
toVList (path, Node a level next) 
  | withValue a = (index a : path, context a) : toVList (path, level) <> toVList (index a : path, next)
  | otherwise = toVList (path, level) <> toVList (index a : path, next)
toVList (_, Empty) = []

regPair :: Contexted a => Tree a -> Tree a -> (ToRegistrate, ToUnRegistrate)
regPair old new = splitDiff $ getDiff (reverseOid $ toVList ([], old)) (reverseOid $ toVList ([], new))

reverseOid :: [(OID, Maybe Context)] -> [(OID, Maybe Context)]
reverseOid = sort . map fun 
  where
    fun (xs, mc) = (reverse xs, mc)

splitDiff :: [Diff a] -> ([a], [a])
splitDiff = splitDiff' ([], []) 
  where
    splitDiff' (toUnReg, toReg) [] = (toUnReg, toReg)
    splitDiff' (toUnReg, toReg) (Both _ _ : xs) = splitDiff' (toUnReg, toReg) xs
    splitDiff' (toUnReg, toReg) (First x : xs) = splitDiff' (x : toUnReg, toReg) xs
    splitDiff' (toUnReg, toReg) (Second x : xs) = splitDiff' (toUnReg, x : toReg) xs