#!/usr/bin/env python

# Copyright (C) 2017  DESY, Notkestr. 85, D-22607 Hamburg
#
# lavue is an image viewing program for photon science imaging detectors.
# Its usual application is as a live viewer using hidra as data source.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation in  version 2
# of the License.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA  02110-1301, USA.
#
# Authors:
#     Christoph Rosemann <christoph.rosemann@desy.de>
#     Jan Kotanski <jan.kotanski@desy.de>
#

import os
import sys
import argparse
import signal
import logging

from argparse import RawTextHelpFormatter

import lavuelib
import lavuelib.liveViewer
from pyqtgraph import QtCore, QtGui

dialog = None


def setupHandler():
    """ sets interrupt handler for SIGINT and SIGTERM
    """
    signal.signal(signal.SIGINT, _intHandler)
    signal.signal(signal.SIGTERM, _intHandler)
    if os.name == 'posix':
        signal.signal(signal.SIGHUP, _intHandler)
    sftimer()


def _intHandler(signum, frame):
    """ interrupt handler
    :param signum:  signal number
    :type signum: :obj:`int`
    :param frame: frame object
    :type frame:  :obj:`frame`
    """
    dialog.close()
    QtGui.QApplication.quit()
    # print("quit")


def sftimer():
    """ creates a garbage collector safe timer
    """
    def timerEvent():
        try:
            pass
        finally:
            QtCore.QTimer.singleShot(500, timerEvent)
    QtCore.QTimer.singleShot(500, timerEvent)


def main():
    global dialog
    """ the main function
    """

    if "GNOME_DESKTOP_SESSION_ID" not in os.environ:
        os.environ["GNOME_DESKTOP_SESSION_ID"] = "qtconfig"
    if os.path.isdir("/usr/lib/kde4/plugins/") and \
       "QT_PLUGIN_PATH" not in os.environ:
        os.environ["QT_PLUGIN_PATH"] = "/usr/lib/kde4/plugins/"

    QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_X11InitThreads)
    QtCore.QResource.registerResource(
        os.path.join(lavuelib.__path__[0], "qrc", "resources.rcc"))

    parser = argparse.ArgumentParser(
        description='2d detector live image viewer',
        formatter_class=RawTextHelpFormatter)
    parser.add_argument(
        "-v", "--version",
        action="store_true",
        default=False,
        dest="version",
        help="program version")
    parser.add_argument(
        "-m", "--mode", dest="mode",
        help="interface mode, i.e. user, expert")
    parser.add_argument(
        "-y", "--style", dest="style",
        help="Qt style")
    parser.add_argument(
        "-e", "--stylesheet", dest="stylesheet",
        help="Qt stylesheet")
    parser.add_argument(
        "-j", "--instance", dest="instance",
        help="LaVue instance with separate configuration")
    parser.add_argument(
        "-ns", "--no-space-instance", action="store_true", default=False,
        dest="nospace",
        help="the configuration file name without a space character\n"
        "  (in the future major release it will become the default one)")
    parser.add_argument(
        "--organization", dest="organization",
        help="Organization name", default="DESY")
    parser.add_argument(
        "--domain", dest="domain",
        help="Organization domain name", default="desy.de")
    parser.add_argument(
        "--configuration-path", dest="configpath",
        help="Configuration path")
    parser.add_argument(
        "-f", "--image-file", dest="imagefile",
        help="image file name to show, e.g."
        " /tmp/myfile2.nxs://entry/data/pilatus,,-1")
    parser.add_argument(
        "-s", "--source", dest="source",
        help="image source(s), i.e. hidra, http, tangoattr,\n"
        "    tangoevents, tangofile, doocsprop, tineprop,\n"
        "    epicspv, zmq, asapo, nxsfile, test\n"
        "multiple-source names is separated by semicolon ';'")
    parser.add_argument(
        "-c", "--configuration", dest="configuration",
        help="configuration strings for the image source separated by comma"
        ", e.g.\n"
        "  hidra -> '-c haspilatus300k.desy.de'\n"
        "  http -> '-c haso228eiger/1.5.0'\n"
        "  tangoattr -> '-c sys/tg_test/1/double_image_ro'\n"
        "  tangoevents -> '-c sys/lamccds/1/video_last_image'\n"
        "  tangofile -> '-c p00/plt/1/LastImageTaken,p00/plt/1/"
        "LastImagePath'\n"
        "  zmq -> '-c haso228:5535,topic'\n"
        "  doocsprop -> '-c TTF2.FEL/BLFW2.CAM/BL0M1.CAM/IMAGE_EXT'\n"
        "  nxsfile -> '-c /tmp/myfile.nxs://entry/data/pilatus'  \n"
        "        or   '-c /tmp/myfile2.nxs://entry/data/pilatus,0,34'  \n"
        "  tineprop -> '-c /HASYLAB/P00_LM00/Output/Frame'\n"
        "  asapo -> '-c pilatus,substream2'\n"
        "  epicspv -> '-c '00SIM0:cam1:,[640,480]'\n"
        "configuration for multiple-sources is separated"
        " by semicolon ';'"
    )
    parser.add_argument(
        "--offset", dest="offset",
        help="relative offset x,y[,TRANSFORMATION]\n"
        "  where x,y are position of the first pixel "
        "for a particular image source\n"
        "  while optional TRANSFORMATION can be:\n"
        "    flip-up-down, flipud, fud, flip-left-right, "
        "fliplr, flr, transpose, t,\n"
        "    rot90, r90, rot180, r180, r270, rot270, rot180+transpose,"
        " rot180t or r180t\n"
        "offset for multiple-sources is separated by semicolon ';'\n"
        "   e.g.\n"
        "    ;200,300;,54;121,3\n"
        "    200,300;100,\n"
        "    200,300;100,200,t\n"
        "    ;200,300,r45;,52;11,3,r180t\n"
    )
    parser.add_argument(
        "-w", "--range-window", dest="rangewindow",
        help="range window slices, i.e. x1:x2,y1:y2 , "
        "e.g. -w 10:500,20:200\n"
        "  where 'm' is '-'"
    )
    parser.add_argument(
        "--ds-factor", dest="dsfactor",
        help="integer down-sampling factor"
    )
    parser.add_argument(
        "--ds-reduction", dest="dsreduction",
        help="down-sampling reduction function, "
        "i.e. 'max', 'min', 'mean' or 'sum'"
    )
    parser.add_argument(
        "-z", "--filters", action="store_true", default=False,
        dest="filters",
        help="apply image filters")
    parser.add_argument(
        "-o", "--memory-buffer", dest="mbuffer", type=int,
        help="size of memory buffer in frames"
    )
    parser.add_argument(
        "--channel", dest="channel",
        help="default channel number or 'sum', 'mean', 'rgb' or "
        "RGB channels separated by comma e.g.'0,1,3'"
    )
    parser.add_argument(
        "-b", "--bkg-file", dest="bkgfile",
        help="background file-name to load"
    )
    parser.add_argument(
        "--bkg-scale", dest="bkgscale",
        help="background scaling factor"
    )
    parser.add_argument(
        "--bright-field-file", dest="brightfieldfile",
        help="bright field file-name to load"
    )
    parser.add_argument(
        "--bright-field-scale", dest="brightfieldscale",
        help="bright field scaling factor"
    )
    parser.add_argument(
        "-k", "--mask-file", dest="maskfile",
        help="mask file-name to load"
    )
    parser.add_argument(
        "-p", "--mask-high-value", dest="maskhighvalue",
        help="highest pixel value to show"
    )
    parser.add_argument(
        "-t", "--transformation", dest="transformation",
        help="image transformation, i.e.\n"
        "  flip-up-down, flip-left-right, transpose,\n"
        "  rot90, rot180, rot270, rot180+transpose"
    )
    parser.add_argument(
        "-i", "--scaling", dest="scaling",
        help="intensity scaling, i.e. sqrt, linear, log"
    )
    parser.add_argument(
        "-l", "--levels", dest="levels",
        help="min,max intensity display levels e.g. -l m20,20\n"
        "  where 'm' is '-'\n"
        " the RGB channel levels can be added separated by ';' e.g.\n"
        " -l '0,40;1,35;0,30;2,45;green' \n"
        "    where 0,40 are the main intensity dispay levels\n"
        "          1,35 are the red channel intensity dispay levels\n"
        "          0,30 are the green channel intensity dispay levels\n"
        "          2,45 are the blue channel intensity dispay levels\n"
        "             and the green channel level widgets are selected"
    )
    parser.add_argument(
        "-q", "--factor", dest="autofactor",
        help="factor of the highest pick for automatic levels in %%,"
        " e.g. -q 0.5"
    )
    parser.add_argument(
        "-g", "--gradient", dest="gradient",
        help="color gradient, i.e. grey, highcontrast, thermal, flame,\n"
        "  bipolar, spectrum, spectrumclip, greyclip, reversegrey, cyclic,\n"
        "  yellowy, inverted"
    )
    parser.add_argument(
        "-r", "--range", dest="viewrange",
        help="viewbox range, i.e. xmin,ymin,xsize,ysize , "
        "e.g. -r 5.6,m60.7,543.2,444.11\n"
        "  where 'm' is '-'"
    )
    parser.add_argument(
        "-x", "--start", action="store_true", default=False,
        dest="start",
        help="connect the image source")
    parser.add_argument(
        "-u", "--tool", dest="tool",
        help="utility tool, i.e. intensity, roi, "
        "movemotors, meshscan, parameters,\n"
        "  linecut, projections, 1d-plot, angle/q, "
        "q+roi+proj, maxima, diffractogram"
    )
    parser.add_argument(
        "--tool-configuration", dest="toolconfig",
        help="JSON dictionary with tool configuration, e.g. "
        "{\"rows_to_plot\":\"0,1\",\"buffer_size\":512}"
    )
    parser.add_argument(
        "-a", "--tango-device", dest="tangodevice",
        help="tango device name of LavueController to communicate"
        " with controller clients during the run")
    parser.add_argument(
        "-d", "--door", dest="doordevice",
        help="door device to communicate with sardana during the run")
    parser.add_argument(
        "-n", "--analysis-device", dest="analysisdevice",
        help="tango analysis device of LambdaOnlineAnalysis"
        " to communicate with analysis clients during the run")
    parser.add_argument(
        "--log", dest="log",
        help="logging level, i.e. debug, info, warning, error, critical")

    options = parser.parse_args()

    if options.version:
        print(lavuelib.__version__)
        sys.exit(0)

    logging.basicConfig(
        format="%(levelname)s: %(message)s")
    logger = logging.getLogger("lavue")
    lavuelib.liveViewer.setLoggerLevel(logger, options.log)

    app = QtGui.QApplication(['LaVue'])
    setupHandler()
    if options.style:
        app.setStyle(options.style)
    if options.stylesheet:
        app.setStyle(options.stylesheet)
    app.setWindowIcon(QtGui.QIcon(":/lavue.png"))
    app.setOrganizationName(options.organization)
    app.setOrganizationDomain(options.domain)
    if options.instance:
        if options.nospace:
            app.setApplicationName("LaVue:%s" % options.instance)
        else:
            app.setApplicationName("LaVue: %s" % options.instance)
    else:
        app.setApplicationName("LaVue")
    app.setApplicationVersion(lavuelib.__version__)
    dialog = lavuelib.liveViewer.MainWindow(options=options)

    dialog.show()

    status = app.exec_()
    dialog = None
    import gc
    gc.collect()
    sys.exit(status)


if __name__ == "__main__":
    main()
