#!/usr/bin/env python3

'''**********************************************************************
 Publite2 is a process manager which launches multiple Freeciv-web servers.
    Copyright (C) 2009-2017  The Freeciv-web project

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

***********************************************************************'''


from os import *

import sys
import time
import http.client
import configparser
from pubstatus import PubStatus
from civlauncher import Civlauncher
import os.path
import glob

metahost = "localhost"
metaport = 8080
metapath = "/freeciv-web/meta/metaserver"
statuspath = "/freeciv-web/meta/status"
settings_file = "settings.ini"
game_types = ["singleplayer", "multiplayer", "pbem"]

metachecker_interval = 40
port = 6000

# The Metachecker class connects to the Freeciv-web metaserver, gets the number of available
# Freeciv-web server instances, and launches new servers if needed.


class metachecker():
    def __init__(self):
        self.server_list = []
        if not os.path.isfile(settings_file):
            print("ERROR: Publite2 isn't setup correctly. Copy settings.ini.dist to settings.ini " +
                  "and update it do match your system.")
            sys.exit(1)
        settings = configparser.ConfigParser()
        settings.read(settings_file)
        self.server_capacity_single = int(settings.get("Resource usage", "server_capacity_single",
                                                       fallback=5))
        self.server_capacity_multi = int(settings.get("Resource usage", "server_capacity_multi",
                                                      fallback=2))
        self.server_capacity_pbem = int(settings.get("Resource usage", "server_capacity_pbem",
                                                     fallback=2))

        self.server_limit = int(settings.get("Resource usage", "server_limit",
                                             fallback=250))
        self.savesdir = settings.get("Config", "save_directory",
                                     fallback="/var/lib/tomcat10/webapps/data/savegames/")

        self.check_count = 0
        self.total = 0
        self.single = 0
        self.multi = 0
        self.pbem = 0
        self.longturn = set()
        self.html_doc = "-"
        self.last_http_status = -1
        s = PubStatus(self)
        s.start()

    def check(self, port):
        while 1:
            try:
                time.sleep(1)
                conn = http.client.HTTPConnection(metahost, metaport)
                conn.request("GET", statuspath)
                r1 = conn.getresponse()
                self.check_count += 1
                self.last_http_status = r1.status
                if (r1.status == 200):
                    self.html_doc = r1.read()
                    meta_status = self.html_doc.decode('ascii').split(";")
                    if (len(meta_status) == 5):
                        self.total = int(meta_status[1])
                        self.single = int(meta_status[2])
                        self.multi = int(meta_status[3])
                        self.pbem = int(meta_status[4])

                        fork_bomb_preventer = (
                            self.total == 0 and self.server_limit < len(self.server_list))
                        if fork_bomb_preventer:
                            print("Error: Have tried to start more than " + str(self.server_limit)
                                  + " servers (the server limit) but according to the"
                                  + " metaserver it has found none.")

                        # Start LongTurn games, one per pass
                        lt_scripts = glob.glob('pubscript_longturn_*.serv')
                        self.longturn.intersection_update(lt_scripts)
                        for script in lt_scripts:
                            if script not in self.longturn:
                                # script[10:-5] is the magic needed for now because
                                # init-freeciv-web adds its own 'pubscript_' and '.serv'
                                new_server = Civlauncher(
                                    "longturn", script[10:-5], port, metahost + ":" + str(metaport) + metapath, self.savesdir)
                                self.server_list.append(new_server)
                                new_server.start()
                                port += 1
                                self.longturn.add(script)
                                break

                        while (self.single < self.server_capacity_single
                               and self.total <= self.server_limit
                               and not fork_bomb_preventer):
                            time.sleep(1)
                            new_server = Civlauncher(
                                game_types[0], game_types[0], port, metahost + ":" + str(metaport) + metapath, self.savesdir)
                            self.server_list.append(new_server)
                            new_server.start()
                            port += 1
                            self.total += 1
                            self.single += 1

                        while (self.multi < self.server_capacity_multi
                               and self.total <= self.server_limit
                               and not fork_bomb_preventer):
                            time.sleep(1)
                            new_server = Civlauncher(
                                game_types[1], game_types[1], port, metahost + ":" + str(metaport) + metapath, self.savesdir)
                            self.server_list.append(new_server)
                            new_server.start()
                            port += 1
                            self.total += 1
                            self.multi += 1

                        while (self.pbem < self.server_capacity_pbem
                               and self.total <= self.server_limit
                               and not fork_bomb_preventer):
                            time.sleep(1)
                            new_server = Civlauncher(
                                game_types[2], game_types[2], port, metahost + ":" + str(metaport) + metapath, self.savesdir)
                            self.server_list.append(new_server)
                            new_server.start()
                            port += 1
                            self.total += 1
                            self.pbem += 1

                else:
                    print("Error: Invalid metaserver status")

            except Exception as e:
                self.html_doc = ("Error: Publite2 is unable to connect to Freeciv-web metaserver on http://" +
                                 metahost + metapath + ", error" + str(e))
                print(self.html_doc)
            finally:
                conn.close()
                time.sleep(metachecker_interval)


if __name__ == '__main__':
    # Perform a test-request to the Metaserver
    try:
        conn = http.client.HTTPConnection(metahost, metaport)
        conn.request("GET", statuspath)
        conn.getresponse()
    except Exception as e:
        print("Error: Publite2 is unable to connect to Freeciv-web metaserver on http://"
              + metahost + metapath + ", error" + str(e))
        sys.exit(1)
    finally:
        conn.close()

    # Start the initial Freeciv-web servers
    mc = metachecker()
    for type in game_types:
        new_server = Civlauncher(
            type, type, port, metahost + ":" + str(metaport) + metapath, mc.savesdir)
        mc.server_list.append(new_server)
        new_server.start()
        port += 1

    test_settings = configparser.ConfigParser()
    test_settings.read(settings_file)
    server_capacity_test = int(test_settings.get(
        "Resource usage", "server_capacity_test", fallback=5))
    test_port = int(test_settings.get("Resource usage",
                    "test_port_start_index", fallback=6600))
    for _ in range(server_capacity_test):
        new_server = Civlauncher("multiplayer", "multiplayer", test_port,
                                 metahost + ":" + str(metaport) + metapath, mc.savesdir)
        mc.server_list.append(new_server)
        new_server.start()
        test_port += 1

    print("Publite2 started!")
    time.sleep(20)
    mc.check(port)
