{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell   #-}
{-# LANGUAGE TypeOperators     #-}

module Language.SexpGrammar.Class where

import Prelude hiding ((.), id)

import Control.Arrow
import Control.Category

import Data.InvertibleGrammar.TH
import qualified Data.List.NonEmpty as NE
import Data.Data
import Data.Map (Map)
import Data.Scientific
import Data.Set (Set)
import Data.Text (Text)
import qualified Data.Map as Map
import qualified Data.Set as Set

import Language.Sexp.Types
import Language.SexpGrammar.Base
import Language.SexpGrammar.Combinators

class SexpIso a where
  sexpIso :: SexpG a

  default sexpIso :: (Enum a, Bounded a, Eq a, Data a) => SexpG a
  sexpIso = enum

instance SexpIso Bool where
  sexpIso = bool

instance SexpIso Int where
  sexpIso = int

instance SexpIso Integer where
  sexpIso = integer

instance SexpIso Double where
  sexpIso = double

instance SexpIso Scientific where
  sexpIso = real

instance SexpIso Text where
  sexpIso = string

instance (SexpIso a, SexpIso b) => SexpIso (a, b) where
  sexpIso = pair . vect (el sexpIso >>> el sexpIso)

instance (Ord k, SexpIso k, SexpIso v) => SexpIso (Map k v) where
  sexpIso = iso Map.fromList Map.toList . list (el sexpIso)

instance (Ord a, SexpIso a) => SexpIso (Set a) where
  sexpIso = iso Set.fromList Set.toList . list (el sexpIso)

instance (SexpIso a) => SexpIso (Maybe a) where
  sexpIso = coproduct
    [ $(grammarFor 'Nothing) . kw (Kw "nil")
    , $(grammarFor 'Just) . sexpIso
    ]

instance (SexpIso a) => SexpIso [a] where
  sexpIso = list $ rest sexpIso

instance (SexpIso a) => SexpIso (NE.NonEmpty a) where
  sexpIso =
    iso (\(x,xs) -> x NE.:| xs )
        (\(x NE.:| xs) -> (x, xs)) .
    pair .
    list (el sexpIso >>> rest sexpIso)