{-# LANGUAGE RecordWildCards #-}
module LiveCoding.Migrate.Cell where

-- base
import Data.Data

-- syb
import Data.Generics.Aliases

-- essence-of-live-coding
import LiveCoding.Cell
import LiveCoding.Exceptions
import LiveCoding.Migrate.Migration

-- * Migrations involving sequential compositions of cells

maybeMigrateToComposition1
  :: (Typeable state1', Typeable state1)
  => Composition state1 state2
  -> state1'
  -> Maybe (Composition state1 state2)
maybeMigrateToComposition1 (Composition (_, state2)) state1' = do
  state1 <- cast state1'
  return $ Composition (state1, state2)

-- | Migrate @cell1@ to @cell1 >>> cell2@.
migrationToComposition1 :: Migration
migrationToComposition1 = migrationTo2 maybeMigrateToComposition1

maybeMigrateFromComposition1
  :: (Typeable state1', Typeable state1)
  => Composition state1 state2
  -> Maybe       state1'
maybeMigrateFromComposition1 (Composition (state1, _)) = cast state1

-- | Migrate to @cell1@ from @cell1 >>> cell2@.
migrationFromComposition1 :: Migration
migrationFromComposition1 = constMigrationFrom2 maybeMigrateFromComposition1

maybeMigrateToComposition2
  :: (Typeable state2', Typeable state2)
  => Composition state1 state2
  -> state2'
  -> Maybe (Composition state1 state2)
maybeMigrateToComposition2 (Composition (state1, _)) state2' = do
  state2 <- cast state2'
  return $ Composition (state1, state2)

-- | Migrate @cell2@ to @cell1 >>> cell2@.
migrationToComposition2 :: Migration
migrationToComposition2 = migrationTo2 maybeMigrateToComposition2

maybeMigrateFromComposition2
  :: (Typeable state2', Typeable state2)
  => Composition state1 state2
  -> Maybe              state2'
maybeMigrateFromComposition2 (Composition (_, state2)) = cast state2

-- | Migrate to @cell2@ from @cell1 >>> cell2@.
migrationFromComposition2 :: Migration
migrationFromComposition2 = constMigrationFrom2 maybeMigrateFromComposition2

-- | Combines all migrations related to composition, favouring the first argument.
migrationComposition :: Migration
migrationComposition
  =  migrationToComposition1
  <> migrationFromComposition1
  <> migrationToComposition2
  <> migrationFromComposition2

-- * Migrations involving parallel compositions of cells

maybeMigrateToParallel1
  :: (Typeable state1', Typeable state1)
  => Parallel state1 state2
  -> state1'
  -> Maybe (Parallel state1 state2)
maybeMigrateToParallel1 (Parallel (_, state2)) state1' = do
  state1 <- cast state1'
  return $ Parallel (state1, state2)

-- | Migrate @cell1@ to @cell1 *** cell2@.
migrationToParallel1 :: Migration
migrationToParallel1 = migrationTo2 maybeMigrateToParallel1

maybeMigrateFromParallel1
  :: (Typeable state1', Typeable state1)
  => Parallel state1 state2
  -> Maybe    state1'
maybeMigrateFromParallel1 (Parallel (state1, _)) = cast state1

-- | Migrate to @cell1@ from @cell1 *** cell2@.
migrationFromParallel1 :: Migration
migrationFromParallel1 = constMigrationFrom2 maybeMigrateFromParallel1

maybeMigrateToParallel2
  :: (Typeable state2', Typeable state2)
  => Parallel state1 state2
  -> state2'
  -> Maybe (Parallel state1 state2)
maybeMigrateToParallel2 (Parallel (state1, _)) state2' = do
  state2 <- cast state2'
  return $ Parallel (state1, state2)

-- | Migrate @cell2@ to @cell1 *** cell2@.
migrationToParallel2 :: Migration
migrationToParallel2 = migrationTo2 maybeMigrateToParallel2

maybeMigrateFromParallel2
  :: (Typeable state2', Typeable state2)
  => Parallel state1 state2
  -> Maybe           state2'
maybeMigrateFromParallel2 (Parallel (_, state2)) = cast state2

-- | Migrate to @cell2@ from @cell1 *** cell2@.
migrationFromParallel2 :: Migration
migrationFromParallel2 = constMigrationFrom2 maybeMigrateFromParallel2

-- | Combines all migrations related to parallel composition, favouring the first argument.
migrationParallel :: Migration
migrationParallel
  =  migrationToParallel1
  <> migrationFromParallel1
  <> migrationToParallel2
  <> migrationFromParallel2

-- * Migration involving 'ArrowChoice'

maybeMigrateToChoice1
  :: (Typeable stateLeft', Typeable stateLeft)
  => Choice stateLeft stateRight
  -> stateLeft'
  -> Maybe (Choice stateLeft stateRight)
maybeMigrateToChoice1 Choice { .. } choiceLeft' = do
  choiceLeft <- cast choiceLeft'
  return Choice { .. }

-- | Migrate @cell1@ to @cell1 ||| cell2@.
migrationToChoice1 :: Migration
migrationToChoice1 = migrationTo2 maybeMigrateToChoice1

maybeMigrateFromChoice1
  :: (Typeable stateLeft', Typeable stateLeft)
  => Choice stateLeft stateRight
  -> Maybe  stateLeft'
maybeMigrateFromChoice1 Choice { .. } = cast choiceLeft

-- | Migrate to @cell1@ from @cell1 ||| cell2@.
migrationFromChoice1 :: Migration
migrationFromChoice1 = constMigrationFrom2 maybeMigrateFromChoice1

maybeMigrateToChoice2
  :: (Typeable stateRight', Typeable stateRight)
  => Choice stateLeft stateRight
  -> stateRight'
  -> Maybe (Choice stateLeft stateRight)
maybeMigrateToChoice2 Choice { .. } choiceRight' = do
  choiceRight <- cast choiceRight'
  return Choice { .. }

-- | Migrate @cell2@ to @cell1 ||| cell2@.
migrationToChoice2 :: Migration
migrationToChoice2 = migrationTo2 maybeMigrateToChoice2

maybeMigrateFromChoice2
  :: (Typeable stateRight', Typeable stateRight)
  => Choice stateLeft stateRight
  -> Maybe            stateRight'
maybeMigrateFromChoice2 Choice { .. } = cast choiceRight

-- | Migrate to @cell2@ from @cell1 ||| cell2@.
migrationFromChoice2 :: Migration
migrationFromChoice2 = constMigrationFrom2 maybeMigrateFromChoice2

-- | Combines all migrations related to choice.
migrationChoice :: Migration
migrationChoice
  =  migrationToChoice1
  <> migrationFromChoice1
  <> migrationToChoice2
  <> migrationFromChoice2

-- * Control flow

maybeMigrateToExceptState
  :: (Typeable state, Typeable state')
  => ExceptState state e
  ->             state'
  -> Maybe (ExceptState state e)
maybeMigrateToExceptState (NotThrown _) state = NotThrown <$> cast state
maybeMigrateToExceptState (Exception e) _ = Just $ Exception e

-- | Migration from @cell2@ to @try cell1 >> safe cell2@
migrationToExceptState :: Migration
migrationToExceptState = migrationTo2 maybeMigrateToExceptState

maybeMigrateFromExceptState
  :: (Typeable state, Typeable state')
  => ExceptState state e
  -> Maybe       state'
maybeMigrateFromExceptState (NotThrown state) = cast state
maybeMigrateFromExceptState (Exception e) = Nothing

-- | Migration from @try cell1 >> safe cell2@ to @cell2@
migrationFromExceptState :: Migration
migrationFromExceptState = constMigrationFrom2 maybeMigrateFromExceptState

-- | Combines all control flow related migrations
migrationExceptState :: Migration
migrationExceptState = migrationToExceptState <> migrationFromExceptState

-- * Overall migration

-- | Combines all 'Cell'-related migrations.
migrationCell :: Migration
migrationCell
  =  migrationComposition
  <> migrationParallel
  <> migrationChoice
  <> migrationExceptState