#  Copyright 2020 Modelling, Simulation and Design Lab (MSDL) at
#  McGill University and the University of Antwerp (http://msdl.cs.mcgill.ca/)
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

"""Makes up a profile of a simulation run.

This file contains the tracer that can analyze the performance and throughput of
the messages traveling through a model during simulation.
"""

from pypdevsbbl.tracers import BaseTracer

class ProfileTracer(BaseTracer):
    """Traces all the coming and going of messages and transitions.

    The output file is in CSV-format and contains two tables, separated with a
    newline. For all DEVS blocks that underwent transitions, a summary of the
    counts of the internal, external and confluent ones. For all connections, an
    overview will be presented of the amount of messages that passed over these
    connections. Finally, a total for all values will be given as well.

    Args:
        uid:                A unique identifier for distributed simulation.
                            Will be set automatically.
        server:             The server that runs the distributed simulation.
                            Will be set automatically.
        filename (str):     The name of the file to write the results to. When
                            ``None``, the console output will be used instead.
    """
    def __init__(self, uid, server, filename):
        super().__init__(uid, server, filename)
        self.connections = {}
        self.internal = {}
        self.external = {}
        self.confluent = {}

    def stopTracer(self):
        full = set(self.internal.keys()) | set(self.external.keys()) | set(self.confluent.keys())
        self.trace("Name,Internal,External,Confluent\n")
        for i in full:
            self.trace(i, self.internal.get(i, 0), self.external.get(i, 0), self.confluent.get(i, 0), sep=',', end='\n')
        self.trace("TOTAL", sum(self.internal.values()), sum(self.external.values()),
                   sum(self.confluent.values()), sep=',', end='\n')
        self.trace("\nOutport,Inport,Count\n")
        for pair in self.connections:
            self.trace(*pair, self.connections[pair], sep=',', end='\n')
        self.trace("TOTAL", "", sum(self.connections.values()), sep=',', end='\n')
        super().stopTracer()

    def traceInternal(self, aDEVS):
        name = aDEVS.getModelFullName()
        self.internal.setdefault(name, 0)
        self.internal[name] += 1

        output = aDEVS.my_output
        for port in output:
            for oport in port.outline:
                if oport.type() == 'INPORT':
                    name = (port.getPortFullName(), oport.getPortFullName())
                    self.connections.setdefault(name, 0)
                    self.connections[name] += 1

    def traceExternal(self, aDEVS):
        name = aDEVS.getModelFullName()
        self.external.setdefault(name, 0)
        self.external[name] += 1

    def traceConfluent(self, aDEVS):
        name = aDEVS.getModelFullName()
        self.confluent.setdefault(name, 0)
        self.confluent[name] += 1
