{-#LANGUAGE FlexibleInstances, UndecidableInstances, IncoherentInstances #-}
module Data.Variant.ToFrom where

import Data.Variant
import Prelude hiding (toInteger)

class ToVariant a where
    toVariant :: a -> Variant

class FromVariant a where
    fromVariant :: Variant -> a

-- Variant itself implements to/from variant
instance ToVariant Variant where
    toVariant = id
instance FromVariant Variant where
    fromVariant = id

-- Various scalar instances

instance ToVariant String where
    toVariant = String
instance FromVariant String where
    fromVariant = flatten

instance ToVariant Bool where
    toVariant = Bool
instance FromVariant Bool where
    fromVariant = toBool

instance ToVariant Double where
    toVariant = Double
instance FromVariant Double where
    fromVariant = toDouble

instance ToVariant Integer where
    toVariant = Integer
instance FromVariant Integer where
    fromVariant = toInteger

instance ToVariant Int where
    toVariant = Integer . fromIntegral
instance FromVariant Int where
    fromVariant = fromIntegral . toInteger

-- Maybes of to/from variant types implement to/from variant
instance ToVariant a => ToVariant (Maybe a) where
    toVariant Nothing = Null
    toVariant (Just a) = toVariant a

instance FromVariant a => FromVariant (Maybe a) where
    fromVariant Null = Nothing
    fromVariant a = Just (fromVariant a)

-- Lists of variants
instance (ToVariant a, ToVariant b) => ToVariant [(a,b)] where
    toVariant xs =
        let (keys, values) = unzip xs
            vkeys = map toVariant keys
            vvalues = map toVariant values
        in AList $ zip vkeys vvalues

instance (FromVariant a, FromVariant b) => FromVariant [(a,b)] where
    fromVariant = map (\(k,v) -> (fromVariant k, fromVariant v)) . toAList

instance ToVariant a => ToVariant [a] where
    toVariant xs = List (map toVariant xs)

instance FromVariant a => FromVariant [a] where
    fromVariant = map fromVariant . values

-- Functions, up to 3 arguments
instance (FromVariant a, ToVariant b) => ToVariant (a -> b) where
    toVariant f = Function (\(x:_) -> toVariant (f $ fromVariant x))
instance (FromVariant a, FromVariant b, ToVariant c) => ToVariant (a -> b -> c) where
    toVariant f = Function (\(x:y:_) -> toVariant (f (fromVariant x) (fromVariant y)))
instance (FromVariant a, FromVariant b, FromVariant c, ToVariant d) => ToVariant (a -> b -> c -> d) where
    toVariant f = Function (\(x:y:z:_) -> toVariant (f (fromVariant x) (fromVariant y) (fromVariant z)))