{-# LANGUAGE TemplateHaskell #-}

module Language.Haskell.Util.Cons
	( decons
	) where

import Data.List
import Debug.Trace.LocationTH
import Language.Haskell.TH
import Text.Printf

-- | Generate a deconstructor that takes as input a value and returns 'Maybe'
-- the deconstructed value.
--
-- This functions as a Data Constructor inverter.  If the number of
-- parameters of a constructor is not equal to 1, 'Maybe' a tuple is returned.--  The result of this function is defined only when 
--
-- Example:
--
-- @
-- $(decons 'Right) (Right "foo") :: Maybe String
-- $(decons '(:)) "bar" :: Maybe (Char, String)
-- @
decons :: Name -> Q Exp
decons n_cons = do
    n_x <- newName "x"
    info <- reify n_cons
    let typeOfCons =
            case info of
                (DataConI _ t _ _)  ->
                    t
                _                   ->
                    $failure $ printf "the referent of the name '%s' is invalid; it should be a data constructor ('DataConI')" (nameBase n_cons)
    --flip seq (return ()) $ typeOfCons  -- TODO: jj
    let numParams :: Type -> Integer
        numParams (ForallT _ _ t) = numParams t
        numParams (VarT _)        = 0
        numParams (ConT _)        = 0
        numParams (TupleT _)      = 0
        numParams (ArrowT)        = 0
        numParams (ListT)         = 0
        numParams (AppT ArrowT _) = 1
        numParams (AppT a b)      = numParams a + numParams b
        numParams (SigT t _)      = numParams t
    names <- sequence . flip genericReplicate (newName "a") $ numParams typeOfCons
    return $
        LamE [VarP n_x] $                                                  -- \x ->
            CaseE (VarE n_x) $                                             --     case x of
                [ flip (Match (ConP n_cons $ map VarP names)) [] $         --         (Cons a b …) ->
                    NormalB . AppE (ConE 'Just) . TupE . map VarE $ names  --             Just (a, b, …)
                , flip (Match WildP) [] $                                  --         _            ->
                    NormalB $ ConE 'Nothing                                --             Nothing
                ]