module DDC.Core.Flow.Process.Operator
        ( Operator (..)
        , bindOfOp)
where
import DDC.Core.Flow.Exp


-- | An abstract series operator.
--
--   Each of the constructors holds all the information we need to produce
--   code for that operator.
data Operator
        -----------------------------------------
        -- | Connect a series from one place to another.
        --   These don't come from the source program, but are useful for 
        --   during code generation.
        = OpId
        { -- Binder for result series.
          opResultSeries        :: BindF

          -- Rate of the input series.
        , opInputRate           :: TypeF

          -- Bound of the input series
        , opInputSeries         :: BoundF

          -- Type of the elements.
        , opElemType            :: TypeF
        }

        -----------------------------------------
        -- | Flat replicate.
        | OpRep
        { -- Binder for result series.
          opResultSeries        :: BindF

          -- Rate of output series.
        , opOutputRate          :: TypeF

          -- Type of the elements.
        , opElemType            :: TypeF

          -- Exp to compute the element to be replicated.
        , opInputExp            :: ExpF }

        -----------------------------------------
        -- | Segmented replicate.
        | OpReps
        { -- Binder for result series.
          opResultSeries        :: BindF

          -- Rate of input series.
        , opInputRate           :: TypeF

          -- Rate of output series.
        , opOutputRate          :: TypeF

          -- Type of the elements.
        , opElemType            :: TypeF

          -- Bound for the segment descriptor.
        , opSegdBound           :: BoundF

          -- Bound for the input series.
        , opInputSeries         :: BoundF }

        -----------------------------------------
        -- | Segmented indices.
        | OpIndices
        { -- Binder for result series.
          opResultSeries        :: BindF

          -- Rate of input series.
        , opInputRate           :: TypeF

          -- Rate of output series.
        , opOutputRate          :: TypeF

          -- Bound for the segment descriptor.
        , opSegdBound           :: BoundF }

        -----------------------------------------
        -- | Fill a vector with elements from a series.
        | OpFill
        { -- Binder for result value (a Unit)
          opResultBind          :: BindF

          -- Bound of target vector.
        , opTargetVector        :: BoundF

          -- Rate of input series.
        , opInputRate           :: TypeF

          -- Bound of input series.
        , opInputSeries         :: BoundF 

          -- Type of the elements.
        , opElemType            :: TypeF }

        -----------------------------------------
        -- | Gather elements from a vector into a series.
        | OpGather
        { -- Binder for result series.
          opResultBind          :: BindF

          -- Bound  of source elem vector.
        , opSourceVector        :: BoundF

          -- Bound  of source index series.
        , opSourceIndices       :: BoundF

          -- Rate of input and output series.
        , opInputRate           :: TypeF

          -- Rate of input vector series.
        , opVectorRate          :: TypeF

          -- Type of gathered elements.
        , opElemType            :: TypeF 
        }

        -----------------------------------------
        -- | Scatter elements from a series into a vector.
        | OpScatter
        { -- Binder for result value (a Unit)
          opResultBind          :: BindF

          -- Bound of target vector.
        , opTargetVector        :: BoundF

          -- Bound of source index series.
        , opSourceIndices       :: BoundF

          -- Bound of source element series.
        , opSourceElems         :: BoundF

          -- Rate of input serieses.
        , opInputRate           :: TypeF

          -- Type of elements.
        , opElemType            :: TypeF
        }

        -----------------------------------------
        -- | Apply a function to corresponding elements in several input series
        --   of the same rate, producing a new series. This subsumes the regular
        --   'map' operator as well as 'zipWith' like operators where the input
        --   lengths are identical.
        | OpMap
        { -- Arity of map, number of input streams.
          opArity               :: Int

          -- Binder for result series.
        , opResultSeries        :: BindF

          -- Rate of all input series.
        , opInputRate           :: TypeF

          -- Names for input series.
        , opInputSeriess        :: [BoundF]

          -- Worker input parameters
        , opWorkerParams        :: [BindF]

          -- Worker body
        , opWorkerBody          :: ExpF
        }

        -----------------------------------------
        -- | Pack a series according to a selector.
        | OpPack
        { -- Binder for result series.
          opResultSeries        :: BindF

          -- Rate of input series.
        , opInputRate           :: TypeF

          -- Bound of input series.
        , opInputSeries         :: BoundF

          -- Rate of output series.
        , opOutputRate          :: TypeF

          -- Type of a series element.
        , opElemType            :: TypeF }

        -----------------------------------------
        -- | Generate a new Series, with elements based on index
        | OpGenerate
        { -- Binder for result series.
          opResultSeries        :: BindF

          -- Rate of output series.
        , opOutputRate          :: TypeF

          -- Worker parameter for function index input.
        , opWorkerParamIndex    :: BindF

          -- Worker body.
        , opWorkerBody          :: ExpF
        }

        -----------------------------------------
        -- | Reduce the elements of a series into a reference.
        | OpReduce
        { -- Binder for result value (a Unit)
          opResultBind          :: BindF

          -- Bound of target Ref.
        , opTargetRef           :: BoundF

          -- Rate of input series.
        , opInputRate           :: TypeF

          -- Bound of input series.
        , opInputSeries         :: BoundF

          -- Neutral element.
        , opZero                :: ExpF

          -- Worker parameter for accumulator input.
        , opWorkerParamAcc      :: BindF

          -- Worker parameter for element input.
        , opWorkerParamElem     :: BindF

          -- Worker body.
        , opWorkerBody          :: ExpF
        }

        -----------------------------------------
        -- | Convert a series from a vector
        | OpSeriesOfRateVec
        { -- Binder for result series.
          opResultSeries        :: BindF

          -- Rate of the input series.
        , opInputRate           :: TypeF

          -- Bound of the input vector
        , opInputRateVec        :: BoundF

          -- Type of the elements.
        , opElemType            :: TypeF
        }

        -----------------------------------------
        -- | Use an existing series passed in as argument
        | OpSeriesOfArgument
        { -- Binder for result series.
          opResultSeries        :: BindF

          -- Rate of the input series.
        , opInputRate           :: TypeF

          -- Type of the elements.
        , opElemType            :: TypeF
        }


        deriving (Show, Eq)

bindOfOp :: Operator -> BindF
bindOfOp o
 = case o of
    OpId{}
     -> opResultSeries o
    OpRep{}
     -> opResultSeries o
    OpReps{}
     -> opResultSeries o
    OpIndices{}
     -> opResultSeries o
    OpMap{}
     -> opResultSeries o
    OpPack{}
     -> opResultSeries o
    OpGenerate{}
     -> opResultSeries o
    OpSeriesOfRateVec{}
     -> opResultSeries o
    OpSeriesOfArgument{}
     -> opResultSeries o

    OpFill{}
     -> opResultBind o
    OpGather{}
     -> opResultBind o
    OpScatter{}
     -> opResultBind o
    OpReduce{}
     -> opResultBind o