"""Pipeline definition, consist of multiple :class:`ProcessorUnit`.

Each model-wise preprocessor should employ a sequence of :class:`ProcessorUnit`
and :class:`StatefulProcessorUnit` to handle input data.
"""

import abc
import typing

from matchzoo import preprocessor
from matchzoo import datapack


class Pipeline(object):
    """Pipeline to handle data given processor units.

    Example:
        >>> pipe = Pipeline()
        >>> tu = preprocessor.TokenizeUnit()
        >>> lu = preprocessor.LowercaseUnit()
        >>> nu = preprocessor.NgramLetterUnit()
        >>> pipe.add(tu)
        >>> pipe.add(lu)
        >>> pipe.add(nu)
        >>> print(len(pipe))
        3
        >>> pipe.remove(lu)
        >>> print(len(pipe))
        2
        >>> len(pipe.processor_units)
        2

    """

    def __init__(self):
        """Class Initialization."""
        self._processor_units = []
        self.context = {}
        self.rv = None

    def fit(
        self,
        input: typing.Any
    ) -> typing.Callable:
        """Fit."""
        self.context = self._fit(input)
        return self

    @abc.abstractmethod
    def _fit(self, input: typing.Any):
        """Fit."""

    @abc.abstractmethod
    def transform(self) -> datapack.DataPack:
        """Transform."""

    def add(
        self,
        processor_unit: preprocessor.ProcessorUnit
    ):
        """
        Add processor unit to :class:`Pipeline`.

        :param: `processor unit` to be added.
        """
        # Remove duplicate.
        if processor_unit in self._processor_units:
            raise ValueError(
                "Trying to add existed processor unit!"
            )
        self._processor_units.append(processor_unit)

    def remove(
        self,
        processor_unit: preprocessor.ProcessorUnit
    ):
        """
        Remove process unit from :class:`Pipeline`.

        :param processor_unit: `processor unit` to be removed.
        """
        if processor_unit not in self._processor_units:
            raise ValueError(
                "Trying to remove a non-exist processor unit!"
            )
        self._processor_units.remove(processor_unit)

    def __len__(self) -> int:
        """Get number of processor units."""
        return len(self._processor_units)

    @property
    def processor_units(
        self
    ) -> typing.List[preprocessor.ProcessorUnit]:
        """
        Get processor units within :class:`Pipeline`.

        :return: `processor units` within :class:`Pipeline`.
        """
        return self._processor_units

@@ -934,10 +934,17 @@
 elf.
-rv
+dataframe
  = N
@@ -960,38 +960,21 @@
 def fit(
-%0A        self,%0A       
+self,
  input: 
@@ -983,21 +983,16 @@
 ping.Any
-%0A    
 ) -%3E typ
@@ -1085,16 +1085,344 @@
 n self%0A%0A
+    def transform(self, input) -%3E datapack.DataPack:%0A        %22%22%22Transform.%22%22%22%0A        self.dataframe = self._transform(input)%0A        return datapack.DataPack(self.dataframe, self.context)%0A    %0A    def fit_transform(self, input) -%3E datapack.DataPack:%0A        %22%22%22Fit transform%22%22%22%0A        return self.fit(input).transform(input)%0A%0A
     @abc
@@ -1491,24 +1491,28 @@
  %22%22%22Fit.%22%22%22%0A
+    
 %0A    @abc.ab
@@ -1524,32 +1524,33 @@
 tmethod%0A    def 
+_
 transform(self) 
@@ -1547,38 +1547,24 @@
 orm(self
-) -%3E datapack.DataPack
+, input)
 :%0A      

update fit, transform in pipeline