module Axel.Denormalize where

import Axel.AST
  ( Expression(ECaseBlock, EEmptySExpression, EFunctionApplication,
           EIdentifier, EIfBlock, ELambda, ELetBlock, ELiteral,
           ERawExpression, ERecordDefinition, ERecordType)
  , Import(ImportItem, ImportType)
  , ImportSpecification(ImportAll, ImportOnly)
  , Literal(LChar, LInt, LString)
  , Statement(SDataDeclaration, SFunctionDefinition, SMacroDefinition,
          SMacroImport, SModuleDeclaration, SNewtypeDeclaration, SPragma,
          SQualifiedImport, SRawStatement, SRestrictedImport, STopLevel,
          STypeSignature, STypeSynonym, STypeclassDefinition,
          STypeclassInstance, SUnrestrictedImport)
  , TopLevel(TopLevel)
  , TypeDefinition(ProperType, TypeConstructor)
  , alias
  , arguments
  , bindings
  , body
  , cond
  , constraints
  , constructor
  , constructors
  , definition
  , definitions
  , expr
  , fields
  , function
  , functionDefinition
  , ifFalse
  , ifTrue
  , imports
  , instanceName
  , matches
  , moduleName
  , name
  , pragmaSpecification
  , signatures
  , typeDefinition
  , whereBindings
  )
import qualified Axel.Parse as Parse
  ( Expression(LiteralChar, LiteralInt, LiteralString, SExpression,
           Symbol)
  )

import Control.Lens.Operators ((^.))

denormalizeExpression :: Expression -> Parse.Expression
denormalizeExpression (ECaseBlock caseBlock) =
  let denormalizedCases =
        map
          (\(pat, res) ->
             Parse.SExpression
               [denormalizeExpression pat, denormalizeExpression res])
          (caseBlock ^. matches)
   in Parse.SExpression $ Parse.Symbol "case" :
      denormalizeExpression (caseBlock ^. expr) :
      denormalizedCases
denormalizeExpression EEmptySExpression = Parse.SExpression []
denormalizeExpression (EFunctionApplication functionApplication) =
  Parse.SExpression $ denormalizeExpression (functionApplication ^. function) :
  map denormalizeExpression (functionApplication ^. arguments)
denormalizeExpression (EIdentifier x) = Parse.Symbol x
denormalizeExpression (EIfBlock ifBlock) =
  Parse.SExpression
    [ Parse.Symbol "if"
    , denormalizeExpression (ifBlock ^. cond)
    , denormalizeExpression (ifBlock ^. ifTrue)
    , denormalizeExpression (ifBlock ^. ifFalse)
    ]
denormalizeExpression (ELambda lambda) =
  let denormalizedArguments =
        Parse.SExpression $ map denormalizeExpression (lambda ^. arguments)
   in Parse.SExpression
        [ Parse.Symbol "\\"
        , denormalizedArguments
        , denormalizeExpression (lambda ^. body)
        ]
denormalizeExpression (ELetBlock letBlock) =
  let denormalizedBindings =
        Parse.SExpression $
        map
          (\(var, val) ->
             Parse.SExpression
               [denormalizeExpression var, denormalizeExpression val])
          (letBlock ^. bindings)
   in Parse.SExpression
        [ Parse.Symbol "let"
        , denormalizedBindings
        , denormalizeExpression (letBlock ^. body)
        ]
denormalizeExpression (ELiteral x) =
  case x of
    LChar char -> Parse.LiteralChar char
    LInt int -> Parse.LiteralInt int
    LString string -> Parse.LiteralString string
denormalizeExpression (ERawExpression rawSource) =
  Parse.SExpression [Parse.Symbol "raw", Parse.LiteralString rawSource]
denormalizeExpression (ERecordDefinition recordDefinition) =
  let denormalizedBindings =
        map
          (\(var, val) ->
             Parse.SExpression [Parse.Symbol var, denormalizeExpression val])
          (recordDefinition ^. bindings)
   in Parse.SExpression (Parse.Symbol "record" : denormalizedBindings)
denormalizeExpression (ERecordType recordType) =
  let denormalizedFields =
        map
          (\(field, ty) ->
             Parse.SExpression [Parse.Symbol field, denormalizeExpression ty])
          (recordType ^. fields)
   in Parse.SExpression (Parse.Symbol "recordType" : denormalizedFields)

denormalizeImportSpecification :: ImportSpecification -> Parse.Expression
denormalizeImportSpecification ImportAll = Parse.Symbol "all"
denormalizeImportSpecification (ImportOnly importList) =
  Parse.SExpression $ map denormalizeImport importList
  where
    denormalizeImport (ImportItem item) = Parse.Symbol item
    denormalizeImport (ImportType type' items) =
      Parse.SExpression (Parse.Symbol type' : map Parse.Symbol items)

denormalizeStatement :: Statement -> Parse.Expression
denormalizeStatement (SDataDeclaration dataDeclaration) =
  let denormalizedTypeDefinition =
        case dataDeclaration ^. typeDefinition of
          TypeConstructor typeConstructor ->
            denormalizeExpression $ EFunctionApplication typeConstructor
          ProperType properType -> Parse.Symbol properType
   in Parse.SExpression
        (Parse.Symbol "data" : denormalizedTypeDefinition :
         map
           (denormalizeExpression . EFunctionApplication)
           (dataDeclaration ^. constructors))
denormalizeStatement (SFunctionDefinition fnDef) =
  Parse.SExpression $ Parse.Symbol "=" : Parse.Symbol (fnDef ^. name) :
  Parse.SExpression (map denormalizeExpression (fnDef ^. arguments)) :
  denormalizeExpression (fnDef ^. body) :
  map (denormalizeStatement . SFunctionDefinition) (fnDef ^. whereBindings)
denormalizeStatement (SMacroDefinition macroDef) =
  Parse.SExpression $ Parse.Symbol "=macro" :
  Parse.Symbol (macroDef ^. functionDefinition . name) :
  Parse.SExpression
    (map denormalizeExpression (macroDef ^. functionDefinition . arguments)) :
  denormalizeExpression (macroDef ^. functionDefinition . body) :
  map
    (denormalizeStatement . SFunctionDefinition)
    (macroDef ^. functionDefinition . whereBindings)
denormalizeStatement (SMacroImport macroImport) =
  Parse.SExpression
    [ Parse.Symbol "importm"
    , Parse.Symbol $ macroImport ^. moduleName
    , Parse.SExpression $ map Parse.Symbol (macroImport ^. imports)
    ]
denormalizeStatement (SModuleDeclaration identifier) =
  Parse.SExpression [Parse.Symbol "module", Parse.Symbol identifier]
denormalizeStatement (SNewtypeDeclaration newtypeDeclaration) =
  let denormalizedTypeDefinition =
        case newtypeDeclaration ^. typeDefinition of
          TypeConstructor typeConstructor ->
            denormalizeExpression $ EFunctionApplication typeConstructor
          ProperType properType -> Parse.Symbol properType
   in Parse.SExpression
        [ Parse.Symbol "newtype"
        , denormalizedTypeDefinition
        , denormalizeExpression $
          EFunctionApplication (newtypeDeclaration ^. constructor)
        ]
denormalizeStatement (SPragma pragma) =
  Parse.SExpression
    [Parse.Symbol "pragma", Parse.LiteralString (pragma ^. pragmaSpecification)]
denormalizeStatement (SQualifiedImport qualifiedImport) =
  Parse.SExpression
    [ Parse.Symbol "importq"
    , Parse.Symbol $ qualifiedImport ^. moduleName
    , Parse.Symbol $ qualifiedImport ^. alias
    , denormalizeImportSpecification (qualifiedImport ^. imports)
    ]
denormalizeStatement (SRawStatement rawSource) =
  Parse.SExpression [Parse.Symbol "raw", Parse.LiteralString rawSource]
denormalizeStatement (SRestrictedImport restrictedImport) =
  Parse.SExpression
    [ Parse.Symbol "import"
    , Parse.Symbol $ restrictedImport ^. moduleName
    , denormalizeImportSpecification (restrictedImport ^. imports)
    ]
denormalizeStatement (STopLevel (TopLevel statements)) =
  Parse.SExpression $ Parse.Symbol "begin" : map denormalizeStatement statements
denormalizeStatement (STypeclassDefinition typeclassDefinition) =
  Parse.SExpression
    (Parse.Symbol "class" :
     Parse.SExpression
       (Parse.Symbol "list" :
        map denormalizeExpression (typeclassDefinition ^. constraints)) :
     denormalizeExpression (typeclassDefinition ^. name) :
     map
       (denormalizeStatement . STypeSignature)
       (typeclassDefinition ^. signatures))
denormalizeStatement (STypeclassInstance typeclassInstance) =
  Parse.SExpression
    (Parse.Symbol "instance" :
     denormalizeExpression (typeclassInstance ^. instanceName) :
     map
       (denormalizeStatement . SFunctionDefinition)
       (typeclassInstance ^. definitions))
denormalizeStatement (STypeSignature typeSig) =
  Parse.SExpression
    [ Parse.Symbol "::"
    , Parse.Symbol (typeSig ^. name)
    , denormalizeExpression (typeSig ^. typeDefinition)
    ]
denormalizeStatement (STypeSynonym typeSynonym) =
  Parse.SExpression
    [ Parse.Symbol "type"
    , denormalizeExpression (typeSynonym ^. alias)
    , denormalizeExpression (typeSynonym ^. definition)
    ]
denormalizeStatement (SUnrestrictedImport identifier) =
  Parse.SExpression [Parse.Symbol "importUnrestricted", Parse.Symbol identifier]