#!/usr/bin/env python
# =============================================================================
#               ____ _   _ ____  _           _ _
#  _ __  _   _ / ___| | | |  _ \| |      ___| (_)
# | '_ \| | | | |  _| |_| | | | | |     / __| | |
# | |_) | |_| | |_| |  _  | |_| | |___ | (__| | |
# | .__/ \__, |\____|_| |_|____/|_____(_)___|_|_|
# |_|    |___/
# =============================================================================
# Authors:
#   Tristan Gingold
#
# Package module:   GHDLs Language Server implementing LSP for VHDL.
#
# License:
# ============================================================================
#  Copyright (C) 2019-2021 Tristan Gingold
#
#  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.
#
#  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, see <gnu.org/licenses>.
#
# SPDX-License-Identifier: GPL-2.0-or-later
# ============================================================================

from argparse import ArgumentParser
from logging import getLogger, DEBUG, INFO, ERROR, basicConfig
from os import environ as os_environ, getcwd as os_getcwd, name as os_name
from pathlib import Path
from sys import (
    argv as sys_argv,
    exit as sys_exit,
    platform as sys_platform,
    stdin as sys_stdin,
    stdout as sys_stdout,
    stderr as sys_stderr,
    version as sys_version,
)

from pyTooling.Decorators import export

from pyGHDL import __version__ as ghdlVersion
from pyGHDL.libghdl import errorout_console, disp_config
from pyGHDL.lsp import LSPConnTrace
from pyGHDL.lsp.lsp import LSPConn, LanguageProtocolServer
from pyGHDL.lsp.vhdl_ls import VhdlLanguageServer

__loggerName = "ghdl-ls"


def __rotate_log_files(basename: str, num: int):
    """Rotate existing log files."""
    # Remove the oldest file.  This one will be lost.
    # Required on Windows as it is an error to rename a file to an existing
    # one.
    # Note: Path.with_suffix cannot be used as there might be multiple
    # suffixes (like in trace.out.0).
    oldfile = Path(f"{basename}.{num}")
    if oldfile.is_file():
        oldfile.unlink()
    # Rotate old files
    for i in range(num, 0, -1):
        oldfile = Path(f"{basename}.{i-1}")
        if oldfile.is_file():
            oldfile.rename(Path(f"{basename}.{i}"))
    # Rotate the newest log file.
    bname = Path(basename)
    if bname.is_file():
        bname.rename(Path(f"{basename}.0"))


def _generateCLIParser() -> ArgumentParser:
    """Creates an CLI argument parser based on ``argparse``."""
    parser = ArgumentParser(
        description="VHDL Language Protocol Server. Find info about clients in `ghdl/ghdl-language-server <https://github.com/ghdl/ghdl-language-server>`__."
    )
    parser.add_argument("--version", "-V", action="version", version="%(prog)s " + ghdlVersion)
    parser.add_argument("--verbose", "-v", action="count", default=0, help="Show debug output")
    parser.add_argument("--log-file", help="Redirect logs to the given file instead of stderr")
    parser.add_argument(
        "--trace-file",
        help="Save RPC data to FILE.in and FILE.out (overrides GHDL_LS_TRACE)",
    )
    parser.add_argument("--input", "-i", help="Read request from file")
    parser.add_argument(
        "--disp-config",
        action="store_true",
        help="Display installation configuration and exit",
    )

    return parser


@export
def main():
    """Entrypoint of GHDL's Language Protocol Server."""
    logger = getLogger(__loggerName)

    parser = _generateCLIParser()
    args = parser.parse_args()

    if args.disp_config:
        errorout_console.Install_Handler()
        disp_config()
        print("python:")
        print(f"sys.platform: {sys_platform}, os.name: {os_name}")
        print(sys_version)
        return

    # Setup logging
    if args.verbose >= 2:
        loglevel = DEBUG
    elif args.verbose >= 1:
        loglevel = INFO
    else:
        loglevel = ERROR

    if args.log_file:
        __rotate_log_files(args.log_file, 5)
        logstream = open(args.log_file, "w")
    else:
        logstream = sys_stderr

    basicConfig(
        format="%(asctime)-15s [%(levelname)s] %(message)s",
        stream=logstream,
        level=loglevel,
    )

    if args.verbose != 0:
        sys_stderr.write(f"Args: {sys_argv}\n")
        sys_stderr.write(f"Current directory: {os_getcwd()}\n")

    logger.info("Args: %s", sys_argv)
    logger.info("Current directory is %s", os_getcwd())

    # Connection
    instream = sys_stdin.buffer
    if args.input is not None:
        instream = open(args.input, "rb")

    conn = LSPConn(instream, sys_stdout.buffer)

    trace_file = args.trace_file
    if trace_file is None:
        trace_file = os_environ.get("GHDL_LS_TRACE")
    if trace_file is not None:
        if args.input is None:
            __rotate_log_files(trace_file + ".in", 5)
            __rotate_log_files(trace_file + ".out", 5)
            conn = LSPConnTrace(trace_file, conn)
        else:
            logger.info("Traces disabled when -i/--input")

    handler = VhdlLanguageServer()

    try:
        server = LanguageProtocolServer(handler, conn)
        server.run()
    except Exception:
        logger.exception("Uncaught error")
        sys_exit(1)


if __name__ == "__main__":
    main()
