#!/usr/bin/env python3

#REMARKS ========================
#        ... cv2.V4L2 is used to avoid stuck while cap.read()

# to override print <= can be a big problem with exceptions
from __future__ import print_function # must be 1st
import builtins

import sys

from fire import Fire

from flashcam.version import __version__

from flashcam import config
from flashcam import usbcheck
from flashcam import direct

from flashcam import v4lc

from flashcam import web

import json

import webbrowser
import multiprocessing
import time
import psutil
###from flashcam.real_camera import Camera
#from flask_script import Manager, Server
import subprocess as sp
import atexit

import flashcam.config as config

import flashcam.uniwrec as uniwrec

class Bcolors:
    HEADER = '[95m'
    OKBLUE = '[94m'
    OKGREEN = '[92m'
    WARNING = '[93m'
    FAIL = '[91m'
    ENDC = '[0m'
    BOLD = '[1m'
    UNDERLINE = '[4m'



def exit_handler():

    ok=False
    kill_gunicorn()

def GUNI():
    CMD="gunicorn -w 1 -b 0.0.0.0:8000 -k gevent --timeout 15 web:app"
    CMD="gunicorn --threads 5  -w 1 -b 0.0.0.0:8000  --timeout 15 web:app"
    print(CMD)
    proc = sp.Popen(CMD.split(), shell=False)
    print(CMD)
    proc.wait()
    proc.kill()
    return proc.pid

def API():
   print('i... In API n',config.CONFIG['port'])
   web.app.run(host='0.0.0.0', port=config.CONFIG['port'], threaded=True)


def kill(proc_pid):
    print(f"kiilling  {proc_pid}")
    process = psutil.Process(proc_pid)
    for proc in process.children(recursive=True):
        proc.kill()
    process.kill()

def kill_gunicorn():
    CMD = f"killall -9 gunicorn"
    print("X... KIILLING", CMD)
    sp.Popen(CMD, shell = True )



def initiate_port( port ):
    config.load_config()
    CMD = f'wget --user  {config.CONFIG["user"]} --password {config.CONFIG["password"]} http://localhost:{port}/video -O /dev/null '
    print("\n\n\ni... ",CMD,"\n\n\n")
    #return
    proc = sp.Popen(CMD, shell=True)
    try:
        proc.wait(timeout=3)
    except sp.TimeoutExpired:
        print("D... wget timeout expired")
    kill(proc.pid)
    ##    Camera = Camera # creating the OBJECT
    #camera = Camera( config.CONFIG["product"], "640x480" )
    #camera.get_frame()

#class CustomServer(Server):
#    def __call__(self, app, *args, **kwargs):
#        custom_call()
#        #Hint: Here you could manipulate app
#        return Server.__call__(self, app, *args, **kwargs)





def main(cmd = "usage", product="", res="640x480",
         frame_kind = "direct",
         threshold = 0,
         average = 0,
         blur = 0,
         laps = -1,
         web5000 = False,

         expo = -1,
         gain = -1,
         mmaga = -1,
         histogram = False,

         save = False,  # this is for UNI - save avi
         qpassfile = "~/.pycamfw_userpass",

         #--astro
         x = 0,
         y = 0,
         debug=False):
    ''' Main function of the project
-c  command
-p  product/pathID
-r  resolution

-f  frame_kind  target_frame
-a  average  averaging
-b blur
-t threshold
-l laps
-w web5000 ... view flask

-e  expo ( in v4l )
-g  gain
-m  gamma
-h  histogram (show mean in image)

-s ....  for UNI AVI
-q .... passfile for UNI

-x
-y ... speed of translation for astrophotography

-d
    '''

    #problematic - i cannot SAVE directly, it rewrites the changes
    #if  not("debug" in config.CONFIG):
    #config.CONFIG['debug'] = debug
    #config.save_config()

    #======================= I copy this to web.py so that gunicorn runs ok
    if not debug:
        print("X... NO DEBUG colors in main")
        _print = print # keep a local copy of the original print
        builtins.print =lambda *args, **kwargs:  None  if (isinstance(args[0], str)) and (args[0].find("D...")==0) else  _print( *args, **kwargs) if ('file' in kwargs) else _print( "{}".format(Bcolors.FAIL   if ((isinstance(args[0], str)) and (args[0].find("X...")>=0)) else Bcolors.ENDC) , *args, Bcolors.ENDC, **kwargs, file=sys.stderr)
    else:
        print("X... DEBUG COLORS FROM MAIN")
        # debug - show all + colors
        _print = print # keep a local copy of the original print
        builtins.print =lambda *args, **kwargs:   _print( *args, **kwargs) if ('file' in kwargs) else _print( "{}".format(Bcolors.FAIL   if ((isinstance(args[0], str)) and (args[0].find("X...")>=0)) else Bcolors.OKGREEN if  ((isinstance(args[0], str)) and (args[0].find("i...")>=0)) else Bcolors.ENDC  ), *args, Bcolors.ENDC, **kwargs, file=sys.stderr)

    #======== DEFINE THE CONFIG FILE HERE ========
    #========   NONONONO   NO              ======
    #======== in this case, all config handling must be win web.py
    #------- as it is the second ENTRY point
    #-------- and the only for gunicorn -----------------



    all_commands = ["usage","savecfg","flask5000", "flask", "show", "ls" , "getres" , "v4l", "uni"]
    if not (cmd in all_commands):
        print("X... command not found!",  all_commands)
        sys.exit(1)
    #if frame_kind == "histo":
    #    histogram = True



    if cmd == "usage":
        print(''' ... usage:
    flashcam ls
    flashcam getres [product | IDpath ]        # flashcam getres "Webcam C270"
    flashcam show ... show
    flashcam show  "Webcam C270"  -r 320x176

    flashcam flask & # (just a hint for gunicorn+wget)
    flashcam flask5000 [-w]   #  view on port 5000, immediatelly start browser tab

    flashcam savecfg [-a 11 ...]  # save cmdline parameters to congig for flask/gunicorn

..... advanced usage .... (accumulation, blur, threshold, frame_kind)
    flashcam show -a 19 -b 3 -t 100 -f [direct|delta|detect|histo]  [-h]  [-l 3600]

....  v4l setting .......... (exposure, gain  for automatically recommended video)
    flashcam v4l -e [auto | 0.0..1.0]  -g [0.0..1.0]

...... uninteruptable stream .....................
    flashcam uni http://192.168.0.91:8000/video  [-s ] # to save avi
        ''')
        sys.exit(0)

    elif cmd == "uni":
        uniwrec.display2( product, save = save, passfile = qpassfile)
        sys.exit(0)


    elif cmd == "v4l":

        recomvid = usbcheck.recommend_video( product )

        cc = v4lc.V4L2_CTL("/dev/video"+str(recomvid[0]))
        v4lc.get_resolutions( recomvid[0]  )
        #print(cc._list_controls())
        capa = cc.get_capbilities()


        if expo == "auto":
            if "exposure_absolute" in capa:
                print("D... AUTO EXPOSURE ON")
                cc.setdef_exposure_auto() # doesnt do default 3
                print("ex def",cc.get_exposure_auto())
                cc.setdef_exposure_auto()  # i thik he knows what is auto
        elif expo !=-1:
            if "exposure_absolute" in capa:
                if "exposure_auto" in capa:
                    print("D... AUTO EXPOSURE OFF")
                    cc.setdef_exposure_auto() # doesnt do default 3
                    print("ex def",cc.get_exposure_auto())
                    cc.set_exposure_auto(1)  # I just a guess, 1 may be manual, 3


                    ex = cc.get_exposure_absolute()
                    mine = cc.getmin_exposure_absolute()
                    maxe = cc.getmax_exposure_absolute()
                    print(f"i... current exposure {ex}   range {mine}, {maxe}")

                    ex = int( expo * (maxe-mine)+mine)
                    print("i... new = ",ex)
                    if ex>maxe: ex=maxe
                    if ex<mine: ex=mine

                    cc.set_exposure_absolute(ex)
            # very stupid wabcam
            elif "exposure" in capa:
                ex = cc.get_exposure()
                mine = cc.getmin_exposure()
                maxe = cc.getmax_exposure()
                print(f"i... current exposure {ex}   range {mine}, {maxe}")

                ex = int( expo * (maxe-mine)+mine)
                print("i... new = ",ex)
                if ex>maxe: ex=maxe
                if ex<mine: ex=mine

                cc.set_exposure(ex)
            else:
                print("X... exposure_absolute NOR exposure not in capacities")

        if gain == "def":
            cc.setdef_gain()
        elif gain !=-1:
            if "gain" in capa:
                ga = cc.get_gain()
                ming = cc.getmin_gain()
                maxg = cc.getmax_gain()
                print(f"i... current gain {ga}   range {ming}, {maxg}")

                ga = int( gain * (maxg-ming)+ming)
                print("i... new = ",ga)
                if ga>maxg: ga=maxg
                if ga<ming: ga=ming

                cc.set_gain(ga)
            else:
                print("X... gain noit in capacities")


        if mmaga == "def":
            cc.setdef_gamma()
        elif mmaga !=-1:
            if "gamma" in capa:
                ga = cc.get_gamma()
                ming = cc.getmin_gamma()
                maxg = cc.getmax_gamma()
                print(f"i... current gamma {ga}   range {ming}, {maxg}")

                ga = int( mmaga * (maxg-ming)+ming)
                print("i... new = ",ga)
                if ga>maxg: ga=maxg
                if ga<ming: ga=ming

                cc.set_gamma(ga)
            else:
                print("X... gamma not in capacities")

        sys.exit(0)

    elif cmd == "ls":
        usbcheck.recommend_video()


    elif cmd == "savecfg":   # i dont have it loaded ....
        print( "i... USER  bin_fla:", config.CONFIG['user'] )
        config.CONFIG['target_frame'] = frame_kind
        config.CONFIG['threshold'] = threshold
        config.CONFIG['timelaps'] = laps
        config.CONFIG['average'] = average
        config.CONFIG['blur'] = blur
        config.CONFIG['histogram'] = histogram
        config.CONFIG['x'] = x
        config.CONFIG['y'] = y
        config.save_config()

    elif cmd == "show2":
        recomvid = usbcheck.recommend_video( product )
        # usbcheck.show_cam( recomvid[0], res, recommended= product)

    # --------------------------------------------------
    elif cmd == "show":
        recomvid = usbcheck.recommend_video( product )
        # how recomvid goes there?
        config.CONFIG["recommended"] = product  # hopefully this

        if (threshold>0) and (blur==0) and (average<2):
            print("X... cannot be -thr>0 and average==0")
            sys.exit(1)

        config.CONFIG['timelaps'] = laps
        config.CONFIG['target_frame'] = frame_kind
        config.CONFIG['threshold'] = threshold
        config.CONFIG['average'] = average
        config.CONFIG['blur'] = blur
        config.CONFIG['histogram'] = histogram
        config.CONFIG['resolution'] = res
        config.CONFIG['x'] = x
        config.CONFIG['y'] = y
        direct.show_cam( )

    elif cmd == "getres":
        recomvid = usbcheck.recommend_video( product )
        resolutions = usbcheck.get_resolutions( recomvid[0] )

    elif cmd == "flask":
        print("i... NOT STARTING flask NOT STARTING GUNICORN")

        CMD="gunicorn --threads 5  -w 1 -b 0.0.0.0:8000  --timeout 15 web:app"
        print(CMD)
        print(CMD)
        print(CMD)
        print("i... now i try to initiate port 8000 and quit")
        time.sleep(4)
        initiate_port(8000)
        sys.exit(0)

        #config.CONFIG["recommended"] = product
        ## gevent appear extremly slow (1f/2sec)
        ## no gevent = synchronous - but fast
        ## !   1 worker = one camerap

        #atexit.register(exit_handler) # killall -9 gunicorn
        #p = multiprocessing.Process(target=GUNI)
        #p.start()
        # proc = sp.Popen(CMD.split(), shell=False)
        #time.sleep(3)
        ##p.join() # wait for gunicorn nnono
        #kill_gunicorn()

    elif cmd == "flask5000":
        saveme = False
        print( "i... USER  bin_fla:", config.CONFIG['user'] )
        if config.CONFIG['target_frame'] != frame_kind:
            config.CONFIG['target_frame'] = frame_kind
            saveme = True
        if config.CONFIG['threshold']    != threshold:
            config.CONFIG['threshold']    = threshold
            saveme = True
        if config.CONFIG['average']      != average:
            config.CONFIG['average']      = average
            saveme = True
        if config.CONFIG['blur']         != blur:
            config.CONFIG['blur']         = blur
            saveme = True
        if config.CONFIG['timelaps']         != laps:
            config.CONFIG['timelaps']         = laps
            saveme = True
        if config.CONFIG['histogram']         != histogram:
            config.CONFIG['histogram']         = histogram
            saveme = True
        if config.CONFIG['resolution']         != res:
            config.CONFIG['resolution']         = res
            saveme = True
        if config.CONFIG['x']         != x:
            config.CONFIG['x']         = x
            saveme = True
        if config.CONFIG['y']         != y:
            config.CONFIG['y']         = y
            saveme = True
        if saveme:
            config.save_config()

        print("i... flask starting on port 5000")
        p = multiprocessing.Process(target=API)
        p.start()
        if web5000:
            webbrowser.open('http://localhost:5000')
        else:
            time.sleep(3)
            initiate_port(5000)


    else:
        print("X... NO COMMAND GIVEN, ENDING")
        unitname.func()


if __name__=="__main__":
    Fire(main)
