#!/usr/bin/python3
"""
## © University of Southampton IT Innovation Centre, 2018
##
## Copyright in this software belongs to University of Southampton
## IT Innovation Centre of Gamma House, Enterprise Road,
## Chilworth Science Park, Southampton, SO16 7NS, UK.
##
## This software may not be used, sold, licensed, transferred, copied
## or reproduced in whole or in part in any manner or form or in or
## on any media by any person other than in accordance with the terms
## of the Licence Agreement supplied with the software, or otherwise
## without the prior written consent of the copyright owners.
##
## This software is distributed WITHOUT ANY WARRANTY, without even the
## implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
## PURPOSE, except where stated in the Licence Agreement supplied with
## the software.
##
##      Created By :            Michael Boniface
##      Created Date :          15-04-2018
##      Updated By :            Nikolay Stanchev
##      Updated Date :          16-04-2018
##      Created for Project :   FLAME
"""


from influxdb import InfluxDBClient
import clmctest.monitoring.LineProtocolGenerator as lp
import urllib.parse
import time
import random


class Simulator(object):
    """
    Simulator used to generate E2E measurements.
    """

    DATABASE = 'E2EMetrics'  # default database name
    DATABASE_URL = 'http://203.0.113.100:8086'  # default database url

    TICK = 1  # a simulation tick represents 1s
    SIMULATION_LENGTH = 120  # simulation time in seconds

    def __init__(self, database_url=DATABASE_URL, database=DATABASE):
        """
        Initialises the simulator by creating a db client object and resetting the database.

        :param database_url: db url
        :param database: db name
        """

        url_object = urllib.parse.urlparse(database_url)
        self.db_client = InfluxDBClient(host=url_object.hostname, port=url_object.port, database=database, timeout=10)

        self.db_url = database_url
        self.db_name = database

        self._reset_db()

    def _reset_db(self):
        """
        Reset the database using the already initialised db client object.
        """

        self.db_client.drop_database(self.db_name)
        self.db_client.create_database(self.db_name)

    def run(self):
        """
        Runs the simulation.
        """

        # all network delays start from 1ms, the dictionary stores the information to report
        ip_endpoints = [
            {'agent_id': 'endpoint1.ms-A.ict-flame.eu',
             'paths': [{
                 'target': 'endpoint2.ms-A.ict-flame.eu',
                 'path_id': 'endpoint1.ms-A.ict-flame.eu---endpoint2.ms-A.ict-flame.eu',
                 'network_delay': 1
             }]},
            {'agent_id': 'endpoint2.ms-A.ict-flame.eu',
             'paths': [{
                 'target': 'endpoint1.ms-A.ict-flame.eu',
                 'path_id': 'endpoint2.ms-A.ict-flame.eu---endpoint1.ms-A.ict-flame.eu',
                 'network_delay': 1
             }]}
        ]

        # current time in seconds (to test the aggregation we write influx data points related to future time), so we start from the current time
        start_time = int(time.time())

        sim_time = start_time

        mean_delay_seconds_media = 10  # initial mean media service delay
        sample_period_net = 2  # sample period for reporting network delays (measured in seconds) - net measurements reported every 2s
        sample_period_media = 5  # sample period for reporting media service delays (measured in seconds) - service measurements reported every 5 seconds

        for i in range(0, self.SIMULATION_LENGTH):
            # measure net delay every 2 seconds for endpoint 1 (generates on tick 0, 2, 4, 6, 8, 10.. etc.)
            if i % sample_period_net == 0:
                endpoint = ip_endpoints[0]
                paths = endpoint['paths']
                for path in paths:
                    self.db_client.write_points(lp.generate_network_delay_report(path['path_id'], endpoint['agent_id'], path['target'], path['network_delay'], sim_time))

                    # increase/decrease the delay in every sample report (min delay is 1)
                    path['network_delay'] = max(1, path['network_delay'] + random.randint(-3, 3))

            # measure net delay every 2 seconds for endpoint 2 (generates on tick 1, 3, 5, 7, 9, 11.. etc.)
            if (i+1) % sample_period_net == 0:
                endpoint = ip_endpoints[1]
                paths = endpoint['paths']
                for path in paths:
                    self.db_client.write_points(lp.generate_network_delay_report(path['path_id'], endpoint['agent_id'], path['target'], path['network_delay'], sim_time))

                    # increase/decrease the delay in every sample report (min delay is 1)
                    path['network_delay'] = max(1, path['network_delay'] + random.randint(-3, 3))

            # measure service response time every 5 seconds
            if i % sample_period_media == 0:
                self.db_client.write_points(lp.generate_service_delay_report(mean_delay_seconds_media, "ms-A.ict-flame.eu",
                                                                             "test-sf-clmc-agent-build_INSTANCE", "endpoint2.ms-A.ict-flame.eu",  sim_time))

                # increase/decrease the delay in every sample report (min delay is 10)
                mean_delay_seconds_media = max(10, mean_delay_seconds_media + random.choice([random.randint(10, 20), random.randint(-20, -10)]))

            # increase the time by one simulation tick
            sim_time += self.TICK

        end_time = sim_time
        print("Start time: {0}, End time: {1}".format(start_time, end_time))


if __name__ == "__main__":
    Simulator().run()