#!/usr/bin/python

# tz_wx_splash.py
#       --copyright--                   Copyright 2016 (C) Tranzoa, Co. All rights reserved.    Warranty: You're free and on your own here. This code is not necessarily up-to-date or of public quality.
#       --url--                         http://www.tranzoa.net/tzpython/
#       --email--                       pycode is the name to send to. tranzoa.com is the place to send to.
#       --bodstamps--
#       January 23, 2016        bar
#       --eodstamps--
##      \file
#       \namespace              tzpython.tz_wx_splash
#
"""
    Display a splash window while checking for a temporary file whose existence tells us to go away.

"""


import  cStringIO
import  multiprocessing
import  os
import  re
import  sys
import  tempfile
import  threading
import  time

import  wx

import  tzlib


KEEP_FROM_CRASHING_DURATION     = 50            #: milliseconds the timeout must be to keep from core dumping on spring

FONT_SIZE                       = 10            #: default label font size


def pil_to_image(pim, alpha = True):
    """ Convert PIL Image to wx.Image. """
    if  alpha       :
        image       = apply(wx.EmptyImage, pim.size)
        image.SetData(     tzlib.pil_tostring(pim.convert( "RGB")))
        image.SetAlphaData(tzlib.pil_tostring(pim.convert("RGBA"))[3::4])
    else:
        image       = wx.EmptyImage(pim.size[0], pim.size[1])
        new_image   = pim.convert('RGB')
        data        = tzlib.pil_tostring(new_image)
        image.SetData(data)

    return(image)


def stopit(me) :
    """ Look for the quit-file. If it exists, get us off-screen. """
    st  = None
    qfn = me.qfn
    while True :
        time.sleep(0.097)
        if  (st is None) and me.win.GetSplashWindow().IsShown() :
            st  = tzlib.elapsed_time()
        if  not me.win :
            break
        if  os.path.exists(qfn) :
            fd  = tzlib.safe_read_whole_binary_file(qfn)
            g   = re.search("^\s*q\S*\s+(" + tzlib.float_regx_str + r")", fd)
            if  g   :
                d   = float(g.group(1))
                if  (st is None) or (tzlib.elapsed_time() - st < d) :
                    continue
                pass
            win = me.win
            if  win :
                wx.CallAfter(win.Destroy)
            break
        pass
    tzlib.whack_file(qfn)       # clean up


def _on_ignore(evt) :
    """ Suppress the click/key-to-remove. """
    pass


def paint_label(bmp, label = "", label_x = 0, label_y = 0, padding = 0, color = None, font_size = None) :
    if  label           :
        label_x         = label_x   or 0
        label_y         = label_y   or 0
        padding         = padding   or 0
        dc              = wx.MemoryDC(bmp)

        fnt             = wx.Font(font_size or FONT_SIZE, wx.MODERN, wx.NORMAL, wx.NORMAL, False)       # dc.GetFont()
        if  color       :
            dc.SetTextForeground(color)
        dc.SetFont(fnt)                                                             # note: needs to be set for GetFullTextExtent to work

        w, h, d, ld     = dc.GetFullTextExtent(label, fnt)
        if  -1.0 < label_x < 0.0 :
            label_x     = max(0, padding, bmp.GetWidth()  - padding - w)
        elif label_x    < 0 :
            label_x    += bmp.GetWidth()
        label_x         = int(label_x)
        if  -1.0 < label_y < 0.0 :
            label_y     = max(0, padding, bmp.GetHeight() - padding - max(h, ld + d))
        elif label_y    < 0 :
            label_y    += bmp.GetHeight()
        label_y         = int(label_y)
        dc.DrawText(label, label_x, label_y)

        dc.Destroy()
    pass



def do_it(image_file, timeout = 0, quit_file_name = None, label = "", label_x = 0, label_y = 0, padding = 0, color = None, font_size = None) :
    """
        Splash an image forever or for a given amount of time, always looking for a file whose existence tells us to go off-screen.

        Return False and do nothing if the image file can't be loaded.

    """

    try                     :
        me                  = wx.App(0)
        me.qfn              = quit_file_name or os.path.join(tempfile.gettempdir(), os.path.splitext(os.path.basename(__file__))[0])

        bmp                 = None
        if  isinstance(image_file, basestring) and (not tzlib.safe_file_size(image_file)) :
            bmp             = cStringIO.StringIO(image_file)
            if  bmp         :
                bmp         = wx.ImageFromStream(bmp)
            if  bmp         :
                bmp         = wx.BitmapFromImage(bmp)
            pass

        if  bmp             :
            pass
        elif tzlib.is_pil_image(image_file) :
            bmp             = wx.BitmapFromImage(pil_to_image(image_file))
        elif isinstance(image_file, wx.Image) :
            bmp             = wx.BitmapFromImage(image_file)
        elif hasattr(image_file, 'Size') :
            bmp             = image_file
        else                :
            bmp             = wx.Bitmap(image_file, wx.BITMAP_TYPE_ANY)
        if  not bmp :
            return(False)

        paint_label(bmp, label = label, label_x = label_x, label_y = label_y, padding = padding, color = color, font_size = font_size)

        me.win  = wx.SplashScreen(bmp, wx.SPLASH_CENTRE_ON_SCREEN | ((timeout and wx.SPLASH_TIMEOUT) or 0), max(KEEP_FROM_CRASHING_DURATION, abs(timeout * 1000)), None, wx.ID_ANY)
        win     = me.win.GetSplashWindow()
        win.Bind(wx.EVT_MOUSE_EVENTS,   _on_ignore)
        win.Bind(wx.EVT_KEY_DOWN,       _on_ignore)

        t       = threading.Thread(target = stopit, args = [ me ])
        t.setDaemon(True)
        t.start()

        me.MainLoop()
        me.win  = None                      # help the thread die

        tzlib.whack_file(me.qfn)            # clean up

    except :
        tzlib.print_exception()

    return(True)


def multi_do_it(*args, **kwargs)    :   # image_file, timeout = 0, quit_file_name = None, label = "", label_x = 0, label_y = 0, padding = 0, color = None, font_size = None) :
    """ Do do_it() in a process. Return the process handle. """
    p   = multiprocessing.Process(name = 'tz_wx_splash', target = do_it, args = args, kwargs = kwargs)  # ( image_file, timeout, quit_file_name, label, label_x, label_y, padding, color, font_size, ))
    p.start()
    return(p)



help_str    = """
%s (options) image_file_name

    Shows a splash window.

Options:

    --timeout   seconds             How long to show the window, maximum.

    --quit_file file_name           File whose existence tells us to quit.
                                      (default: %s)

    --label     text                Draw the given text on the image.
    --label_x   pixels_from_side    Where to draw the label.
                                        Negative number is from right side.
                                        Negative number above -1.0 means flush right.
                                        (default: %u)
    --label_y   pixels_from_side     Where to draw the label.
                                        Negative number is from bottom.
                                        Negative number above -1.0 means flush bottom.
                                        (default: %u)
    --label_padding       pixels    How many pixels to pad a flushed label.
                                        (default: %u)
    --label_color         R,G,B     Set the label color in #RRGGBB or red,green,blue or named form.
                                        (default: black)
    --font_size           points    Set the font size.
                                        (default: %u)

    --quit                          Tell any running instance of this
                                    program to quit using the quit file.

    --quit_min  seconds             Tell any running instance of this
                                    program to quit using the quit file,
                                    but only after the splash is shown for
                                    a minimum duration in seconds.

"""



if __name__ == '__main__' :

    import  TZCommandLineAtFile
    import  tz_os_priority

    program_name   = sys.argv.pop(0)

    TZCommandLineAtFile.expand_at_sign_command_line_files(sys.argv)

    quit_file_name  = os.path.join(tempfile.gettempdir(), os.path.splitext(os.path.basename(__file__))[0])
    timeout         = 0

    label           = None
    label_x         = 0
    label_y         = 0
    padding         = 5
    color           = None
    font_size       = FONT_SIZE


    while True :
        oi  = tzlib.array_find(sys.argv, [ "--help", "-h", "-?", "/h", "/H", "/?" ] )
        if  oi < 0  : break
        del sys.argv[oi]
        print help_str % ( os.path.basename(program_name), quit_file_name, label_x, label_y, padding, font_size, )
        sys.exit(254)


    while True      :
        oi  = tzlib.array_find(sys.argv, [ "--time_out", "--time-out", "--timeout", "--to", "-t", "--duration", "-d", ] )
        if  oi < 0  : break
        del sys.argv[oi]
        timeout = float(sys.argv.pop(oi))


    while True      :
        oi  = tzlib.array_find(sys.argv, [ "--quit_file_name", "--quit-file-name", "--quitfilename", "--qfn", "--quit_file", "--quit-file", "--quitfile", "--qf", ] )
        if  oi < 0  : break
        del sys.argv[oi]
        quit_file_name = sys.argv.pop(oi)
    tzlib.whack_file(quit_file_name)                                    # get rid of legacy files


    while True      :
        oi  = tzlib.array_find(sys.argv, [ "--quit", "-q", ] )
        if  oi < 0  : break
        del sys.argv[oi]
        if  tzlib.safe_write_whole_binary_file(quit_file_name, 'q\n') :
            print "Told any running instance of ourself to stop."
            sys.exit(0)
        print >>sys.stderr, "Could not write %s!" % quit_file_name
        sys.exit(103)


    while True      :
        oi  = tzlib.array_find(sys.argv, [ "--quit_min", "--quit-min", "--quitmin", "--quit_minimum", "--quit-minimum", "--quitminimum", "--qm", ] )
        if  oi < 0  : break
        del sys.argv[oi]
        d           = float(sys.argv.pop(oi))
        if  tzlib.safe_write_whole_binary_file(quit_file_name, 'q %f\n' % d) :
            print "Told any running instance of ourself to stop after showing a minimum of %f seconds." % d
            sys.exit(0)
        print >>sys.stderr, "Could not write %s!" % quit_file_name
        sys.exit(103)


    while True      :
        oi  = tzlib.array_find(sys.argv, [ "--label", "-l", ] )
        if  oi < 0  : break
        del sys.argv[oi]
        label   = sys.argv.pop(oi)

    while True      :
        oi  = tzlib.array_find(sys.argv, [ "--label_x", "--lx", "-x", ] )
        if  oi < 0  : break
        del sys.argv[oi]
        label_x = float(sys.argv.pop(oi))

    while True      :
        oi  = tzlib.array_find(sys.argv, [ "--label_y", "--ly", "-y", ] )
        if  oi < 0  : break
        del sys.argv[oi]
        label_y = float(sys.argv.pop(oi))


    while True      :
        oi  = tzlib.array_find(sys.argv, [ "--padding", "-p", "--label_padding", "--label-padding", "--labelpadding", "--lp", ] )
        if  oi < 0  : break
        del sys.argv[oi]
        padding = float(sys.argv.pop(oi))

    while True      :
        oi  = tzlib.array_find(sys.argv, [ "--color", "-c", "--label_color", "--label-color", "--labelcolor", "--lc", ] )
        if  oi < 0  : break
        del sys.argv[oi]
        color   = sys.argv.pop(oi)
        color   = re.sub(r'[^a-zA-Z0-9,\#\s]', '', color)
        color   = re.sub(r'\s+', ' ', color)
        color   = color.replace(' ', ',')
        color   = color.strip(',')
        cls     = color.split(',')
        if  len(cls) == 3 :
            color   = "#%02x%02x%02x" % ( int(cls[0]), int(cls[1]), int(cls[2]), )
        pass

    while True      :
        oi  = tzlib.array_find(sys.argv, [ "--size", "--label_font_size", "--label-font-size", "--labelfontsize", "--font_size", "--font-size", "--fontsize", "--fs", ] )
        if  oi < 0  : break
        del sys.argv[oi]
        font_size   = float(sys.argv.pop(oi))


    if  not len(sys.argv) :
        print >>sys.stderr, "Please tell me an image file to display!"
        sys.exit(101)

    img_file    = sys.argv.pop(0)

    if  len(sys.argv) :
        print >>sys.stderr, "I do not know what to make of %s !" % sys.argv
        sys.exit(102)

    sys.argv.insert(0, program_name)                                    # wx wants this

    tz_os_priority.set_proc_to_idle_priority(0)                         # bring our CPU priority down to low

    if  not do_it(img_file, timeout = timeout, quit_file_name = quit_file_name, label = label, label_x = label_x, label_y = label_y, padding = padding, color = color, font_size = font_size) :
        print >>sys.stderr, "I cannot open %s !" % img_file
        sys.exit(104)

    pass


#
#
#
# eof
