# Source code for taurus.cli.alt

    # ###########################################################################
    #
    # This file is part of Taurus
    #
    # http://taurus-scada.org
    #
    # Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
    #
    # Taurus is free software: you can redistribute it and/or modify
    # it under the terms of the GNU Lesser General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.
    #
    # Taurus is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU Lesser General Public License for more details.
    #
    # You should have received a copy of the GNU Lesser General Public License
    # along with Taurus.  If not, see <http://www.gnu.org/licenses/>.
    #
    # ###########################################################################

    """
    This module provides several taurus CLI subcommands (plot, trend, image,...)
    that show a widget which may have more than one alternative implementation.
    The available registered implementations for each command can be listed with
    the `--ls-alt` option. Specific implementations can be selected with the
    `--use-alt` option.

    These commands also honour the default implementation selection configured in
    :mod:`taurus.tauruscustomsettings` via the `*_ALT` variables

    The alternative implementations can be registered using the following
    entry-point group names:

    - `taurus.plot.alt`: the loaded object is expected to be a `TaurusPlot`
      implementation

    - `taurus.trend.alt`: the loaded object is expected to be a `TaurusTrend`
      implementation

    - `taurus.trend2d.alt`: the loaded object is expected to be a
      `TaurusTrend2dDialog` implementation

    - `taurus.image.alt`: the loaded object is expected to be a `TaurusImageDialog`
      implementation

    Expected API for alternative implementations
    --------------------------------------------

    When registering a widget class to be used for these commands, the classes need
    to provide the following minimum API:

    - For **all** the classes:
      - must be a Qt.QWidget
      - must implement `setModel()` and `getModel()` equivalent to those in
      :class:`taurus.qt.qtgui.base.TaurusBaseComponent`

    - For TaurusPlot:
      - should implement `setXAxisMode(mode)` where mode is one of `"n"` or `"t"`
      - should implement `loadConfigFile(name)` equivalent to
      :meth:`taurus.qt.qtcore.configuration.BaseConfigurableClass.loadConfigFile`

    - For TaurusTrend:
      - should implement `setXAxisMode(mode)` where mode is one of `"n"` or `"t"`
      - should implement  `loadConfigFile(name)` equivalent to
      :meth:`taurus.qt.qtcore.configuration.BaseConfigurableClass.loadConfigFile`
      - should implement `setMaxDataBufferSize(n)` where `n` is an integer
      - should implement `setForcedReadingPeriod(period)` where `period` is an
      integer

    - For TaurusTrend2D:
      - should accept the following keyword arguments in its constructor:

        - stackMode=x_axis_mode where mode is one of `"d"` or `"n"` or `"t"`
        - wintitle=window_name,
        - buffersize=max_buffer_size

    - For TaurusImage:
      - should accept the following keyword arguments in its constructor:

        - wintitle=window_name,

      - should implement `setRGBmode(mode)` where mode is one of `"gray"`, `"rgb"`
    """

    import sys
    import click
    import taurus.cli.common
    from taurus.core.util.plugin import selectEntryPoints
    from taurus import tauruscustomsettings as _ts
    from taurus import warning, info
    from taurus.qt.qtgui.application import TaurusApplication

    __all__ = [
        "x_axis_mode_option",
        "max_buffer_option",
        "forced_read_option",
        "plot_cmd",
        "trend_cmd",
        "trend2d_cmd",
        "image_cmd",
    ]

    EP_GROUP_PLOT = "taurus.plot.alts"
    EP_GROUP_TREND = "taurus.trend.alts"
    EP_GROUP_TREND2D = "taurus.trend2d.alts"
    EP_GROUP_IMAGE = "taurus.image.alts"

    def _print_alts(group):
        alts = [ep.name for ep in selectEntryPoints(group)]
        print("Registered alternatives :\n {}\n".format("\n ".join(alts)))

    def _load_class_from_group(group, include=(".*",), exclude=()):
        """
        Factory that returns the first available class from the group entry point.
        The selection is done among the classes registered in
        the `group` entry-point, prioritized according to the given
        `include` and `exclude` patterns
        (see :func:`taurus.core.util.plugin.selectEntryPoints`)
        """
        eps = selectEntryPoints(group, include=include, exclude=exclude)
        for ep in eps:
            try:
                return ep.load(), ep.name
            except Exception:
                info("Cannot load %s", ep.name)
        raise ImportError("Could not load any class from {}".format(eps))

    def x_axis_mode_option(choices=("t", "n")):
        hlp = {
            "n": "regular axis",
            "e": "event number",
            "t": "absolute time/date axis",
            "d": "delta time axis",
        }
        o = click.option(
            "-x",
            "--x-axis-mode",
            "x_axis_mode",
            type=click.Choice(choices),
            default=choices[0],
            show_default=True,
            help=(
                "X axis mode: "
                + ", ".join([k + " for " + hlp[k] for k in choices])
            ),
        )
        return o

    def max_buffer_option(default):
        o = click.option(
            "-b",
            "--buffer",
            "max_buffer_size",
            type=int,
            default=default,
            show_default=True,
            help=(
                "Maximum number of values to be stacked "
                + "(when reached, the oldest values will be "
                + "discarded)"
            ),
        )
        return o

    forced_read_option = click.option(
        "-r",
        "--forced-read",
        "forced_read_period",
        type=int,
        default=-1,
        metavar="MILLISECONDS",
        help="force re-reading of the attributes every MILLISECONDS ms",
    )

    @click.command("plot")
    @taurus.cli.common.models
    @taurus.cli.common.config_file
    @x_axis_mode_option(["n", "t"])
    @taurus.cli.common.demo
    @taurus.cli.common.window_name("TaurusPlot")
    @taurus.cli.common.use_alternative
    @taurus.cli.common.list_alternatives
    def plot_cmd(
        models, config_file, x_axis_mode, demo, window_name, use_alt, ls_alt
    ):
        """Shows a plot for the given models"""

        if ls_alt:
            _print_alts(EP_GROUP_PLOT)
            sys.exit(0)

        if use_alt is None:
            use_alt = getattr(_ts, "PLOT_ALT", ".*")

        try:
            TPlot, epname = _load_class_from_group(
                EP_GROUP_PLOT, include=[use_alt]
            )
        except Exception:
            _print_alts(EP_GROUP_PLOT)
            sys.exit(1)

        app = TaurusApplication(app_name="taurusplot({})".format(epname))

        w = TPlot()
        w.setWindowTitle(window_name)

        if demo:
            models = list(models)
            models.extend(["eval:rand(100)", "eval:0.5*sqrt(arange(100))"])

        try:
            w.setXAxisMode(x_axis_mode)
        except Exception as e:
            warning(
                'Could not set X axis mode to "%s" on %s plot. Reason: "%s"',
                x_axis_mode,
                epname,
                e,
            )
            sys.exit(1)

        if config_file is not None:
            try:
                w.loadConfigFile(config_file)
            except Exception as e:
                warning(
                    'Could not load config file "%s" on %s plot. Reason: "%s"',
                    config_file,
                    epname,
                    e,
                )
                sys.exit(1)

        if models:
            w.setModel(models)

        w.show()
        sys.exit(app.exec_())

    @click.command("trend")
    @taurus.cli.common.models
    @taurus.cli.common.config_file
    @x_axis_mode_option(["t", "n"])
    @taurus.cli.common.demo
    @taurus.cli.common.window_name("TaurusTrend")
    @taurus.cli.common.use_alternative
    @taurus.cli.common.list_alternatives
    @max_buffer_option(None)
    @forced_read_option
    def trend_cmd(
        models,
        config_file,
        x_axis_mode,
        demo,
        window_name,
        use_alt,
        ls_alt,
        max_buffer_size,
        forced_read_period,
    ):
        """Shows a trend for the given models"""

        # list alternatives option
        if ls_alt:
            _print_alts(EP_GROUP_TREND)
            sys.exit(0)

        # use alternative
        if use_alt is None:
            use_alt = getattr(_ts, "TREND_ALT", ".*")

        # get the selected alternative
        try:
            TTrend, epname = _load_class_from_group(
                EP_GROUP_TREND, include=[use_alt]
            )
        except Exception:
            _print_alts(EP_GROUP_TREND)
            sys.exit(1)

        app = TaurusApplication(app_name="taurustrend({})".format(epname))
        w = TTrend()

        # window title option
        w.setWindowTitle(window_name)

        # demo option
        if demo:
            models = list(models)
            models.extend(["eval:rand()", "eval:1+rand(2)"])

        # x axis mode option
        try:
            w.setXAxisMode(x_axis_mode)
        except Exception as e:
            warning(
                'Could not set X axis mode to "%s" on %s plot. Reason: "%s"',
                x_axis_mode,
                epname,
                e,
            )
            sys.exit(1)

        # configuration file option
        if config_file is not None:
            try:
                w.loadConfigFile(config_file)
            except Exception as e:
                warning(
                    'Could not load config file "%s" on %s plot. Reason: "%s"',
                    config_file,
                    epname,
                    e,
                )
                sys.exit(1)

        # set models
        if models:
            w.setModel(list(models))

        # period option
        if forced_read_period > 0:
            w.setForcedReadingPeriod(forced_read_period)

        # max buffer size option
        if max_buffer_size is not None:
            try:
                w.setMaxDataBufferSize(max_buffer_size)
            except Exception as e:
                warning(
                    'Could not set max buffer size on %s trend. Reason: "%s"',
                    epname,
                    e,
                )
                sys.exit(1)

        w.show()
        sys.exit(app.exec_())

    @click.command("trend2d")
    @taurus.cli.common.model
    @x_axis_mode_option(["d", "t", "n", "e"])
    @taurus.cli.common.demo
    @taurus.cli.common.window_name("TaurusTrend2D")
    @taurus.cli.common.use_alternative
    @taurus.cli.common.list_alternatives
    @max_buffer_option(512)
    def trend2d_cmd(
        model, x_axis_mode, demo, window_name, use_alt, ls_alt, max_buffer_size
    ):

        # list alternatives option
        if ls_alt:
            _print_alts(EP_GROUP_TREND2D)
            sys.exit(0)

        # use alternative
        if use_alt is None:
            use_alt = getattr(_ts, "TREND2D_ALT", ".*")

        # get the selected alternative
        try:
            TTrend2D, epname = _load_class_from_group(
                EP_GROUP_TREND2D, include=[use_alt]
            )
        except Exception:
            _print_alts(EP_GROUP_TREND2D)
            sys.exit(1)

        app = TaurusApplication(app_name="Taurus Trend 2D ({})".format(epname))
        w = TTrend2D(
            stackMode=x_axis_mode, wintitle=window_name, buffersize=max_buffer_size
        )

        if demo:
            model = "eval:x=linspace(0,3,40);t=rand();sin(x+t)"

        if model:
            w.setModel(model)

        w.show()
        sys.exit(app.exec_())

    @click.command("image")
    @taurus.cli.common.model
    @taurus.cli.common.demo
    @taurus.cli.common.window_name("TaurusImage")
    @taurus.cli.common.use_alternative
    @taurus.cli.common.list_alternatives
    @click.option(
        "-c",
        "--color-mode",
        "color_mode",
        type=click.Choice(["gray", "rgb"]),
        default="gray",
        show_default=True,
        help=("Color mode expected from the attribute"),
    )
    def image_cmd(model, demo, window_name, color_mode, use_alt, ls_alt):
        # list alternatives option
        if ls_alt:
            _print_alts(EP_GROUP_IMAGE)
            sys.exit(0)

        # use alternative
        if use_alt is None:
            use_alt = getattr(_ts, "IMAGE_ALT", ".*")

        # get the selected alternative
        try:
            TImage, epname = _load_class_from_group(
                EP_GROUP_IMAGE, include=[use_alt]
            )
        except Exception:
            _print_alts(EP_GROUP_IMAGE)
            sys.exit(1)

        app = TaurusApplication(app_name="Taurus Image ({})".format(epname))

        rgb_mode = color_mode == "rgb"

        # TODO:  is "-c rgb --demo" doing the right thing?? Check it.
        if demo:
            if color_mode == "rgb":
                model = "eval:randint(0,256,(10,20,3))"
            else:
                model = "eval:rand(256,128)"

        w = TImage(wintitle=window_name)

        w.setRGBmode(rgb_mode)

        # set model
        if model:
            w.setModel(model)

        w.show()
        sys.exit(app.exec_())