parseReferences :: PersistSettings -> Text -> Q Exp
parseReferences ps s = lift $
     map (mkEntityDefSqlTypeExp embedEntityMap entMap) noCycleEnts
  where
    entMap = M.fromList $ map (\ent -> (entityHaskell ent, ent)) noCycleEnts
    noCycleEnts = map breakCycleEnt entsWithEmbeds
    -- every EntityDef could reference each-other (as an EmbedRef)
    -- let Haskell tie the knot
    embedEntityMap = M.fromList $ map (\ent -> (entityHaskell ent, toEmbedEntityDef ent)) entsWithEmbeds
    entsWithEmbeds = map setEmbedEntity rawEnts
    setEmbedEntity ent = ent
      { entityFields = map (setEmbedField (entityHaskell ent) embedEntityMap) $ entityFields ent
      }
    rawEnts = parse ps s

    -- self references are already broken
    -- look at every emFieldEmbed to see if it refers to an already seen HaskellName
    -- so start with entityHaskell ent and accumulate embeddedHaskell em
    breakCycleEnt entDef =
      let entName = entityHaskell entDef
      in  entDef { entityFields = map (breakCycleField entName) $ entityFields entDef }

    breakCycleField entName f@(FieldDef { fieldReference = EmbedRef em }) =
      f { fieldReference = EmbedRef $ breakCycleEmbed [entName] em }
    breakCycleField _ f = f

    breakCycleEmbed ancestors em =
        em { embeddedFields = map (breakCycleEmField $ emName : ancestors)
                                  (embeddedFields em)
           }
      where
        emName = embeddedHaskell em

    breakCycleEmField ancestors emf = case embeddedHaskell <$> membed of
        Nothing -> emf
        Just embName -> if embName `elem` ancestors
          then emf { emFieldEmbed = Nothing, emFieldCycle = Just embName }
          else emf { emFieldEmbed = breakCycleEmbed ancestors <$> membed }
      where
        membed = emFieldEmbed emf
parseReferences :: PersistSettings -> Text -> Q Exp
parseReferences ps s = lift $
     map (mkEntityDefSqlTypeExp embedEntityMap entMap) noCycleEnts
  where
    entMap = M.fromList $ map (\ent -> (entityHaskell ent, ent)) noCycleEnts
    noCycleEnts = map breakCycleEnt entsWithEmbeds
    -- every EntityDef could reference each-other (as an EmbedRef)
    -- let Haskell tie the knot
    embedEntityMap = M.fromList $ map (\ent -> (entityHaskell ent, toEmbedEntityDef ent)) entsWithEmbeds
    entsWithEmbeds = map setEmbedEntity rawEnts
    setEmbedEntity ent = ent
      { entityFields = map (setEmbedField (entityHaskell ent) embedEntityMap) $ entityFields ent
      }
    rawEnts = parse ps s

    -- self references are already broken
    -- look at every emFieldEmbed to see if it refers to an already seen HaskellName
    -- so start with entityHaskell ent and accumulate embeddedHaskell em
    breakCycleEnt entDef =
      let entName = entityHaskell entDef
      in  entDef { entityFields = map (breakCycleField entName) $ entityFields entDef }

    breakCycleField entName f@(FieldDef { fieldReference = EmbedRef em }) =
      f { fieldReference = EmbedRef $ breakCycleEmbed [entName] em }
    breakCycleField _ f = f

    breakCycleEmbed ancestors em =
        em { embeddedFields = map (breakCycleEmField $ emName : ancestors)
                                  (embeddedFields em)
           }
      where
        emName = embeddedHaskell em

    breakCycleEmField ancestors emf = case embeddedHaskell <$> membed of
        Nothing -> emf
        Just embName -> if embName `elem` ancestors
          then emf { emFieldEmbed = Nothing, emFieldCycle = Just embName }
          else emf { emFieldEmbed = breakCycleEmbed ancestors <$> membed }
      where
        membed = emFieldEmbed emf
parseReferences ps s = lift $
     map (mkEntityDefSqlTypeExp embedEntityMap entMap) noCycleEnts
  where
    entMap = M.fromList $ map (\ent -> (entityHaskell ent, ent)) noCycleEnts
    noCycleEnts = map breakCycleEnt entsWithEmbeds
    -- every EntityDef could reference each-other (as an EmbedRef)
    -- let Haskell tie the knot
    embedEntityMap = M.fromList $ map (\ent -> (entityHaskell ent, toEmbedEntityDef ent)) entsWithEmbeds
    entsWithEmbeds = map setEmbedEntity rawEnts
    setEmbedEntity ent = ent
      { entityFields = map (setEmbedField (entityHaskell ent) embedEntityMap) $ entityFields ent
      }
    rawEnts = parse ps s

    -- self references are already broken
    -- look at every emFieldEmbed to see if it refers to an already seen HaskellName
    -- so start with entityHaskell ent and accumulate embeddedHaskell em
    breakCycleEnt entDef =
      let entName = entityHaskell entDef
      in  entDef { entityFields = map (breakCycleField entName) $ entityFields entDef }

    breakCycleField entName f@(FieldDef { fieldReference = EmbedRef em }) =
      f { fieldReference = EmbedRef $ breakCycleEmbed [entName] em }
    breakCycleField _ f = f

    breakCycleEmbed ancestors em =
        em { embeddedFields = map (breakCycleEmField $ emName : ancestors)
                                  (embeddedFields em)
           }
      where
        emName = embeddedHaskell em

    breakCycleEmField ancestors emf = case embeddedHaskell <$> membed of
        Nothing -> emf
        Just embName -> if embName `elem` ancestors
          then emf { emFieldEmbed = Nothing, emFieldCycle = Just embName }
          else emf { emFieldEmbed = breakCycleEmbed ancestors <$> membed }
      where
        membed = emFieldEmbed emf