# -*- coding: utf-8 -*-
"""
/***************************************************************************
 SocketIOServer
                                 A QGIS plugin
 This plugin is an integration of the Infra3D application with QGIS
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                              -------------------
        begin                : 2022-09-28
        git sha              : $Format:%H$
        copyright            : (C) 2022 by Sourcepole AG
        email                : hka@sourcepole.ch
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
"""
import platform
import logging

from flask import Flask, render_template
from flask_socketio import SocketIO


class SocketIOServer:

    def __init__(self):
        self.app = Flask(__name__)
        self.app.config["SECRET_KEY"] = "secret!"
        self.app.logger.setLevel(logging.INFO)
        self.socketio = SocketIO(self.app, async_mode="threading")
        self.running = False

        # Register socketio events
        self.socketio.on_event("rpcrequest", self.rpc_request)
        self.socketio.on_event("rpcresponse", self.rpc_response)
        self.socketio.on_event("pubsub", self.pubsub)

        # Register flask routes
        self.app.add_url_rule("/", view_func=self.index)

    def index(self) -> str:
        """Return a rendered HTML string

        Returns:
            _type_: _description_
        """
        return render_template("index.html")

    def rpc_request(self, request: dict):
        """Method to handle and "rpcrequest" event.

        Remote Procedure Call (rpc) is a protocol that specifies
        how a program can call a subroutine (function) in a different program.
        FYI: We use a very simplified version of the protocol here.

        Args:
            request (dict): Dict with the RPC parameters.
                This dict includes:
                    * method: Name fo the method that we want to call
                    * args: The argumentes that we want to pass to that method
        """
        self.app.logger.info(f"RPC REQUEST: {request}")
        self.socketio.emit("rpcrequest", request)

    def rpc_response(self, response: dict):
        """Method to handle an "rpcreponse" event

        Remote Procedure Call (rpc) is a protocol that specifies
        how a program can call a subroutine (function) in a different program.
        FYI: We use a very simplified version of the protocol here.

        Args:
            response (dict): Dict that includes the response parameters:
                This dict includes:
                    * success: Whether the call was successfull or an exception occured
                    * result: Return value of the method
                    * id: Request ID only used to identify the request
        """
        self.app.logger.info(f"RPC RESPONSE: {response}")
        self.socketio.emit("rpcresponse", response)

    def pubsub(self, params: dict):
        """Method to handle pubsub events

        publish-subscribe (pubsub) is a messaging pattern,
        where event can be published / subscribed.
        More information can be found here:
        https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern

        Args:
            params (dict): Dict that includes the event
                that was emitted and his corresponding parameters.
        """
        self.app.logger.info(f"Received pubsub Event: {params}")
        self.socketio.emit(params["event"], params["params"])

    def start(self, port: int = 5000, debug: bool = False):
        """Start the socketio server

        Args:
            port (int): Port to bind to
            debug (bool): Debug mode on / off
        """
        self.running = True
        # Setup dummy stdout, to fix
        # https://github.com/pallets/click/issues/2415
        if platform.system().lower() == "windows":
            import os
            import sys
            sys.stdout = open(os.devnull, 'w')

        # This socketio server is only intended to be used
        # by one client (the QGIS plugin on the local machine)
        # That is why it is OK to define "allow_unsafe_werkzeug" here
        self.socketio.run(
            self.app,
            host="127.0.0.1",
            port=port,
            debug=debug,
            allow_unsafe_werkzeug=True
        )


if __name__ == "__main__":
    server = SocketIOServer()
    server.start(debug=True)
