# This file is part of Radler.
#
# Radler 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.
#
# Radler 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 Radler.  If not, see <http://www.gnu.org/licenses/>.

'''
@license: GPLv3

Created on June, 2015

@author: Léonard Gérard leonard.gerard@sri.com


'''
import yaml

from radler.astutils.nodetrees import fun_dict_of
from radler.astutils.tools import write_file
from radler.radlr import infos
from radler.radlr.errors import warning
from radler.radlr.rast import AstVisitor, follow_links
from radler.radlr.ros.rosutils import qn_topic, node_package


## Machine template running every node of a machine (as local)
# source_file : is the user source file used to generate the plant
# machine_qname : the machine qualname
machine_templates = {
'machine_launchfile':
"""
<!-- Launch file generated by radler from the source file {source_file}
     running locally every node of the machine {machine_qname}
  -->
<launch>
{node_launcher}
</launch>

"""
}

## Node templates to be applied for every node we want to run
# node_name :
# node_package :
# node_executable :

node_templates_machine_sublevel = {
'node_launcher':
'''
  <node ns="{node_package}" name="{node_name}" pkg="{node_package}" type="{node_executable}" output="screen">{sub_latency}
  </node>{pub_maxperiod}
'''
}

sub_templates_node_sublevel = {
'sub_latency':
'''
    <param name="{topic_name}/maxlatency" value="{maxlatency}" type="double"/>'''
}

pub_templates_node_sublevel = {
'pub_maxperiod':
'''
  <param name="{topic_name}/maxperiod" value="{maxperiod}" type="double"/>'''
}



def app(d, templates):
    for (s,t) in templates.items():
        v = t.format(**d)
        if s not in d or not d[s]:
            d[s] = v
        else:
            d[s] += v

def clear(d, templates):
    for s in templates:
        d[s] = ''


def machine(visitor, m, d):
    clear(d, node_templates_machine_sublevel)
    clear(d, machine_templates)
    d['machine_qname'] = str(m._qname)

    visitor.node_bf(m, d)

    app(d, machine_templates)
    filename = 'launch/' + d['machine_qname'] + '.launch'
    d['launch_files'].append(filename)
    file = d['package_path'] / filename
    write_file(file, d['machine_launchfile'])

def publication(p, d):
    d['topic_name'] = qn_topic(p['TOPIC']._qname)
    pub_each = float(p['EACH']._val) if p['EACH'] else 1
    d['maxperiod'] = (d['node_period'] * pub_each + d['node_wcet']) / 1000000000.0 #double in seconds
    app(d, pub_templates_node_sublevel)

def subscription(s, d):
    d['topic_name'] = qn_topic(s['TOPIC']._qname)
    d['maxlatency'] = float(s['MAXLATENCY']._val) / 1000000000.0 #double in seconds
    app(d, sub_templates_node_sublevel)


def node(visitor, n, d):
    name = str(n._name)
    d['node_name'] = name
    d['node_executable'] = name #TODO 8: ros backend has many naming issues caused by ROS preventing names to have '.' in them and the fact that we do not want to mangle... The executable name is set by cmake to be the node name...
    d['node_period'] = int(n['PERIOD']._val)
    d['node_wcet'] = int(n['WCET']._val) if n['WCET'] else d['node_period']

    d['node_package'] = node_package(n._qname)
    clear(d, sub_templates_node_sublevel)
    for s in n['SUBSCRIBES']:
        subscription(s, d)

    clear(d, pub_templates_node_sublevel)
    for p in n['PUBLISHES']:
        publication(p, d)

    app(d, node_templates_machine_sublevel)



def gen_launchfiles(plantinfo, package_name, package_folder):
    """ This pass is in charge of generating the launch files specific to a plant.
    The corresponding package is supposed to be already generated.
    @return: the list of generated file handles
    """

    ## Do the actual plant pass to generate the needed launch files.

    d = dict()
    d['source_file'] = str(infos.source_file)
    v = AstVisitor(fun_dict_of((machine, node)),
                   onleaf=follow_links(AstVisitor.leaf_bf), #@UndefinedVariable
                   kind='bf')
    d['package_path'] = package_folder
    d['launch_files'] = []
    v.visit(plantinfo.plant, d)

    return d['launch_files']



def gen_srosfile(plantinfo, package_name, package_folder):
    """ Generate SROS config file from a plant description.
    @return: the config file handle
    """
    ## we create the correct structure for the sros yaml configuration file and dump it
    cfg = dict()
    topics = list()
    cfg['topics'] = topics

    warned_systems = set()

    def getIP(system):
        try:
            return system['IP']._val
        except AttributeError:
            if not (system._qname in warned_systems):
                warning("No IP given, so SROS generation assumes IP 127.0.0.1 for system " + str(system._qname), system._location)
                warned_systems.add(system._qname)
            return '127.0.0.1'

    for t in plantinfo.topics:

        publisherIP = getIP(plantinfo[plantinfo[t]['publisher_node']]['system'])
        subscriberIPs = [getIP(plantinfo[s]['system']) for s in plantinfo[t]['subscriber_nodes']]

        topic_info = {
            'name' : qn_topic(t._qname),
            'publishers': [publisherIP],
            'subscribers': subscriberIPs
        }
        topics.append(topic_info)

    sros_cfg_file = 'sros_cfg.yaml'
    write_file(package_folder / sros_cfg_file, yaml.dump(cfg))

    return sros_cfg_file


