#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
    Скрипт для управления Lunapark API сервером.
    Позволяет запустить, остановить сервер, получить информацию о текущем статусе.
"""

import os
import socket
import subprocess
import fcntl
import errno
import signal
import time

PID_FILENAME = 'tank_api_server.pid'


def check_server_pid(workdir):
    """
        Проверить pid из рабочей директории
        @return: pid в виде числа int, если файл существует, в нём записано число и он залочен;
                 в противном случае возвращается None
    """
    pidfile_name = os.path.join(workdir, PID_FILENAME)
    if not os.path.exists(pidfile_name):
        # если нет pid-файла, считаем, что сервер не запущен
        return None
    pidfile = open(pidfile_name, 'r')
    pid = int(pidfile.read())
    pidfile.close()
    # проверяем лок файла
    pidfile = open(pidfile_name, 'a')
    try:
        fcntl.lockf(pidfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
    except IOError as error:
        if error.errno in (errno.EACCES, errno.EAGAIN):
            return pid
        else:
            raise
    fcntl.lockf(pidfile, fcntl.LOCK_UN)
    pidfile.close()
    return None


def is_port_free(port):
    """
        Занят ли порт @port на localhost
        @return: True, если порт занят; в противном случае возвращается False
    """
    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect(('localhost', port))
        sock.close()
        return False
    except socket.error:
        pass
    return True


def is_lunapark_api_server_launched(script_options):
    """
        Запущен ли сервер (проверяется pid и указанный порт)
        @return: True, если сервер запущен; в противном случае возвращается False
    """
    # проверяем pid-файл
    pid = check_server_pid(script_options.workdir)
    if not pid:
        return False
    # проверяем, занят ли порт
    if is_port_free(script_options.port):
        return False
    else:
        return True


def get_lunapark_api_server_state(script_options):
    """
        Вывести на экран состояние Lunapark API server
    """
    result = 'Stopped.'
    if is_lunapark_api_server_launched(script_options):
        result = 'Launched.'
    return result


def print_lunapark_api_server_config(script_options):
    """
        Вывести на экран текущую конфигурацию сервера
    """
    print('Tank API server settings:')
    print('  - Tank API server workdir: %s' % script_options.workdir)
    print('  - Tank API server port: %s' % script_options.port)
    print('  - Tank API server source dir: %s' % script_options.sourcedir)


def print_lunapark_api_info(script_options):
    """
        Вывести на экран информацию о сервере
    """
    print("Lunapark API server state: %s" % get_lunapark_api_server_state(
        script_options))
    print_lunapark_api_server_config(script_options)


def start_lunapark_server(script_options):
    """
        Запустить сервер.
        Сервер запускается, если он ещё не запущен (проверка по PID файлу) и свободен указанный порт
    """
    print('Run tank API server.')
    print_lunapark_api_server_config(script_options)
    server_pid = check_server_pid(script_options.workdir)
    if server_pid:
        print("Tank API server is already launched. PID: %s." % server_pid)
        return
    if is_port_free(script_options.port):
        server_path = os.path.join(script_options.sourcedir,
                                   'tank_api_server.py')
        server_environ = {
            'LUNAPARK_API_SERVER_PORT': str(script_options.port),
            'LUNAPARK_API_SERVER_WORKDIR': str(script_options.workdir),
            'LUNAPARK_API_SERVER_DAEMONIZE': 'True'
        }
        subprocess.Popen(server_path, env=server_environ)
        return
    else:
        print('Error. Port %s is not free. Cannot start the server.' % script_options.port)
        exit(1)


def stop_lunapark_server(script_options):
    """
        Остановить Lunapark API сервер.
        Проверяется лок PID-файла, если он залочен, убивается процесс, PID которого указан в файле
    """
    print('Stop tank API server.')
    server_pid = check_server_pid(script_options.workdir)
    print(server_pid)
    if server_pid:
        print("Kill process %s." % server_pid)
        os.kill(server_pid, signal.SIGTERM)
    else:
        print('Server is not launched.')


def restart_lunapark_server(script_options):
    """
        Перезапустить Lunapark API сервер
    """
    if is_lunapark_api_server_launched(script_options):
        stop_lunapark_server(script_options)
        print("Wait 3 seconds")
        time.sleep(3)
    start_lunapark_server(script_options)
