#!/usr/bin/env python2.7

""" Copyright (C) 2012 mountainpenguin (pinguino.de.montana@googlemail.com)
    <http://github.com/mountainpenguin/pyrt>

    This file is part of pyRT.

    pyRT 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 3 of the License, or
    (at your option) any later version.

    pyRT 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 pyRT.  If not, see <http://www.gnu.org/licenses/>.
"""

from __future__ import print_function
from modules import mkpasswd
# move imports to start() to speed up other functions
import os
import traceback
import glob
import time
import subprocess
import argparse
import signal
import psutil

__AUTHOR__ = "mountainpenguin (pinguino.de.montana@googlemail.com)"
__SITE__ = "http://github.com/mountainpenguin/pyrt"
__VERSION__ = "1.0.0"
__LICENSE__ = "GPLv3"


def _searchPID():
    if os.path.exists("proc/pyrt.pid"):
        PID = int(open("proc/pyrt.pid").read().strip())
        # TODO: Find a way to check that PID belongs to Pyrt, if there exist a program with the same PID,
        # because if the Pyrt crash (does not remove the PID file) and another program starts and gets the
        # same PID, then _searchPID will think that Pyrt is already running.

        if psutil.pid_exists(PID):
            # check username of process corresponds to calling user
            p = psutil.Process(PID)
            if p.uids().real != os.getuid():
                raise OSError("PID {0} is not owned by you".format(PID))

            # check calling command includes python and pyrt as arguments
            cmd = p.cmdline()
            if "python" not in cmd[0] or "pyrt" not in cmd[1]:
                raise OSError("PID {0} has been recycled!".format(PID))

            return PID


def _searchBotPID():
    PIDFiles = glob.glob("proc/bots/*.pid")
    # e.g. proc/bots/12345.pid
    pids = []
    for PIDFile in PIDFiles:
        with open(PIDFile) as doc:
            pids.append(int(doc.read().strip()))

    return pids


def _isGitRepo():
    if os.path.exists(".git"):
        return True
    else:
        return False


def _checkRepo():
    p = subprocess.Popen(["git remote update"], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    print("Checking for updates...")
    p.wait()
    stdout, stderr = p.stdout.read(), p.stderr.read()
    if "origin" in stderr:
        return True
    else:
        p2 = subprocess.Popen(["git status"], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        p2.wait()
        stdout, stderr = p2.stdout.read(), p2.stderr.read()
        if "Your branch is behind" in stdout:
            return True
        else:
            print("Repository is up-to-date")
            return False


def _updateRepo():
    # update remote
    os.system("git pull")
    open(".uncache", "a").close()


def start(update=True):
    if _searchPID():
        print("pyRT has already been started and is running")
        return

    if update:
        runUpdate()

    from modules import server
    from modules import config
    from modules import daemon

    print("Loading pyRT configuration from %s" % os.path.join(os.getcwd(), "config/.pyrtrc"))
    c = config.Config()
    c.loadconfig()

    print("Starting daemon with log file %s" % c.get("logfile"))
    STDOUT = open(c.get("logfile"), "a")
    with daemon.DaemonContext(stdout=STDOUT, stderr=STDOUT, working_directory=os.getcwd()):
        serv = server.Main()
        PID = os.getpid()
        open("proc/pyrt.pid", "w").write(str(PID))
        serv.main(c)


def stop(force=False):
    if not _searchPID():
        print("pyRT is not running")
        return

    if force:
        signum = signal.SIGKILL
        signame = "SIGKILL"
    else:
        signum = signal.SIGTERM
        signame = "SIGTERM"

    bots = _searchBotPID()
    for p in bots:
        print("Sending %s to process bot PID %d" % (signame, p))
        try:
            os.kill(p, signum)
        except OSError:
            print("Couldn't kill bot, no process with PID %d" % p)
            if os.path.exists("proc/bots/%d.pid" % p):
                os.remove("proc/bots/%d.pid" % p)
        except:
            traceback.print_exc()

    count = 0
    while count < 3:
        bots = _searchBotPID()
        if len(bots) > 0:
            print("Waiting for bots to die (%d)" % count)
            time.sleep(2)
            count += 1
        else:
            break

    PID = _searchPID()
    print("Sending %s to process PID %d" % (signame, PID))
    try:
        os.kill(PID, signum)
    except OSError:
        print("Something went horribly wrong, there is no process with PID %d" % PID)
        print("To allow restarting, delete the file proc/pyrt.pid")
    except:
        traceback.print_exc()
    else:
        os.remove("proc/pyrt.pid")

    if os.path.exists(".sockets/rss.interface"):
        print("Removing RPC RSS socket file")
        os.remove(".sockets/rss.interface")
    for i in range(10):
        pth = ".sockets/rpc%i.interface" % i
        if os.path.exists(pth):
            print("Removing RPC IRC socket file <%s>" % pth)
            os.remove(pth)


def status():
    if _searchPID():
        print("pyRT is running with PID: %i" % _searchPID())
    else:
        print("pyRT is not running")


def runUpdate():
    if _isGitRepo():
        updateready = _checkRepo()
        if updateready:
            goahead = raw_input("An update is available, download? (Y/N): ")
            if goahead.upper() == "Y":
                _updateRepo()
                return
            else:
                print("Ignoring update")
    else:
        print("Not a git repository")

if __name__ == "__main__":
    # cmd line syntax
    # ./pyrt start | stop | restart | status | mkpasswd
    print("pyRT rtorrent webUI v%s" % (__VERSION__))
    print("Copyright (C) 2012 mountainpenguin")
    print("This program comes with ABSOLUTELY NO WARRANTY")
    print("This is free software, and you are welcome to redistribute it under certain conditions")
    print("See LICENCE for details", end="\n\n")

    parser = argparse.ArgumentParser(description="PyRT rtorrent webUI")
    parser.add_argument("action",  choices=("start", "stop", "restart", "status", "mkpasswd", "update"),  help="Control the webUI daemon")
    parser.add_argument("-n", "--noupdate", help="Don't check for updates", action="store_false")
    parser.add_argument("-f", "--force", help="Force the webUI to stop", action="store_true")
    parser.add_argument("-u", "--uncache", help="Aggressively uncache for the next two refreshes", action="store_true")
    args = parser.parse_args()

    if args.uncache:
        open(".uncache", "a").close()

    if args.action == "start":
        start(args.noupdate)
    elif args.action == "stop":
        stop(args.force)
    elif args.action == "restart":
        stop(args.force)
        start(args.noupdate)
    elif args.action == "status":
        status()
    elif args.action == "mkpasswd":
        mkpasswd.main()
    elif args.action == "update":
        runUpdate()
    else:
        print("Syntax: pyrt start|stop|restart|status|mkpasswd")
