# This is open-source software licensed under a BSD license.
# Please see the file LICENSE.txt for details.
"""
The ``Cursor`` plugin displays a summary line of text that changes as the
user moves the cursor around an image.  In the standard reference viewer
configuration, it appears as a line containing green text just below the
``Colorbar`` plugin.

**Plugin Type: Global**

``Cursor`` is a global plugin.  Only one instance can be opened.

**Usage**

``Cursor`` simply tracks the cursor as it moves around an image and displays
information about the pixel coordinates, WCS coordinates (if available)
and the value of the pixel under the cursor.

There is no associated configuration GUI.

.. note:: Pixel coordinates are affected by the general setting
          "pixel_coords_offset" which can be set in the "general.cfg"
          configuration file for ginga.  The default is value for this
          setting is 1.0, which means pixel coordinates are reported
          from an origin of 1, as per the FITS standard.

"""
import platform

import numpy

from ginga import GingaPlugin, toolkit
from ginga.gw import Readout
from ginga.ImageView import ImageViewNoDataError
from ginga.fonts import font_asst

__all__ = ['Cursor']


class Cursor(GingaPlugin.GlobalPlugin):

    def __init__(self, fv):
        # superclass defines some variables for us, like logger
        super(Cursor, self).__init__(fv)

        prefs = self.fv.get_preferences()
        self.settings = prefs.create_category('plugin_Cursor')
        self.settings.add_defaults(font_name=None, font_size=None)
        self.settings.load(onError='silent')

        fv.add_callback('field-info', self.field_info_cb)
        fv.set_callback('channel-change', self.focus_cb)

        self.readout = None
        self.gui_up = False

    def build_gui(self, container):
        readout = Readout.Readout(-1, 24)

        # NOTE: Special hack for certain platforms, otherwise the font
        # on the readout is too small
        macos_ver = platform.mac_ver()[0]

        font_size = self.settings.get('font_size', None)
        if font_size is None:
            if len(macos_ver) > 0:
                # Mac OS X
                font_size = 16
            elif toolkit.get_family().startswith('gtk'):
                # Gtk
                font_size = 11
            else:
                font_size = 11

        font_name = self.settings.get('font_name', None)
        if font_name is None:
            font_name = font_asst.resolve_alias('fixed', 'Courier')
            if len(macos_ver) > 0:
                # Mac OS X
                font_name = 'monaco'

        readout.set_font(font_name, font_size)

        self.readout = readout
        rw = self.readout.get_widget()

        container.add_widget(rw, stretch=0)
        self.gui_up = True

    def readout_config(self, fitsimage, image, readout):
        if (readout is None) or (image is None):
            return True
        self.logger.debug("configuring readout (%s)" % (str(readout)))
        # Configure readout for this image.
        # Get and store the sizes of the fields necessary to display
        # all X, Y coords as well as values.

        try:
            width, height = fitsimage.get_data_size()
        except ImageViewNoDataError as exc:  # table
            self.logger.debug(str(exc))
            return

        # Set size of coordinate areas (4 is "." + precision 3)
        readout.maxx = len(str(width)) + 4
        readout.maxy = len(str(height)) + 4
        minval, maxval = image.get_minmax()
        readout.maxv = max(len(str(minval)), len(str(maxval)))
        return True

    def force_update(self, channel):
        viewer = channel.fitsimage
        data_x, data_y = viewer.get_last_data_xy()
        self.fv.showxy(viewer, data_x, data_y)

    def redo(self, channel, image):
        if not self.gui_up or channel is None:
            return
        self.readout_config(channel.fitsimage, image, self.readout)

        # force an update on an image change, because the WCS
        # may be different, even if the data coords are the same
        self.force_update(channel)

    def change_readout(self, channel, fitsimage):
        self.readout.fitsimage = fitsimage

        image = fitsimage.get_image()
        if image is not None:
            self.readout_config(fitsimage, image, self.readout)
            self.logger.debug("configured readout")

    def focus_cb(self, viewer, channel):
        if not self.gui_up or channel is None:
            return

        self.change_readout(channel, channel.fitsimage)

        # force an update on a channel change, because the WCS
        # may be different, even if the data coords are the same
        self.force_update(channel)

    def start(self):
        channel = self.fv.get_channel_info()
        self.focus_cb(self.fv, channel)

    def stop(self):
        self.readout = None
        self.gui_up = False
        return True

    def close(self):
        self.fv.stop_global_plugin(str(self))
        return True

    def field_info_cb(self, viewer, channel, info):
        if not self.gui_up:
            return
        readout = self.readout
        fitsimage = channel.fitsimage

        if readout.fitsimage != fitsimage:
            self.change_readout(channel, fitsimage)

        # If this is a multiband image, then average the values
        # for the readout
        value = info.value
        if isinstance(value, numpy.ndarray):
            avg = numpy.average(value)
            value = avg

        # Update the readout
        px_x = "%.3f" % info.x
        px_y = "%.3f" % info.y
        maxx = max(readout.maxx, len(str(px_x)))
        if maxx > readout.maxx:
            readout.maxx = maxx
        maxy = max(readout.maxy, len(str(px_y)))
        if maxy > readout.maxy:
            readout.maxy = maxy
        maxv = max(readout.maxv, len(str(value)))
        if maxv > readout.maxv:
            readout.maxv = maxv

        if 'ra_txt' in info:
            text = "%1.1s: %-14.14s  %1.1s: %-14.14s  X: %-*.*s  Y: %-*.*s  Value: %-*.*s" % (
                info.ra_lbl, info.ra_txt, info.dec_lbl, info.dec_txt,
                maxx, maxx, px_x, maxy, maxy, px_y, maxv, maxv, value)
        else:
            text = "%1.1s: %-14.14s  %1.1s: %-14.14s  X: %-*.*s  Y: %-*.*s  Value: %-*.*s" % (
                '', '', '', '',
                maxx, maxx, px_x, maxy, maxy, px_y, maxv, maxv, value)
        readout.set_text(text)

    def __str__(self):
        return 'cursor'


# Append module docstring with config doc for auto insert by Sphinx.
from ginga.util.toolbox import generate_cfg_example  # noqa
if __doc__ is not None:
    __doc__ += generate_cfg_example('plugin_Cursor', package='ginga')

# END
