
########################################################################################################################
# Copyright (c) 2015, University of Osnabrueck                                                                         #
#   All rights reserved.                                                                                               #
#                                                                                                                      #
#   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the   #
#   following conditions are met:                                                                                      #
#                                                                                                                      #
#   1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following#
#       disclaimer.                                                                                                    #
#                                                                                                                      #
#   2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the       #
#       following disclaimer in the documentation and/or other materials provided with the distribution.               #
#                                                                                                                      #
#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, #
#   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE  #
#   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, #
#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR    #
#   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  #
#   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE   #
#   USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                                           #
########################################################################################################################
import sys
import os

sys.path.append('src/util')
from src.prepare import calculateTerrain
from src.prepare import parseToBinary
from src.prepare import parseOSM
from src.prepare import parseGML
from src.prepare import removeOffset
from src.prepare import extractEdges
from src.launcher import wrapLauncher
from src.launcher import prepare4ns3
from src.launcher import makeViewable
import networking
import marshal
import build_native
import time
import datetime
import collections
import zipfile
import src.globals as gl
import numpy as np
import os


#you can change the forder structure here
fInputfiles  = 'inputfiles/'
fOutputfiles = 'outputfiles/'
fConfigfiles = 'configfiles/'
fViewerConfig= 'viewerconfig/'
fPolygons    = 'polygons/'
fEdges       = 'edges/'
fRays        = 'rays/'
fLogs        = 'logs/'
fCoverage    = 'coverage/'
fTmp         = 'tmp/'

LOG1 = 1
LOG2 = 2
LOG3 = 3
LOGFULL = 4
DEV = 5

USAGE = "Usage: python run.py -g | mapfile [configfile] [transmitterfile] [receiverfile] [param=...] [name=...]"

def saveConfigFile(config, filename, configfolder=fConfigfiles):
    """ Stores dictionary config as file
        Keys are sorted in alphabetical order
    :param config: Dictionary with config parameters
    :param filename: Filename
    """
    od = collections.OrderedDict(sorted(config.items()))
    f = open(configfolder+filename, "w")
    for k, v in od.items():
        if isinstance(v, str):
            f.write(k + " = '" + str(v) + "'\n")
        else:
            f.write(k + " = " + str(v) + "\n")
    f.close()


def parseConfigFile(filename):
    """
    Parses a configfile as dictionary
    :param filename: Filename
    :param configfolder: Folder, which contains the configfile
    :return: Dictionary containing all key-values pares of the file
    """
    f = open(filename, "r")
    config = {}
    for line in f:
        s = line.split("#")[0].split(" = ")
        if len(s) > 1:
            try:
                config[s[0].strip()] = eval(s[1])
            except:
                config[s[0].strip()] = s[1].strip()
    f.close()
    return config


def parseTransmitterFile(filename):
    """
    Parses a file with transmitter or receiver positions
    :param filename: Filename, with folders
    :return: List of positions
    """
    f = open(filename)
    transmitter = []
    for line in f:
        t = line.split(" ")
        for tr in t:
            try:
                transmitter.append(eval(tr))
            except:
                pass
    f.close()
    return transmitter


def saveTransmitterFile(transmitter, filename):
    """
    Saves a list of positions as file
    :param transmitter: List of positions
    :param filename: Full filename with folders
    """
    f = open(filename, "w")

    i = 0
    for t in transmitter:
        f.write(str(t) + " ")
        i += 1
        if i%6 == 0:
            f.write("\n")

    f.close()


def parsePositionsFile(filename):
    """
    Parses a input file with positions
    :param filename: Full filename with folder
    :return:
        type: 1 or 4,
        positions: A list with positions
    """
    validTypes = [1,4]

    f = open(filename)
    file = f.read().split("\n")
    type = int(file[0][0])

    if type not in validTypes:
        print "Invalid type for positions. Line - 1 | List - 4 \n Exiting..."
        sys.exit()

    positions = []
    if type == 4:
        for line in file[1:]:
            t = line.split("#")[0].split(" ")
            for tr in t:
                try:
                    positions.append(eval(tr))
                except:
                    pass

    if type == 1:
        for line in file[1:]:
            t = line.split("#")[0].split(" ")
            try:
                l = (eval(t[0]), eval(t[1]), float(t[2]))
                positions.append(l)
            except:
                pass

    f.close()
    return type, positions


def savePositions4Launcher(points, filename):
    """
    Saves positions in a way the
    :param points:
    :param filename:
    :return:
    """
    f = open(filename, "w")

    for t in points:
        if isinstance(t, tuple):
            start, end, step = t
            for v in start, end:
                for p in v:
                    f.write(str(p) + " ")
            f.write(str(step) + " ")
        else:
            for p in t:
                f.write(str(p) + " ")
        f.write("\n")

    f.close()

def line2List(lines):
    positions = []

    if isinstance(lines, tuple):
        lines = [lines]

    for l in lines:

        (start, end, step) = l
        vec = np.array(end) - np.array(start)
        leng = np.linalg.norm(vec)
        vec = vec/leng
        vec *= step
        current = np.array(start)

        while np.linalg.norm(np.array(current) - np.array(start)) <= leng:
            positions.append(current.tolist())
            current = np.array(current) + np.array(vec)

    return positions

def mergeDict(dictFrom, dictTo):
    for k, v in dictFrom.items():
        if k not in dictTo.keys():
            dictTo[k] = v
    return dictTo

def saveAsZip(identifier, debuglvl):

    zipfname = fOutputfiles + identifier + '.zip'
    folder = identifier + '/'

    path = 'tmp/' + folder
    zipf = zipfile.ZipFile(zipfname, 'w', zipfile.ZIP_DEFLATED)

    if debuglvl >= LOGFULL:
        for root, dirs, files in os.walk(path):
            for file in files:
                zipf.write(os.path.join(root, file))

    elif debuglvl >= LOG3:
        for root, dirs, files in os.walk(path):
            for file in files:
                if file.split('.')[-1] == 'bin':
                    zipf.write(os.path.join(root, file))
        zipf.write(path + fPolygons + 'buildings.txt')

    elif debuglvl >= LOG2:
        zipf.write(path + fPolygons + 'buildings.txt')

    if debuglvl >= LOG3:
        zipf.write(fViewerConfig + identifier + '.vcfg')

    zipf.write(fOutputfiles + identifier + '.txt')
    zipf.write(fConfigfiles + identifier + '.cfg')
    rec = fConfigfiles + identifier + "_receiver.rec"
    tr = fConfigfiles + identifier + "_transmitter.tr"
    if os.path.isfile(rec):
        zipf.write(rec)
    if os.path.isfile(tr):
        zipf.write(tr)
    zipf.close()

def saveViewerConfig(identifier, config):
    f = open(fViewerConfig + identifier + '.vcfg', 'w')

    folder = identifier + '/'
    f.write(fTmp + folder + fPolygons + 'buildings.bin\t(0.8,0.8,0.8,0.1)\t0\tGL_TRIANGLES\n')
    f.write(fTmp + folder + fPolygons + 'terrain.bin\t(0.0,4.0,0.1)\t0\tGL_TRIANGLES\n')
    f.write(fTmp + folder + fEdges + 'buildings.bin\t(1.0,1.0,1.0)\t0\tGL_LINES\n')
    #f.write(fTmp + folder + fEdges + 'terrain.bin\t(1.0,1.0,1.0)\t0\tGL_LINES\n')
    if config["debugRays"] is True:
        for transmitter in config["transmitters"]:
            x,y,z = transmitter
            transmitter = "[{:g}, {:g}, {:g}]".format(x,y,z)
            f.write(fTmp + folder + fRays + str(transmitter) + '.bin\t(1.0,0.0,0.0)\t1\tGL_LINES\n')
        f.close()

def saveInConfigfolder(file, identifier, fconfig = fConfigfiles):
    filename = file.split("/")[-1]
    filetype = filename.split(".")[-1]
    filename = "receiver." if filetype == "rec" else "transmitter."
    gl.copy(file, fconfig + identifier + "_" + filename + filetype)

def frange(x, y, jump):
    while x < y:
        yield x
        x += jump

def evaluateConfig(config):
    if not isinstance(config["borders"], list) or len(config["borders"]) not in [0,4]:
        raise "Borders should be a list of 4 integers or empty"
    if not isinstance(config["buildingHeight"], int) or config["buildingHeight"] <= 0:
        raise "BuildingHeight should be an integer and >= 0"
    if not isinstance(config["calculationServers"], tuple):
        raise "CalculationServers should be a tuple"
    if config["center"] is not None and (not isinstance(config["center"], list) or len(config["center"]) is not 3):
        raise "Center should be a list of 3 integers or None"
    if not isintorfloat(config["coverageLevel"]) or config["coverageLevel"] < 0:
        raise "CoverageLevel should be a number (int or float) and >= 0"
    if not isintorfloat(config["coverageMaxLevel"]) or config["coverageLevel"] > config["coverageMaxLevel"]:
        raise "CoverageMaxLevel should be a number (int or float) and >= coverageLevel"
    if not isintorfloat(config["deadDistance"]) or config["deadDistance"] < 0:
        raise "DeadDistance should be a number (int or float) and >= 0"
    if not isinstance(config["debugLevel"], int) or config["debugLevel"] not in [LOG1, LOG2, LOG3, LOGFULL, DEV]:
        raise "DebugLevel should be an integer between " + str(LOG1) + " and " + str(DEV)
    if not isinstance(config["debugRays"], bool):
        raise "DebugRays should be True or False"
    if not isintorfloat(config["diffractionThreshold"]) or config["diffractionThreshold"] < 0:
        raise "Diffractionthreshold should be a number (int or float) and >= 0"
    if not isinstance(config["groundHeight"], int) or config["groundHeight"] < 0:
        raise "GroundHeight should be an integer and >= 0"
    if not isinstance(config["interference"], bool):
        raise "Interference should be True or False"
    if not isinstance(config["maxIterations"], int) or config["maxIterations"] <= 0:
        raise "MaxIterations should be an integer and > 0"
    if not isinstance(config["name"], str):
        raise "Name should be a string"
    if not isinstance(config["maxRange"], int) or config["maxRange"] < 0:
        raise "MaxRange should be an integer and >= 0"
    if not isinstance(config["numberWorkers"], int) or config["numberWorkers"] <= 0:
        raise "NumberWorkers should be an integer and > 0"
    if not isinstance(config["ppServers"], tuple):
        raise "Ppservers should be a tuple"
    if not isinstance(config["rayNumber"], int) or config["rayNumber"] <= 0:
        raise "RayNumber should be an integer and > 0"
    if config["receivers"] not in ["auto","full"] and not isinstance(config["receivers"], list) and not os.path.isfile(config["receivers"]):
        raise "Receivers should be either 'auto' or 'full' or a list of lists"
    if isinstance(config["receivers"], list) and not isinstance(config["receivers"][0], list):
        raise BaseException("Your receivers-list should have the following format: [[x,y,z]] or [[x0,y0,z0],...,[xn,yn,zn]]")
    if not isinstance(config["receiverType"], int):
        raise "ReceiverType should be an integer. DO NOT change this value manually!"
    if not isintorfloat(config["receiveThreshold"]) or config["receiveThreshold"] <= 0:
        raise "ReceiveThreshold should be a number (int or float) and > 0"
    if not isintorfloat(config["reflectionPart"]) or config["reflectionPart"] < 0:
        raise "ReflectionPart should be a number (int or float) and >= 0"
    if not isintorfloat(config["scatteringPart"]) or config["scatteringPart"] < 0:
        raise "ScatteringPart should be a number (int or float) and >= 0"
    if not isintorfloat(config["stepSize"]) or config["stepSize"] <= 0:
        raise "StepSize should be a number (int or float) and > 0"
    if not isinstance(config["terrainHeight"], int) or config["terrainHeight"] < 0:
        raise "TerrainHeight should be an integer and >= 0"
    if config["terrainLevel"] is not "auto" and not isintorfloat(config["terrainLevel"]):
        raise "TerrainLevel should be 'auto' or a number (int or float)"
    if not isinstance(config["terrainWidth"], int) or config["terrainWidth"] < 0:
        raise "TerrainWidth should be an integer and >= 0"
    if not isinstance(config["timeout"], int) or config["timeout"] <= 0:
        raise "Timeout should be an integer and > 0"
    if config["transmitters"] not in ["area","cubic"] and not isinstance(config["transmitters"], list) and not os.path.isfile(config["transmitters"]):
        raise "Transmitters should be either 'area' or 'cubic' or a list of lists"
    if isinstance(config["transmitters"], list) and not isinstance(config["transmitters"][0], list):
        raise "Your transmitters-list should have the following format: [[x,y,z]] or [[x0,y0,z0],...,[xn,yn,zn]]"
    if not isinstance(config["transmitterType"], int):
        raise "TransmitterType should be an integer. DO NOT change this value manually!"
    if not isintorfloat(config["wavelength"]) or config["wavelength"] <= 0:
        raise "Wavelength should be a number (int or float) and > 0"



def isintorfloat(param):
    return isinstance(param, int) or isinstance(param, float)




def main():

    ts = time.time()
    identifier = None
    build_native.build()

    config = {
        "borders":              [],     #[-51, -27, 53, 21] usually the borders are determined from data, you can limit the coverage area manually here
        "buildingHeight":       10,     #used for osm maps, sets the height of each building
        "calculationServers":   (),     #you can distribute calculations to other computers, which are running calculationServer.py (only used if u have more transmitters than 'numberWorkers'), "('ip1','ip2',...)"
        "center":               None,     #[433663.812, 5793036.36, 0]usually the center is determined from data and used as origin, you can choose a center manually here
        "coverageLevel":        1,      #height at which 2d coverage calculation will be performed
        "coverageMaxLevel":     1,      #3d calculation will be performed between coverageLevel and coverageMaxLevel
        "deadDistance":         0.5,    #minimal distance between events
        "debugLevel":           LOG3,   #1: just config and outputfile
                                        #2: +files for 2D-Viewer
                                        #3: +files for 3D-Viewer
                                        #4: complete tmp-folder
                                        #5: +debug-messages
        "debugRays":            True,   #set True to draw rays in 3d-Viewer
        "diffractionThreshold": 0.125*7,     #influence range of edges
        "groundHeight":         0,      #used for osm maps, sets height of ground
        "interference":         False,  #enable/disable multi-path effects
        "maxIterations":        50,     #force stop when a ray is reflected very often
        "name":                 "auto", #sets name of outputfile: <mapname>_name. Set value to auto if u just want a timestamp (%Y%m%d-%H%M%S) after mapname
        "maxRange":             0,      #don't calculate signal strength if distance between receiver and transmitter is to far, will speed up calculation for larger maps
        "numberWorkers":        4,      #number of workers, only coverage maps are calculated in parallel, should be the number of cpu-cores
        "ppServers":            (),     #specify tuple of ppServers for cluster computing (doesn't work, although it should -> incompatible versions? network configuration? problem finding raylauncher? unfortunately no useful error message)
                                        #use param calculationServers if you want parallel caluclation (replaces ppServers with a self-built solution),
        "preprop" :             True,   #set this to false if you already done the preparation for a map, make sure parameter 'name' is the same
        "rayNumber":            5000,   #number of rays launched at sources
        "receivers":            "auto", #set specific receiver here, if you want to calculate just some links [[0,0,1],[1,1,1],...]
        "receiverType":         2,      #default value 2, set by script, dont change!
        "receiveThreshold":     .5,     #size of "antenna", should be half of stepSize
        "reflectionPart":       0.153,  #sets what part of the incoming energy is reflected
        "scatteringPart":       0.0181, #sets what part of the incoming energy is scattered
        "stepSize":             1,      #discretization of the coverage maps
        "terrainHeight":        2,      #resolution of terrain  #ray-caster
        "terrainLevel":         "auto", #make a flat terrain instead of analyzing building heights
        "terrainWidth":         2,      #resolution of terrain
        "timeout":              6000,   #timeout in seconds of a single job: one job is one receiver <-> transmitter link,
        "transmitters":         [[0, 0, 1]],  #coverage maps will be calculated for each position, use 'area' or 'cubic' full coverage
        "transmitterType":      0,      #default value 0, set by script, dont change!
        "wavelength":           3e8 / 2.4e9
    }

    # evaluate commandline
    params = len(sys.argv)

    if params < 2:
        print "Invalid amount of arguments!", USAGE
        sys.exit()

    if sys.argv[1] == "-g":
        saveConfigFile(config, "sampleconfig.cfg")
        print "Created a sample configfile \"sampleconfig.cfg\" in the configfiles-directory."
        sys.exit()

    if not os.path.isfile(sys.argv[1]) or sys.argv[1].split(".")[-1] not in ["gml","osm","raw"]:
        print "Invalid Inputfile!", USAGE
        sys.exit()

    setName = False
    #Parse params
    for j in range(2, len(sys.argv), 1):
        #reading inputfiles
        if os.path.isfile(sys.argv[j]) or os.path.isfile(fConfigfiles+sys.argv[j]):
            file = sys.argv[j]
            filetype = file.split(".")[-1]
            if filetype == "tr":
                config["transmitters"] = file
            elif filetype == "rec":
                config["receivers"] = file
            elif filetype == "cfg":
                config2 = parseConfigFile(file)
                config = mergeDict(config, config2)
            else:
                print "Skipping invalid filetype:", filetype

        #reading params
        else:
            key = sys.argv[j].split("=")[0]
            value = sys.argv[j].split("=")[1]
            print "Processing key:", key
            if key not in config.keys():
                print "Skipping invalid parameter:", key
            else:
                try:
                    config[key] = eval(value)
                except:
                    config[key] = value.strip()

    evaluateConfig(config)

    inputMap = sys.argv[1]
    filename = inputMap.split(".")[:-1]    #cardfilnename
    mapname = inputMap.split("/")[-1][:-4]
    inputtype = inputMap.split(".")[-1]   #cardtype gml/osm

    if not config["name"] == "auto":
        identifier = mapname + "_" + config["name"]
    else:
        td = datetime.datetime.fromtimestamp(ts).strftime('%Y%m%d-%H%M%S')
        identifier = mapname + "_" + td

    inputfile = str(config["transmitters"])
    if os.path.isfile(inputfile):
        saveInConfigfolder(inputfile, identifier)
        tp, tr = parsePositionsFile(inputfile)
        if tp == 1:
            tr = line2List(tr)
        config["transmitters"] = tr
        config["transmitterType"] = tp
    inputfile = str(config["receivers"])
    if os.path.isfile(inputfile):
        saveInConfigfolder(inputfile, identifier)
        tp, rec = parsePositionsFile(inputfile)
        config["receivers"] = rec
        config["receiverType"] = tp


    # specify transmitterType
    if config["transmitters"] == "area":
        config["transmitterType"] = 2
    elif config["transmitters"] == "cubic":
        config["transmitterType"] = 3
    else:
        if len(config["transmitters"]) > 1 and not isinstance(config["transmitters"][0], tuple):
            config["transmitterType"] = 4

    # specify receiverType
    if config["receivers"] == "full" or config["receivers"] == "auto":
        if len(config["receivers"]) == 1:
            config["receiverType"] = 0
        if config["coverageLevel"] < config["coverageMaxLevel"]:
            config["receiverType"] = 3
    else:
        if len(config["receivers"]) > 1 and not isinstance(config["receivers"][0], tuple):
            config["receiverType"] = 4


    configfilename = identifier + ".cfg"
    saveConfigFile(config, configfilename)

    preprop = config["preprop"]

    gl.mkdir(fTmp)
    workfolder = fTmp + identifier + '/'
    if preprop:
        gl.rmdir(workfolder)

    gl.mkdir(workfolder)
    gl.mkdir(workfolder + fPolygons)
    gl.mkdir(workfolder + fEdges)
    gl.mkdir(workfolder + fRays)
    gl.mkdir(workfolder + fLogs)
    gl.mkdir(fInputfiles)
    gl.mkdir(fConfigfiles)
    gl.mkdir(fOutputfiles)

    gl.initLogger(workfolder + fLogs, identifier, config["debugLevel"])

    gl.logger.info("parsing mapfile: " + inputMap)

    receiverfile = workfolder + "receiverlist.cfg"
    if config["receiverType"] in [1,4]:
        savePositions4Launcher(config["receivers"], receiverfile)

    #parse file
    rawpolyfilename = workfolder + fPolygons + "map_raw.txt"
    if preprop:
        if inputtype == "gml":
            gmlfilename = inputMap
            parseGML.parse(gmlfilename, rawpolyfilename)
        elif inputtype == "osm":
            osmfilename = inputMap
            parseOSM.parse(osmfilename, rawpolyfilename, config)
        elif inputtype == "raw":
            gl.copy(inputMap, rawpolyfilename)
        else:
            raise Exception("invalid filetype: " + inputtype)


    #remove offsetA
    gl.logger.info("removing offset")
    buildingspolyfilename = workfolder + fPolygons + "buildings.txt"
    if preprop:
        removeOffset.removeOffset(rawpolyfilename, buildingspolyfilename, config["center"])
    buildingspolybinfilename = workfolder + fPolygons + "buildings.bin"
    if preprop:
        parseToBinary.parse(buildingspolyfilename, buildingspolybinfilename)

    #calculate terrain
    gl.logger.info("calculating terrain")
    terrainpolyfilename = workfolder + fPolygons + "terrain.txt"
    if preprop:
        bordersfloat = calculateTerrain.calculate(buildingspolyfilename, terrainpolyfilename, config)
        borders = []
        for b in bordersfloat:
            borders.append(int(b))
        #if "borders" not in config.keys():
        if not config["borders"]:
            config["borders"] = borders
            saveConfigFile(config, configfilename)
        else:
            borders = config["borders"]
    else:
        borders = config["borders"]

    terrainpolybinfilename = workfolder + fPolygons + "terrain.bin"
    if preprop:
        parseToBinary.parse(terrainpolyfilename, terrainpolybinfilename)

    #c++ launcher does extract edges now, much faster, wow


    buildingsedgesfilename = workfolder + fEdges + "buildings.txt"
    buildingsedgesbinfilename = workfolder + fEdges + "buildings.bin"
    terrainedgesfilename = workfolder + fEdges + "terrain.txt"
    terrainedgesbinfilename = workfolder + fEdges + "terrain.bin"

    #extract edges | Done from c++ launcher, much faster
    #gl.logger.info("extracting edges")
    #if preprop:
    #    extractEdges.extract(buildingspolyfilename, buildingsedgesfilename, buildingsedgesbinfilename)

    #Terrainedges not supported in v1
    if preprop:
        extractEdges.extract(terrainpolyfilename, terrainedgesfilename)


    #raycaster
    gl.logger.info("starting simulation")
    rayfolder = workfolder + fRays
    coveragefolder = workfolder + fCoverage
    gl.rmdir(coveragefolder)
    gl.mkdir(coveragefolder)


    #generate transmitter list if coverage required
    if config["transmitters"] == "area":
        config["transmitters"] = []
        for y in frange(borders[1], borders[3], config["stepSize"]):
            for x in frange(borders[0], borders[2], config["stepSize"]):
                config["transmitters"].append([x, y, config["coverageLevel"]])
    elif config["transmitters"] == "cubic":
        config["transmitters"] = []
        for z in frange(config["coverageLevel"], config["coverageMaxLevel"], config["stepSize"]):
            for y in frange(borders[1], borders[3], config["stepSize"]):
                for x in frange(borders[0], borders[2], config["stepSize"]):
                    config["transmitters"].append([x, y, z])



    #do ray casting for each transmitter (over network)
    nparts = len(config["calculationServers"]) + 1
    transmittermap = []

    for t in xrange(nparts):
        transmittermap.append([])

    transmitters = config["transmitters"]
    if len(config["transmitters"]) > config["numberWorkers"] and len(config["calculationServers"]) > 0:
        print "Do ray casting over network..."

        for i, t in enumerate(config["transmitters"]):
            transmittermap[i % nparts].append(t)

        transmitters = transmittermap[0]

        for i, s in enumerate(config["calculationServers"]):
            t = transmittermap[i + 1]
            gl.logger.info("Init connection to "+s)
            sock = networking.startLinkClient(s, False)

            gl.logger.info("send data")
            for fn in [buildingspolyfilename, terrainpolyfilename]:
                f = open(fn, "rb")
                data = f.read()
                f.close()
                networking.send(data, sock)
            networking.send(marshal.dumps(t), sock)
            networking.send(marshal.dumps(config), sock)

            if config["receiverType"] == 4:
                f = open(receiverfile, "rb")
                data = f.read()
                f.close()
                networking.send(data, sock)

        sock.close()

    #do own part of calculation
    wrapLauncher.calculateTransmitterParallel(coveragefolder, buildingspolyfilename, terrainpolyfilename,
                                              buildingsedgesfilename, terrainedgesfilename, rayfolder, transmitters,
                                              config, receiverfile)


    #receive generated data
    if len(config["transmitters"]) > config["numberWorkers"] and len(config["calculationServers"]) > 0:
        print "Receive Data..."
        for i, s in enumerate(config["calculationServers"]):
            sock = networking.startLinkClient(s, True)
            net_identifier = networking.receive(sock)
            for t in transmittermap[i + 1]:
                x,y,z = t
                t = "[{:g}, {:g}, {:g}]".format(x,y,z)
                f = open(coveragefolder + str(t) + '.txt', 'w')
                data = networking.receive(sock)
                f.write(data)
                f.close()
                if config["debugRays"] is True:
                    f = open(rayfolder + str(t) + '.txt', 'w')
                    data = networking.receive(sock)
                    f.write(data)
                    f.close()
            for string in ['.log', "_launcher.log"]:
                f = open(workfolder + fLogs + net_identifier + string, 'w')
                data = networking.receive(sock)
                f.write(data)
                f.close()
            #f = open(workfolder + fLogs + net_identifier + "_launcher.log", 'w')
            #data = networking.receive(sock)
            #f.write(data)
            #f.close()
            #sock.close()
            print "Data retreived for", net_identifier


    #prepare for ns-3
    print "Preparing data for ns-3..."
    ns3filename = fOutputfiles + identifier + ".txt"
    prepare4ns3.prepare4ns3(coveragefolder, ns3filename, borders, config)
    if (config["debugLevel"]) >= 3:
        if preprop:
            extractEdges.toBin(buildingsedgesfilename, buildingsedgesbinfilename)
            extractEdges.toBin(terrainedgesfilename, terrainedgesbinfilename)

    if config["debugRays"] is True:
        for transmitter in config["transmitters"]:
            x,y,z = transmitter
            string = "[{:g}, {:g}, {:g}].txt".format(x,y,z)
            rayfilename = rayfolder + string
            raybinfilename = rayfilename[:-3] + "bin"
            makeViewable.makeViewable(rayfilename, raybinfilename)
            gl.logger.debug("Make file: " + str(rayfilename) + "viewable")

    #generate zip with results
    gl.logger.info("generating viewerconfig")
    saveViewerConfig(identifier, config)

    gl.logger.info("generating zipfile")
    print "Generating zipfile"
    saveAsZip(identifier, config['debugLevel'])

    gl.logger.info("total time: " + str(time.time() - ts))

    print "Finished."


if __name__ == "__main__":

    main()
