{-# LANGUAGE DeriveDataTypeable #-}

module Language.Grammars.ZipperAG.Examples.LET.Let_Bidi where

import Data.Generics.Zipper
import Language.Grammars.ZipperAG
import Language.Grammars.ZipperAG.Examples.LET.Let_DataTypes_Boilerplate

-- Forward Transformation (GET)
getRootC_RootA :: Zipper a -> RootA
getRootC_RootA ag = case (constructor ag) of
                     "RootC" -> RootA (getLetC_LetA $ ag.$1) (createLink ag)

getLetC_LetA :: Zipper a -> LetA
getLetC_LetA ag = case (constructor ag) of
                   "LetC" -> LetA (getListC_ListA $ ag.$1) (getInC_IntA $ ag.$2) (createLink ag)

getInC_IntA :: Zipper a -> InA
getInC_IntA ag = case (constructor ag) of
                   "InC" -> InA (getE_A $ ag.$1) (createLink ag)

getListC_ListA :: Zipper a -> ListA
getListC_ListA ag = case (constructor ag) of
                      "ConsLetC"    -> ConsLetA (lexeme_ConsLetC ag) (getLetC_LetA $ ag.$2) (getListC_ListA $ ag.$3) (createLink ag)
                      "ConsAssignC" -> ConsAssignA (lexeme_ConsAssignC ag) (getE_A $ ag.$2) (getListC_ListA $ ag.$3) (createLink ag)
                      "EmptyListC"  -> EmptyListA (createLink ag)

getE_A :: Zipper a -> A
getE_A ag = case (constructor ag) of
             "Add"   -> Plus (getE_A $ ag.$1) (getT_A $ ag.$2) (createLink ag)
             "Sub"   -> Minus (getE_A $ ag.$1) (getT_A $ ag.$2) (createLink ag)
             "Et"    -> getT_A $ ag.$1

getT_A :: Zipper a -> A
getT_A ag = case (constructor ag) of
             "Mul"   -> Time (getT_A $ ag.$1) (getF_A $ ag.$2) (createLink ag)
             "Div"   -> Divide (getT_A $ ag.$1) (getF_A $ ag.$2) (createLink ag)
             "Tf"    -> getF_A $ ag.$1

getF_A :: Zipper a -> A
getF_A ag = case (constructor ag) of
             "Nest"  -> getF_A $ ag.$1
             "Neg"   -> Minus (Constant 0 Empty) (getF_A $ ag.$1) (createLink ag)
             "Const" -> Constant (lexeme_Const ag) (createLink ag)
             "Var"   -> Variable (lexeme_Var   ag) (createLink ag)

-- Backward Transformation (PUT)
putRootA_RootC :: Zipper a -> RootC
putRootA_RootC ag = case (constructor ag) of
                     "RootA" -> RootC (putLetA_LetC $ ag.$1)

putLetA_LetC :: Zipper a -> LetC
putLetA_LetC ag = case (constructor ag) of
                   "LetA" -> LetC (putListA_ListC $ ag.$1) (putInA_IntC $ ag.$2)

putInA_IntC :: Zipper a -> InC
putInA_IntC ag = case (constructor ag) of
                   "InA" -> InC (putA_E $ ag.$1)

putListA_ListC :: Zipper a -> ListC
putListA_ListC ag = case (constructor ag) of
                      "ConsLetA"    -> ConsLetC (lexeme_ConsLetA_1 ag) (putLetA_LetC $ ag.$2) (putListA_ListC $ ag.$3)
                      "ConsAssignA" -> ConsAssignC (lexeme_ConsAssignA_1 ag) (putA_E $ ag.$2) (putListA_ListC $ ag.$3)
                      "EmptyListA"  -> EmptyListC

putA_E :: Zipper a -> E
putA_E ag = case (getLink ag) of
             IsE e -> e
             IsT t -> Et $ t
             IsF f -> Et $ Tf $ f
             Empty -> case (constructor ag) of
                        "Plus"     -> Add (putA_E $ ag.$1) (putA_T $ ag.$2)
                        "Minus"    -> case (getHole ag :: Maybe A) of
                                        Just (Minus (Constant 0 _) _ _) -> Et $ Tf $ Neg (putA_F $ ag.$2)
                                        otherwise                       -> Sub (putA_E $ ag.$1) (putA_T $ ag.$2)
                        "Times"    -> Et $ Mul (putA_T $ ag.$1) (putA_F $ ag.$2)
                        "Divide"   -> Et $ Div (putA_T $ ag.$1) (putA_F $ ag.$2)
                        "Constant" -> Et $ Tf $ Const (lexeme_Constant ag)
                        "Variable" -> Et $ Tf $ Var (lexeme_Variable ag)

putA_T :: Zipper a -> T
putA_T ag = case (getLink ag) of
             IsE e -> Tf $ Nest $ e
             IsT t -> t
             IsF f -> Tf $ f
             Empty -> case (constructor ag) of
                       "Plus"     -> Tf $ Nest $ Add (putA_E $ ag.$1) (putA_T $ ag.$2)
                       "Minus"    -> case (getHole ag :: Maybe A) of
                                       Just (Minus (Constant 0 _) _ _) -> Tf $ Neg (putA_F $ ag.$2)
                                       otherwise                       -> Tf $ Nest $ Sub (putA_E $ ag.$1) (putA_T $ ag.$2)
                       "Times"    -> Mul (putA_T $ ag.$1) (putA_F $ ag.$2)
                       "Divide"   -> Div (putA_T $ ag.$1) (putA_F $ ag.$2)
                       "Constant" -> Tf $ Const (lexeme_Constant ag)
                       "Variable" -> Tf $ Var (lexeme_Variable ag)

putA_F :: Zipper a -> F
putA_F ag = case (getLink ag) of
             IsE e -> Nest $ e
             IsT t -> Nest $ Et $ t
             IsF f -> f
             Empty -> case (constructor ag) of
                        "Plus"     -> Nest $ Add (putA_E $ ag.$1) (putA_T $ ag.$2)
                        "Minus"    -> case (getHole ag :: Maybe A) of
                                        Just (Minus (Constant 0 _) _ _) -> Neg (putA_F $ ag.$2)
                                        otherwise                       -> Nest $ Sub (putA_E $ ag.$1) (putA_T $ ag.$2)
                        "Times"    -> Nest $ Et $ Mul (putA_T $ ag.$1) (putA_F $ ag.$2)
                        "Divide"   -> Nest $ Et $ Div (putA_T $ ag.$1) (putA_F $ ag.$2)
                        "Constant" -> Const (lexeme_Constant ag)
                        "Variable" -> Var (lexeme_Variable ag)