"""

"""
# This file is part of zasim. zasim is licensed under the BSD 3-clause license.
# See LICENSE.txt for details.

from .target import Target
from .computations import ElementaryCellularAutomatonBase, LifeCellularAutomatonBase
from .beta_async import BetaAsynchronousAccessor, BetaAsynchronousNeighbourhood
from .accessors import SimpleStateAccessor
from .nondeterministic import OneDimNondeterministicCellLoop, TwoDimNondeterministicCellLoop, RandomGenerator
from .loops import (OneDimCellLoop, TwoDimCellLoop, OneDimSparseCellLoop, TwoDimSparseCellLoop,
                    OneDimSparseNondetCellLoop, TwoDimSparseNondetCellLoop)
from .border import SimpleBorderCopier, TwoDimSlicingBorderCopier, BorderSizeEnsurer
from .stats import SimpleHistogram, ActivityRecord
from .neighbourhoods import ElementaryFlatNeighbourhood, VonNeumannNeighbourhood, MooreNeighbourhood
from .stepfunc import StepFunc

from ..simulator import ElementaryCagenSimulator, CagenSimulator

import inspect
import numpy as np

def automatic_stepfunc(size=None, config=None, computation=None,
                       nondet=1, beta=1,
                       histogram=False, activity=False,
                       copy_borders=True, neighbourhood=None,
                       base=2, visitors=None,
                       sparse_loop=False,
                       target_class=Target,
                       needs_random_generator=False, random_generator=None, **kwargs):
    """From the given parameters, assemble a StepFunc with the given
    computation and visitors objects. Additionally, a target is created.

    Returns the stepfunc."""
    if size is None:
        # pypy compat: np.array is a type in pypy, whereas it's a function in numpy
        if ("ndarray" in dir(np) and isinstance(config, np.ndarray)) \
                or ("ndarray" not in dir(np) and isinstance(config, np.array)):
            size = config.shape

    target = target_class(size, config, base=base)
    size = target.size

    if neighbourhood is None:
        if len(size) == 1:
            neighbourhood_class = ElementaryFlatNeighbourhood
        elif len(size) == 2:
            neighbourhood_class = VonNeumannNeighbourhood

    if beta != 1 and nondet == 1:
        acc = BetaAsynchronousAccessor(beta)
        needs_random_generator = True
        if neighbourhood is None:
            neighbourhood = neighbourhood_class(Base=BetaAsynchronousNeighbourhood)
        elif inspect.isfunction(neighbourhood):
            neighbourhood = neighbourhood(Base=BetaAsynchronousNeighbourhood)
        elif not isinstance(neighbourhood, BetaAsynchronousNeighbourhood):
            raise ValueError("If you set beta to a value other than 1, you "
                             "must supply a neighbourhood that is based on "
                             "BetaAsynchronousNeighbourhood.")
    elif beta == 1:
        acc = SimpleStateAccessor()
        if neighbourhood is None:
            neighbourhood = neighbourhood_class()
        elif inspect.isfunction(neighbourhood):
            neighbourhood = neighbourhood()

    elif beta != 1 and nondet != 1:
        raise ValueError("Cannot have beta asynchronism and deterministic != 1.")

    if len(size) == 1:
        if nondet == 1.0:
            if sparse_loop:
                loop = OneDimSparseCellLoop()
            else:
                loop = OneDimCellLoop()
        else:
            if sparse_loop: