#!/usr/bin/env python3
# IRI-IIF of light version of LI system
# Copyright (C) 2011 Matěj Grégr, Michal Kajan, Libor Polčák, Vladimír Veselý
# 
# 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 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 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 <http://www.gnu.org/licenses/>.

import os
import socket
import sys
import time
import string
import copy

from collections import deque

from modules.sockets.li_socket import LISocket
from modules.sockets.li import acceptConnection
from modules.sockets.li_socket_manager import LISocketManager
from modules.sockets.li import acceptConnection
from modules.tools.config import parseClientTcpIfcConfig, ReadConfig
from subprocess import call
import subprocess as sub
import modules.tools.log as log

sm = 0;
name = "obsX"

def parseIRIIIFCollectorConfig(iniFileName):
    """ Parse configuration file and returns port numbers """
    cfg = ReadConfig(iniFileName)
    try:
        name = cfg.get("iri-iif probe", "name")
    except configError:
        log.critical("THe probe name is missing in the configuration file")
        raise
    return name;

def parseIRIIIFModuleConfig(iniFileName):
    """ Parse configuration file and returns port numbers """
    cfg = ReadConfig(iniFileName)
    try:
        modules = cfg.items("iri-iif modules")
    except configError:
        log.critical("Missing modules configuration for")
        raise
    rmodules = {m:e for m,e in modules}
    return rmodules

def parseIRIIIFInterfacesConfig(iniFileName):
    """ Parse configuration file and returns port numbers """
    cfg = ReadConfig(iniFileName)
    try:
        interfaces = cfg.items("iri-iif interfaces")
    except configError:
        log.critical("Missing interfaces configuration")
        raise
    ifc = [[i,(m.lower()).split(' ')] for i,m in interfaces]
    return ifc


def startModules(interfaces, modules):
    exe = "ifconfig | grep -v \"         \" | grep \"   \" | awk '{ print $1 }'"
    p = sub.check_output(exe,shell=True)
    p = p.decode("utf-8")
    allInterfaces = p.split('\n')
    for interface in allInterfaces:
        if interface != "":
            log.info("setting '" + interface + "' to promisc. mode")
            exe = "ifconfig " + interface + " promisc"
            call(exe, shell = True)

    for interface, active_modules in interfaces:
        for mod_name in active_modules:
            ex = "sleep 2 && "
            if interface == "any":
                ifc_param = " -i any"
            else:
                ifc_param = " -i " + interface
            ex += modules[mod_name] + ifc_param
            ex += "&"
            #print("EXEC:" + ex)
            call(ex,shell = True) #WARNING easy to inject!!!

def countModules(interfaces, modules):
    modulesCount = 0;
    for interface, active_modules in interfaces:
        for mod_name in active_modules:
            modulesCount += 1

    print("MODULES count: " + str(modulesCount))
    return modulesCount



def main(argv):
    log.setupLogging(argv[1:])
    # Read configuration
    iricore = parseClientTcpIfcConfig("iricore", "iri-collector.ini")

    interfaces = parseIRIIIFInterfacesConfig("iri-collector.ini")
    modules = parseIRIIIFModuleConfig("iri-collector.ini")
    
    global name
    name = parseIRIIIFCollectorConfig("iri-collector.ini")

    modulesNum = countModules(interfaces, modules)
    global sm
    sm = LISocketManager(
        (
        ),
        ("iricol",),
        (),
        (
            ("iricore",) + iricore,
        ),
        modulesNum
    )

    # Start modules
    startModules(interfaces, modules)
    # Run
    sm.tryInterfaces(argv[0])
    sm.mainLoop(globals())
    # Close sockets
    sm.closeSockets()

def brokenSocketIRICORE(s, sm):
    """ Handles situation when socket connection fails

    Returns if the IRI-IIF should continue
    """
    s.restartHeartbeat()
    return True

def processMessageIRICORE(msg, s, sm):
    """ Possibly negative ack or whatever """
    return True

def brokenSocketIRICOL(s, sm):
    """ Handles situation when socket connection fails

    Returns if the IRI-IIF should continue
    """
    # It is important to send heartbeats from this side of the socket even
    # though noone replies. If you do not send anything the socket is closed
    # and everything goes down
    s.restartHeartbeat()
    return True

def processRequestIRICOL(s, sm):
    """ Process network packets from iricol interface

    s Server socket
    sm socket manager
    """
    conn = acceptConnection(s)
    log.info("IRICOL: NEW CONNECTION!")
    sm.addPtPtSocket(LISocket(conn, "collector"), "r" + str(time.localtime()))

def processMessageCOLLECTOR(msg, sm, s):

    log.info("IRI-IIF forwarding: ", name," : ", msg)
    sm.send("iricore",(name, msg))

    return True

def brokenSocketCOLLECTOR(s, sm):
    """ Handles situation when socket connection fails

    Returns if the IRI-IIF should continue
    """
    # FIXME restart the module that just crashed
    sm.removeSocket(s)
    return True

if __name__ == "__main__":
    try:
        main(sys.argv)
    except Exception as e:
        log.unhandledException("IRI-IIF", e)

