"""
#*********************************#
#*           Grail               *#
#*********************************#
#*   Author: Lukas Mahler        *#
#*   Copyright: (C)2018-2020     *#
#*   Version: 0.5.0              *#
#*   Date: 25.12.2020            *#
#*********************************#
# Instant Upload Screenshot-Tool  #
# based on the idea of Gyazoo.    #
# -> MS Windows only!             #
#*********************************#
"""

__author__ = "Lukas Mahler"
__copyright__ = "Copyright 2018-2021"
__version__ = "0.5.1"
__date__ = "16.01.2021"
__email__ = "lm@ankerlab.de"
__status__ = "Development"


try:
    # Defaults
    import os
    import sys
    import webbrowser
    from io import BytesIO
    from shutil import rmtree
    from getpass import getpass
    from tempfile import gettempdir
    from socket import create_connection
    from configparser import ConfigParser
    from time import gmtime, strftime, sleep

    # GUI
    from PyQt4 import QtGui, QtCore, Qt
    from PyQt4.QtCore import QProcess, QPoint
    from fillscreen import Ui_View

    # Customs
    import win32gui
    import win32clipboard
    import paramiko
    from desktopmagic.screengrab_win32 import getRectAsImage

except ImportError as e:
    print("ERROR: Missing Module |||", e)
    sleep(10)
    os.system("PAUSE>NUL")
    sys.exit()


###################################################################################################
###################################################################################################


class Fillscreen(QtGui.QWidget, Ui_View):

    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        ###############################################################################################
        ###############################################################################################

        # Bastelstüble wegen Transparentem Window welches aber den Input auf unterliegendes blockieren soll

        # self.setAttribute(QtCore.Qt.WA_TranslucentBackground, True)
        # self.setBackgroundRole(QtGui.QPalette.Base)
        # self.setAttribute(Qt.Qt.WA_NoSystemBackground, True)
        self.setWindowFlags(
            QtCore.Qt.WindowStaysOnTopHint |
            QtCore.Qt.FramelessWindowHint |
            QtCore.Qt.X11BypassWindowManagerHint
        )
        # self.istransparent = True

        self.setupUi(self)

        # self.setWindowOpacity(0.3)
        # Qcolor = "rgba(255, 255, 255, 0)"
        # self.setStyleSheet("background-color:{0}".format(Qcolor))

        ###############################################################################################
        ###############################################################################################

        # Change Cursor to select tool on Startup
        self.change_cursor()
        self.begin = QtCore.QPoint()
        self.end = QtCore.QPoint()
        self.startingpoint = QtCore.QPoint()
        self.endpoint = QtCore.QPoint()

    def showFullscreen(self):
        self.showMaximized()

    # https://stackoverflow.com/a/44468898/5593051
    def paintEvent(self, event):
        qp = QtGui.QPainter(self)
        # br = QtGui.QBrush(QtGui.QColor(100, 10, 10, 40))
        qp.setPen(QtGui.QPen(QtCore.Qt.red, 3))
        qp.drawRect(QtCore.QRect(self.begin, self.end))

    def mousePressEvent(self, event):
        if event.button() == QtCore.Qt.RightButton:
            print("DEBUG: Exiting on Right Click Request")
            app.quit()
        else:
            self.begin = event.pos()
            self.end = event.pos()
            # self.startingpoint = event.pos()
            self.startingpoint = get_real_pos()
            # print("Startpoint:",self.startingpoint)
            self.update()

    def mouseMoveEvent(self, event):
        self.end = event.pos()
        self.update()

    def mouseReleaseEvent(self, event):
        self.begin = event.pos()
        self.end = event.pos()
        self.endpoint = event.pos()
        self.endpoint = get_real_pos()
        # print("Endpoint:",self.endpoint)
        self.update()
        do_screen(self.startingpoint, self.endpoint)
        app.quit()

    @staticmethod
    def change_cursor():
        # http://doc.qt.io/archives/qt-4.8/qcursor.html
        QtGui.QApplication.setOverrideCursor(QtCore.Qt.CrossCursor)
    # QtGui.QApplication.restoreOverrideCursor()


###################################################################################################
###################################################################################################	


def read_ini(rootdir):
    myini = {}
    config = ConfigParser()
    try:
        config.read_file(open(rootdir+'settings.ini'))
    except Exception as e:
        print("ERROR: Couldn't locate a 'settings.ini' |||", e)
        # Creating new blank ini
        with open(rootdir+'settings.ini', 'w') as f:
            txt = "[MAIN]\n" \
                  "# MODE EITHER ONLINE OR OFFLINE\nMODE = OFFLINE\n\n\n" \
                  "# IF MODE IS ONLINE PROVIDE THE SFTP INFO BELOW\n" \
                  "[ONLINE]\n\n" \
                  "# URL EITHER IS AN IP OR A FQDN\nURL = \n\n" \
                  "# PORT IS THE SSH PORT\nPORT = 22\n\n" \
                  "# THE TIME IN SECONDS TO TRY TO CONNECT\nTIMEOUT = 5\n\n" \
                  "# YOUR SSH USER, THIS CAN BE BLANK TO GET ASKED INTERACTIVELY\nUSR = \nPASS = \n\n" \
                  "# THE LOCAL SERVER PATH WHERE TO PUT THE SCREENSHOT\nPTH = ./Screens/\n\n" \
                  "# THIS SHOULD BE THE FULL ONLINE URL\nWHERE = \n\n\n" \
                  "# IF MODE IS OFFLINE YOU MAY CHANGE THE SETTINGS BELOW\n" \
                  "[OFFLINE]\n\n" \
                  "WHERE = "
            f.write(txt)

    mode = config.get('MAIN', 'MODE')
    if mode == "ONLINE":
        url = config.get('ONLINE', 'URL')
        port = int(config.get('ONLINE', 'PORT'))
        timeout = int(config.get('ONLINE', 'TIMEOUT'))
        usr = config.get('ONLINE', 'USR')
        passw = config.get('ONLINE', 'PASS')
        pth = config.get('ONLINE', 'PTH')
        where = config.get('ONLINE', 'WHERE')
        myini.update(
            mode    = mode,
            URL     = url,
            PORT    = port,
            TIMEOUT = timeout,
            USR     = usr,
            PASS    = passw,
            PTH     = pth,
            WHERE   = where
        )
    elif mode == "OFFLINE":
        where = config.get('OFFLINE', 'WHERE')
        myini.update(
            mode	= mode,
            WHERE	= where
        )
    else:
        print("ERROR: you seem to be having a corrupt or broken 'settings.ini' please recheck!")

    return myini


def get_real_pos():
    flags, hcursor, (x, y) = win32gui.GetCursorInfo()
    return {"x": x, "y": y}


def calc_frame(startinspot, endspot):
    # If dragging began from top
    if startinspot['x'] < endspot['x']:
        left = startinspot['x']
        right = endspot['x']
        top = startinspot['y']
        bottom = endspot['y']
    # If dragging began from bottom
    else:
        left = endspot['x']
        right = startinspot['x']
        top = endspot['y']
        bottom = startinspot['y']

    # print("DEBUG:  Start:",left,"x",top)
    # print("DEBUG:  End  :",right,"x",bottom)

    if abs(left-right) <= 10 and abs(top-bottom) <= 10:
        coordinates = None
    else:
        coordinates = (left, top, right+1, bottom+1)

    return coordinates


def do_screen(start, end):
    """ This Function somehow is the Brain now """
    # QtGui.QPixmap.grabWindow(QtGui.QApplication.desktop().winId()).save('screenshot.jpg', 'jpg')
    # Hide all windows to take a Screenshot of the underlying interface
    for window in windows:
        window.hide()
    frame = calc_frame(start, end)
    print("Took Snapshot on Coordinates:", frame)
    # https://github.com/python-pillow/Pillow/issues/1547
    # https://github.com/ludios/Desktopmagic/blob/master/desktopmagic/screengrab_win32.py
    # im = ImageGrab.grab(bbox=frame)

    if not frame:
        print("ERROR: Screenshots below 5x5 px not allowed, exiting...")
        sleep(5)
        return

    im = getRectAsImage(frame)
    send_to_clipboard(im)
    timestamp = strftime("%Y-%m-%d_%H.%M.%S")

    tempdir = gettempdir()+"\\Screens"
    # print("DEBUG:",tempdir)
    if not os.path.exists(tempdir):
        os.makedirs(tempdir)
    tempsave = '{0}\\{1}.png'.format(tempdir, 'Screen_'+timestamp)
    im.save(tempsave)

    if settings['MODE'] == "ONLINE":
        upload_to_url(tempsave)
    else:
        save_to_local(tempsave)

    clean_temp(tempdir)


# https://stackoverflow.com/questions/34322132/copy-image-to-clipboard-in-python3
def send_to_clipboard(im=None):
    if im:
        output = BytesIO()
        im.convert("RGB").save(output, "BMP")
        data = output.getvalue()[14:]
        output.close()
        win32clipboard.OpenClipboard()
        win32clipboard.EmptyClipboard()
        win32clipboard.SetClipboardData(win32clipboard.CF_DIB, data)
        win32clipboard.CloseClipboard()
        print("Copied to Clipboard")
    else:
        win32clipboard.EmptyClipboard()
        print("Cleared Clipboard")


# https://stackoverflow.com/questions/432385/sftp-in-python-platform-independent
def upload_to_url(tempsave):

    # SFTP INFO
    url 	= settings['URL']
    port 	= settings['PORT']
    timeout = settings['TIMEOUT']
    usr 	= settings['USR']
    passw 	= settings['PASS']
    pth		= settings['PTH'] + os.path.basename(tempsave)
    where	= settings['WHERE'] + os.path.basename(tempsave)

    # Test Connection first or go into fallback
    try:
        s = create_connection((url, port), timeout)
        s.close()
    except Exception as e:
        print("ERROR: Connection failed. |||", e)
        print("\nFalling back to local save...")
        save_to_local(tempsave, fallback=1)
        return

    # If not provided ask for Username & Password
    if not usr:
        usr = input("Please provide your Username: ")

    if not passw:
        passw = getpass("Please provide the password for {0}: ".format(usr))

    try:
        transport = paramiko.Transport((url, port))
        transport.connect(username=usr, password=passw)
    except Exception as e:
        print("ERROR: SSH2 negotiation or authentication failed. |||", e)
        print("\nFalling back to local save...")
        save_to_local(tempsave, fallback=1)
        return

    print("Starting to Upload...")
    sftp = paramiko.SFTPClient.from_transport(transport)
    sftp.put(tempsave, pth)
    sftp.close()
    transport.close()

    print("Screen was Uploaded: URL: " + where)
    open_url(where)


def save_to_local(tempsave, fallback=None):

    if not fallback:
        where = settings['WHERE']
    else:
        print("    -> Did fallback!")
        config = ConfigParser()
        config.read_file(open(root+'_settings.ini'))
        where = config.get('OFFLINE', 'WHERE')

    if not where:
        print("No local Screens folder provided, creating one...")
        where = os.path.dirname(os.path.abspath(__file__))+"\\Screens"

    print("OFFLINE MODE: Screenshot Folder:", where)

    if not os.path.exists(where):
        os.makedirs(where)

    src = tempsave
    dst = where + "\\" + os.path.basename(tempsave)
    # print("Source:",src,"\nDestination:",dst)
    os.rename(src, dst)


def open_url(where):
    webbrowser.open(where, new=0, autoraise=True)


def clean_temp(tempdir):
    if os.path.exists(tempdir):
        print("Cleaning up the tempdir [", tempdir, "]...")
        rmtree(tempdir, ignore_errors=True)


###################################################################################################
###################################################################################################


if __name__ == '__main__':

    root = os.path.dirname(os.path.abspath(__file__))+"\\"
    settings = read_ini(root)
    if os.path.exists(root+"qt.conf"):
        try:
            os.remove(root+"qt.conf")
        except Exception as e:
            print("DEBUG:", e)
    app = QtGui.QApplication(sys.argv)
    windows = []

    # https://stackoverflow.com/questions/51058236/create-qt-windows-based-on-number-of-list-items
    for i in range(QtGui.QApplication.desktop().screenCount()):
        topLeft = QtGui.QApplication.desktop().screenGeometry(i).topLeft()
        window = Fillscreen()
        window.move(topLeft)
        window.showFullscreen()
        windows.append(window)
    app.exec_()