"""
The IRAF plugin implements a remote control interface for the Ginga FITS
viewer from an IRAF session.  In particular it supports the use of the
IRAF 'display' and 'imexamine' commands.

Instructions for use:

Set the environment variable IMTDEV appropriately, e.g.

    $ export IMTDEV=inet:45005         (or)
    $ export IMTDEV=unix:/tmp/.imtg45

Ginga will try to use the default value if none is assigned.

Start IRAF plugin (Plugins->Start IRAF).

From Ginga you can load images and then use 'imexamine' from IRAF to load
them, do photometry, etc.  You can also use the 'display' command from IRAF
to show images in Ginga.  The 'IRAF' tab will show the mapping from Ginga
channels to IRAF numerical 'frames'.

When using imexamine, the plugin disables normal UI processing on the
channel image so that keystrokes, etc. are passed through to IRAF.  You can
toggle back and forth between local Ginga control and IRAF control using
the radio buttons at the top of the tab or using the space bar.

IRAF commands that have been tested: display, imexam, rimcur and tvmark.
"""
import sys, os
import logging
import threading
import socket
import ginga.util.six as six
if six.PY2:
    import Queue
else:
    import queue as Queue
import array
import numpy
import time

from ginga import GingaPlugin, AstroImage
from ginga import cmap, imap
from ginga.gw import Widgets, Viewers
from ginga.misc import Bunch

# XImage protocol support
import IIS_DataListener as iis


class IRAF(GingaPlugin.GlobalPlugin):

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

        self.keyqueue = Queue.Queue()
        self.keyevent = threading.Event()
        self.keymap = {
            'comma': ',',
            }
        self.ctrldown = False

        self.layertag = 'iraf-canvas'
        # this will be set in initialize()
        self.canvas = None
        self.dc = fv.get_draw_classes()

        self.addr = iis.get_interface()

        self.ev_quit = self.fv.ev_quit
        self.dataTask = None

        # Holds frame buffers
        self.fb = {}
        self.current_frame = 0

        # cursor position
        self.cursor_x = 1.0
        self.cursor_y = 1.0

        self.mode = 'ginga'
        self.imexam_active = False
        self.imexam_chname = None

        # init the first frame(frame 0)
        self.init_frame(0)

        # colormap for use with IRAF displays
        self.cm_iis = cmap.ColorMap('iis_iraf', cma
p_iis_iraf)
        self.im_iis = imap.get_imap('ultrasmooth')

        fv.add_callback('add-channel', self.add_channel)
        fv.add_callback('delete-channel', self.delete_channel)
        #fv.set_callback('channel-change', self.focus_cb)

        self.gui_up = False

    def build_gui(self, container):

        canvas = self.dc.DrawingCanvas()
        canvas.enable_draw(False)
      
  ## canvas.set_callback('none-move', self.cursormotion)
        canvas.add_callback('key-press', self.window_key_press)
        canvas.add_callback('key-release', self.window_key_release)
        self.canvas = canvas

        vbox = Widgets.VBox()

        fr = Widgets.Frame("IRAF")

        captions = [
            ("Addr:", 'label', "Addr", 'llabel', 'Restart', 'button'),
            ("Set Addr:", 'label', "Set Addr", 'entry'),
            ("Control", 'hbox'),
            ("Channel:", 'label', 'Channel', 'llabel'),
            ]
        w, b = Widgets.build_info(captions)
        self.w.update(b)

        addr = str(self.addr.name)
        b.addr.set_text(addr)
        b.restart.set_tooltip("Restart the server")
        b.restart.add_callback('activated', self.restart_cb)

        b.set_addr.set_length(100)
        b.addr.set_text(addr)
        b.set_addr.set_tooltip("Set address to run remote control server")
        b.set_addr.add_callback('activated', self.set_addr_cb)

        self.w.mode_d = {}
        btn1 = Widgets.RadioButton("Ginga")
        btn1.set_state(True)
        btn1.add_callback('activated', lambda w, val: self.switchMode('ginga'))
        self.w.mode_d['ginga'] = btn1
        self.w.control.add_widget(btn1)
        btn2 = Widgets.RadioButton("IRAF", group=btn1)
        btn2.add_callback('activated', lambda w, val: self.switchMode('iraf'))
        self.w.mode_d['iraf'] = btn2
        self.w.control.add_widget(btn2)

        fr.set_widget(w)
        vbox.add_widget(fr, stretch=0)

        fr = Widgets.Frame("Frame/Channel")

        lbl = Widgets.Label("")
        self.w.frch = lbl

        fr.set_widget(lbl)
        vbox.add_widget(fr, stretch=0)

        # stretch
        vbox.add_widget(Widgets.Label(''), stretch=1)

        btns = Widgets.HBox()
        btns.set_spacing(4)
        btns.set_border_width(4)

        btn = Widgets.Button("Close")
        btn.add_callback('activated', lambda w: self.close())
        btns.add_widget(btn)
        btns.add_widget(Widgets.Label(''), stretch=1)
        vbox.add_widget(btns)

        container.add_widget(vbox, stretch=1)
        self.gui_up = True

        fmap = self.get_channel_frame_mapping()
        self.update_chinfo(fmap)


    def update_chinfo(self, fmap):
        if not self.gui_up:
            return
        # Update the GUI with the new frame/channel mapping
        fmap.sort(lambda x, y: x[1] - y[1])

        s = ["%2d: %s" % (num, name) for (name, num) in fmap]
        self.w.frch.set_text("\n".join(s))

    def _setMode(self, modeStr, chname):
        modeStr = modeStr.lower()
        self.w.mode_d[modeStr].set_state(True)
        self.w.channel.set_text(chname)

        self.switchMode(modeStr)

    def setMode(self, modeStr, chname):
        self.imexam_chname = chname
        self.fv.gui_do(self._setMode, modeStr, chname)

    def toggleMode(self):
        isIRAF = self.w.mode_d['iraf'].get_state()
        chname = self.imexam_chname
        if isIRAF:
            self.logger.info("setting mode to Ginga")
            self.setMode('Ginga', chname)
        else:
            self.logger.info("setting mode to IRAF")
            self.setMode('IRAF', chname)


    def add_channel(self, viewer, chinfo):
        self.logger.debug("channel %s added." % (chinfo.name))

        n = self.channel_to_frame(chinfo.name)
        if n is None:
            found = len(self.fb)
            for n, fb in self.fb.items():
                if fb.chname is None:
                    found = n
            fb = self.init_frame(found)
            fb.chname = chinfo.name

        fmap = self.get_channel_frame_mapping()
        self.fv.gui_do(self.update_chinfo, fmap)


    def delete_channel(self, viewer, chinfo):
        self.logger.debug("delete channel %s" % (chinfo.name))

        n = self.channel_to_frame(chinfo.name)
        if n is not None:
            self.fb[n].chname = None

        fmap = self.get_channel_frame_mapping()
        self.fv.gui_do(self.update_chinfo, fmap)

    def switchMode(self, modeStr):
        modeStr = modeStr.lower()
        chname = self.imexam_chname
        chinfo = self.fv.get_channel(chname)

        if modeStr == 'iraf':
            self.ui_disable(chinfo.fitsimage)
        else:
            self.ui_enable(chinfo.fitsimage)

    def start(self):
        try:
            if self.addr.prot == 'unix':
                os.remove(self.addr.path)
        except:
            pass

        # start the data listener task, if appropriate
        ev_quit = threading.Event()
        self.dataTask = iis.IIS_DataListener(
            self.addr, controller=self,
            ev_quit=ev_quit, logger=self.logger)
        self.fv.nongui_do(self.dataTask.mainloop)

    def stop(self):
        if self.dataTask:
            self.dataTask.stop()
        self.gui_up = False

    def restart_cb(self, w):
        # restart server
        if self.dataTask:
            self.dataTask.stop()
        self.start()

    def set_addr_cb(self, w):
        # get and parse address
        addr = w.get_text()
        self.addr = iis.get_interface(addr=addr)
        addr = str(self.addr.name)
        self.w.addr.set_text(addr)

    def channel_to_frame(self, chname):
        for n, fb in self.fb.items():
            if fb.chname == chname: