# -*- coding: utf-8 -*-
"""
    sphinx.ext.doctest
    ~~~~~~~~~~~~~~~~~~

    Mimic doctest by automatically executing code snippets and checking
    their results.

    :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
    :license: BSD, see LICENSE for details.
"""

import re
import sys
import time
import codecs
import StringIO
from os import path
# circumvent relative import
doctest = __import__('doctest')

from docutils import nodes
from docutils.parsers.rst import directives

from sphinx.builders import Builder
from sphinx.util import force_decode
from sphinx.util.nodes import set_source_info
from sphinx.util.compat import Directive
from sphinx.util.console import bold
from sphinx.util.pycompat import bytes

blankline_re = re.compile(r'^\s*<BLANKLINE>', re.MULTILINE)
doctestopt_re = re.compile(r'#\s*doctest:.+$', re.MULTILINE)

# set up the necessary directives

class TestDirective(Directive):
    """
    Base class for doctest-related directives.
    """

    has_content = True
    required_arguments = 0
    optional_arguments = 1
    final_argument_whitespace = True

    def run(self):
        # use ordinary docutils nodes for test code: they get special attributes
        # so that our builder recognizes them, and the other builders are happy.
        code = '\n'.join(self.content)
        test = None
        if self.name == 'doctest':
            if '<BLANKLINE>' in code:
                # convert <BLANKLINE>s to ordinary blank lines for presentation
                test = code
                code = blankline_re.sub('', code)
            if doctestopt_re.search(code):
                if not test:
                    test = code
                code = doctestopt_re.sub('', code)
        nodetype = nodes.literal_block
        if self.name in ('testsetup', 'testcleanup') or 'hide' in self.options:
            nodetype = nodes.comment
        if self.arguments:
            groups = [x.strip() for x in self.arguments[0].split(',')]
        else:
            groups = ['default']
        node = nodetype(code, code, testnodetype=self.name, groups=groups)
        set_source_info(self, node)
        if test is not None:
            # only save if it differs from code
            node['test'] = test
        if self.name == 'testoutput':
            # don't try to highlight output
            node['language'] = 'none'
        node['options'] = {}
        if self.name in ('doctest', 'testoutput') and 'options' in self.options:
            # parse doctest-like output comparison flags
            option_strings = self.options['options'].replace(',', ' ').split()
            for option in option_strings:
                if (option[0] not in '+-' or option[1:] not in
                    doctest.OPTIONFLAGS_BY_NAME):
                    # XXX warn?
                    continue
                flag = doctest.OPTIONFLAGS_BY_NAME[option[1:]]
                node['options'][flag] = (option[0] == '+')
        return [node]

class TestsetupDirective(TestDirective):
    option_spec = {}

class TestcleanupDirective(TestDirective):
    option_spec = {}

class DoctestDirective(TestDirective):
    option_spec = {
        'hide': directives.flag,
        'options': directives.unchanged,
    }

class TestcodeDirective(TestDirective):
    option_spec = {
        'hide': directives.flag,
    }

class TestoutputDirective(TestDirective):
    option_spec = {
        'hide': directives.flag,
        'options': directives.unchanged,
    }

parser = doctest.DocTestParser()

# helper classes

class TestGroup(object):
    def __init__(self, name):
        self.name = name
        self.setup = []
        self.tests = []
        self.cleanup = []

    def add_code(self, code, prepend=False):
        if code.type == 'testsetup':
            if prepend:
                self.setup.insert(0, code)
            else:
                self.setup.append(code)
        elif code.type == 'testcleanup':
            self.cleanup.append(code)
        elif code.type == 'doctest':
            self.tests.append([code])
        elif code.type == 'testcode':
            self.tests.append([code, None])
        elif code.type == 'testoutput':
            if self.tests and len(self.tests[-1]) == 2:
                self.tests[-1][1] = code
        else:
            raise RuntimeError('invalid TestCode type')

    def __repr__(self):
        return 'TestGroup(name=%r, setup=%r, cleanup=%r, tests=%r)' % (
            self.name, self.setup, self.cleanup, self.tests)

class TestCode(object):
    def __init__(self, code, type, lineno, options=None):
        self.code = code
        self.type = type
        self.lineno = lineno
        self.options = options or {}

    def __repr__(self):
        return 'TestCode(%r, %r, %r, options=%r)' % (
            self.code, self.type, self.lineno, self.options)

class SphinxDocTestRunner(doctest.DocTestRunner):
    def summarize(self, out, verbose=None):
        string_io = StringIO.StringIO()
        old_stdout = sys.stdout
        sys.stdout = string_io
        try:
            res = doctest.DocTestRunner.summarize(self, verbose)
        finally:
            sys.stdout = old_stdout
        out(string_io.getvalue())
        return res

    def _DocTestRunner__patched_linecache_getlines(self, filename,
                                                   module_globals=None):
        # this is overridden from DocTestRunner adding the try-except below
        m = self._DocTestRunner__LINECACHE_FILENAME_RE.match(filename)
        if m and m.group('name') == self.test.name:
            try:
                example = self.test.examples[int(m.group('examplenum'))]
            # because we compile multiple doctest blocks with the same name
            # (viz. the group name) this might, for outer stack frames in a
            # traceback, get the wrong test which might not have enough examples
            except IndexError:
                pass
            else:
                return example.source.splitlines(True)
        return self.save_linecache_getlines(filename, module_globals)

# the new builder -- use sphinx-build.py -b doctest to run

class DocTestBuilder(Builder):
    """
    Runs test snippets in the documentation.
    """
    name = 'doctest'

    def init(self):
        # default options
        self.opt = doctest.DONT_ACCEPT_TRUE_FOR_1 | doctest.ELLIPSIS | \
                   doctest.IGNORE_EXCEPTION_DETAIL

        # HACK HACK HACK
        # doctest compiles its snippets with type 'single'. That is nice
        # for doctest examples but unusable for multi-statement code such
        # as setup code -- to be able to use doctest error reporting with
        # that code nevertheless, we monkey-patch the "compile" it uses.
        doctest.compile = self.compile

        sys.path[0:0] = self.config.doctest_path

        self.type = 'single'

        self.total_failures = 0
        self.total_tries = 0
        self.setup_failures = 0
        self.setup_tries = 0
        self.cleanup_failures = 0
        self.cleanup_tries = 0

        date = time.strftime('%Y-%m-%d %H:%M:%S')

        self.outfile = codecs.open(path.join(self.outdir, 'output.txt'),
                                   'w', encoding='utf-8')
        self.outfile.write('''\
Results of doctest builder run on %s
==================================%s
''' % (date, '='*len(date)))

    def _out(self, text):
        self.info(text, nonl=True)
        self.outfile.write(text)

    def _warn_out(self, text):
        self.info(text, nonl=True)
        if self.app.quiet:
            self.warn(text)
        if isinstance(text, bytes):
            text = force_decode(text, None)
        self.outfile.write(text)

    def get_target_uri(self, docname, typ=None):
        return ''

    def get_outdated_docs(self):
        return self.env.found_docs

    def finish(self):
        # write executive summary
        def s(v):
            return v != 1 and 's' or ''
        self._out('''
Doctest summary
===============
%5d test%s
%5d failure%s in tests
%5d failure%s in setup code
%5d failure%s in cleanup code
''' % (self.total_tries, s(self.total_tries),
       self.total_failures, s(self.total_failures),
       self.setup_failures, s(self.setup_failures),
       self.cleanup_failures, s(self.cleanup_failures)))
        self.outfile.close()

        if self.total_failures or self.setup_failures or self.cleanup_failures:
            self.app.statuscode = 1

    def write(self, build_docnames, updated_docnames, method='update'):
        if build_docnames is None:
            build_docnames = sorted(self.env.all_docs)

        self.info(bold('running tests...'))
        for docname in build_docnames:
            # no need to resolve the doctree
            doctree = self.env.get_doctree(docname)
            self.test_doc(docname, doctree)

    def test_doc(self, docname, doctree):
        groups = {}
        add_to_all_groups = []
        self.setup_runner = SphinxDocTestRunner(verbose=False,
                                                optionflags=self.opt)
        self.test_runner = SphinxDocTestRunner(verbose=False,
                                               optionflags=self.opt)
        self.cleanup_runner = SphinxDocTestRunner(verbose=False,
                                                  optionflags=self.opt)

        self.test_runner._fakeout = self.setup_runner._fakeout
        self.cleanup_runner._fakeout = self.setup_runner._fakeout

        if self.config.doctest_test_doctest_blocks:
            def condition(node):
                return (isinstance(node, (nodes.literal_block, nodes.comment))
                        and node.has_key('testnodetype')) or \
                       isinstance(node, nodes.doctest_block)
        else:
            def condition(node):
                return isinstance(node, (nodes.literal_block, nodes.comment)) \
                        and node.has_key('testnodetype')
        for node in doctree.traverse(condition):
            source = node.has_key('test') and node['test'] or node.astext()
            if not source:
                self.warn('no code/output in %s block at %s:%s' %
                          (node.get('testnodetype', 'doctest'),
                           self.env.doc2path(docname), node.line))
            code = TestCode(source, type=node.get('testnodetype', 'doctest'),
                            lineno=node.line, options=node.get('options'))


import cocotb
from cocotb.clock import Clock
from cocotb.triggers import Timer
from cocotb.triggers import RisingEdge
from cocotb.result import TestFailure
from cocotb.drivers.amba import AXI4StreamMaster
from cocotb.drivers.amba import AXI4StreamSlave

import numpy as np

class dut_control:
	"""
		A class which contains variety of useful and reused functions and
		capabilities to control a DUT
	"""

	def __init__(self, dut):
		"""
			Initialise the object
		"""

		# save the DUT object internally
		self.dut = dut

		# default parameters
		self.CLK_PERIOD = 10

	@cocotb.coroutine
	def init_ports(self):
		"""
			Set any port initial values.
			This function is meant to be overrode by the child
		"""
		self.dut._log.info("The init_ports() function has not been overridden and therefore no action has been taken")
		yield Timer(0)

	@cocotb.coroutine
	def reset(self):
		"""
			Reset the DUT.
			This function is meant to be overrode by the child
		"""
		self.dut._log.info("The reset() function has not been overridden and therefore no action has been taken")
		yield Timer(0)

	@cocotb.coroutine
	def clock_start(self):
		"""
			Startup the clock required for the DUT.
			This function is meant to be overrode by the child
		"""
		self.dut._log.info("The clock_start() function has not been overridden and therefore no action has been taken")
		yield Timer(0)

	@cocotb.coroutine
	def setup_interfaces(self):
		"""
			Setup the DUT interfaces.
			This function is meant to be overrode by the child
		"""
		self.dut._log.info("The setup_interfaces() function has not been overridden and therefore no action has been taken")
		yield Timer(0)

	@cocotb.coroutine
	def init(self):
		"""
			Initialise the DUT
		"""

		# log the process
		self.dut._log.info("-"*64)
		self.dut._log.info("INITIALISING THE DUT")
		self.dut._log.info("")

		# initialse values
		self.dut._log.info("Setting initial port values")
		yield self.init_ports()

		# set the clock
		self.dut._log.info("Starting the clock")
		yield self.clock_start()

		# set the clock
		self.dut._log.info("Setting up interfaces")
		yield self.setup_interfaces()

		# reset the DUT
		self.dut._log.info("Resetting the DUT")
		yield self.reset()

		self.dut._log.info("DUT Initialisation complete")
		yield Timer(0)

	def print_elements(self):
		"""
			Traverses the dut printing out information about every element in
			the design
		"""

		# loop through the top level elements
		for design_element in self.dut:

			print("-"*100)
			print("Found %s : python type = %s: " % (design_element, type(design_element)))
			print("         : _name = %s: ", design_element._name)
			print("         : _path = %s: ", design_element._path)

			# found a sub element to push into
			if type(design_element) == cocotb.handle.HierarchyArrayObject or type(design_element) == cocotb.handle.HierarchyObject:
				print("Pushing into design element")
				print_elements(design_element)

	def GSR_control(self, dut, value):
		"""
			Allows the setting of the GSR (Global Set/Reset) in Xilinx unisim
			elements.  Will traverse the whole design looking for a GSR signal name
			and will set it.
		"""

		# loop through the top level elements
		for design_element in dut:

			# check that the GSR signal has been found
			if design_element._name == 'GSR':
				design_element.value = value

			# found a sub element to push into
			if type(design_element) == cocotb.handle.HierarchyArrayObject or type(design_element) == cocotb.handle.HierarchyObject:
				self.GSR_control(design_element, value)

	def convert_to_signed(self, signal, number_bits):
		"""
			Convert the list of input numbers to python signed integer format for
			a given number of bits used to represent the number.
		"""

		output_signal = []

		for sample in signal:
			if sample > (2**(number_bits-1)-1):
				output_signal.append( sample - 2**number_bits )
			else:
				output_signal.append( sample )

		return output_signal

	@cocotb.coroutine
	def clk_wait(self, clk_rising, wait_period):
		"""
			Wait for a given number of clock cycles to be more compact for long
			clock delays
		"""

		for i in range(int(wait_period)):
			yield clk_rising

	def signed_to_fixedpoint(self, numbers, number_width, normalised=False, multiplier=1.0):
		"""
			Convert signed numbers to fixed point two's complement version
		"""

		# normalisation factor calculation
		if normalised:
			norm_factor = (2**(number_width-1)-1)/max(numbers)
		else:
			norm_factor = 1

		# support both single numbers and lists
		if type(numbers) == list:
			output = []
			for i, number in enumerate(numbers):
					if number < 0:
						output.append( int(np.round(2**(number_width) + multiplier*number*norm_factor)) )
					else:
						output.append( int(np.round(multiplier*number*norm_factor)) )
			return output

		else:
			if numbers < 0:
				output = int(np.round(2**(number_width) + number*norm_factor))
			else:
				output = int(np.round(number*norm_factor))
			return output

	def fixedpoint_to_signed(self, numbers, number_width, normalised=False):
		"""
			Convert fixed point two's complement number to signed number
		"""

		# support both single numbers and lists
		if type(numbers) == list:
			output = []


#!/usr/bin/python

# glib-ginterface-gen.py: service-side interface generator
#
# Generate dbus-glib 0.x service GInterfaces from the Telepathy specification.
# The master copy of this program is in the telepathy-glib repository -
# please make any changes there.
#
# Copyright (C) 2006, 2007 Collabora Limited
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

import sys
import os.path
import xml.dom.minidom

from libglibcodegen import Signature, type_to_gtype, cmp_by_name, \
        camelcase_to_lower, NS_TP, dbus_gutils_wincaps_to_uscore, \
        signal_to_marshal_name, method_to_glue_marshal_name

NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"

class Generator(object):

    def __init__(self, dom, prefix, basename, signal_marshal_prefix,
                 headers, end_headers, not_implemented_func,
                 allow_havoc):
        self.dom = dom
        self.__header = []
        self.__body = []

        assert prefix.endswith('_')
        assert not signal_marshal_prefix.endswith('_')

        # The main_prefix, sub_prefix thing is to get:
        # FOO_ -> (FOO_, _)
        # FOO_SVC_ -> (FOO_, _SVC_)
        # but
        # FOO_BAR/ -> (FOO_BAR_, _)
        # FOO_BAR/SVC_ -> (FOO_BAR_, _SVC_)

        if '/' in prefix:
            main_prefix, sub_prefix = prefix.upper().split('/', 1)
            prefix = prefix.replace('/', '_')
        else:
            main_prefix, sub_prefix = prefix.upper().split('_', 1)

        self.MAIN_PREFIX_ = main_prefix + '_'
        self._SUB_PREFIX_ = '_' + sub_prefix

        self.Prefix_ = prefix
        self.Prefix = prefix.replace('_', '')
        self.prefix_ = prefix.lower()
        self.PREFIX_ = prefix.upper()

        self.signal_marshal_prefix = signal_marshal_prefix
        self.headers = headers
        self.end_headers = end_headers
        self.not_implemented_func = not_implemented_func
        self.allow_havoc = allow_havoc

    def h(self, s):
        self.__header.append(s)

    def b(self, s):
        self.__body.append(s)

    def do_node(self, node):
        node_name = node.getAttribute('name').replace('/', '')
        node_name_mixed = self.node_name_mixed = node_name.replace('_', '')
        node_name_lc = self.node_name_lc = node_name.lower()
        node_name_uc = self.node_name_uc = node_name.upper()

        interfaces = node.getElementsByTagName('interface')
        assert len(interfaces) == 1, interfaces
        interface = interfaces[0]
        self.iface_name = interface.getAttribute('name')

        tmp = interface.getAttribute('tp:implement-service')
        if tmp == "no":
            return

        tmp = interface.getAttribute('tp:causes-havoc')
        if tmp and not self.allow_havoc:
            raise AssertionError('%s is %s' % (self.iface_name, tmp))

        self.b('static const DBusGObjectInfo _%s%s_object_info;'
               % (self.prefix_, node_name_lc))
        self.b('')

        methods = interface.getElementsByTagName('method')
        signals = interface.getElementsByTagName('signal')
        properties = interface.getElementsByTagName('property')
        # Don't put properties in dbus-glib glue
        glue_properties = []

        self.b('struct _%s%sClass {' % (self.Prefix, node_name_mixed))
        self.b('    GTypeInterface parent_class;')
        for method in methods:
            self.b('    %s %s;' % self.get_method_impl_names(method))
        self.b('};')
        self.b('')

        if signals:
            self.b('enum {')
            for signal in signals:
                self.b('    %s,' % self.get_signal_const_entry(signal))
            self.b('    N_%s_SIGNALS' % node_name_uc)
            self.b('};')
            self.b('static guint %s_signals[N_%s_SIGNALS] = {0};'
                   % (node_name_lc, node_name_uc))
            self.b('')

        self.b('static void %s%s_base_init (gpointer klass);'
               % (self.prefix_, node_name_lc))
        self.b('')

        self.b('GType')
        self.b('%s%s_get_type (void)'
               % (self.prefix_, node_name_lc))
        self.b('{')
        self.b('  static GType type = 0;')
        self.b('')
        self.b('  if (G_UNLIKELY (type == 0))')
        self.b('    {')
        self.b('      static const GTypeInfo info = {')
        self.b('        sizeof (%s%sClass),' % (self.Prefix, node_name_mixed))
        self.b('        %s%s_base_init, /* base_init */'
               % (self.prefix_, node_name_lc))
        self.b('        NULL, /* base_finalize */')
        self.b('        NULL, /* class_init */')
        self.b('        NULL, /* class_finalize */')
        self.b('        NULL, /* class_data */')
        self.b('        0,')
        self.b('        0, /* n_preallocs */')
        self.b('        NULL /* instance_init */')
        self.b('      };')
        self.b('')
        self.b('      type = g_type_register_static (G_TYPE_INTERFACE,')
        self.b('          "%s%s", &info, 0);' % (self.Prefix, node_name_mixed))
        self.b('    }')
        self.b('')
        self.b('  return type;')
        self.b('}')
        self.b('')

        self.h('/**')
        self.h(' * %s%s:' % (self.Prefix, node_name_mixed))
        self.h(' *')
        self.h(' * Dummy typedef representing any implementation of this '
               'interface.')
        self.h(' */')
        self.h('typedef struct _%s%s %s%s;'
               % (self.Prefix, node_name_mixed, self.Prefix, node_name_mixed))
        self.h('')
        self.h('/**')
        self.h(' * %s%sClass:' % (self.Prefix, node_name_mixed))
        self.h(' *')
        self.h(' * The class of %s%s.' % (self.Prefix, node_name_mixed))
        self.h(' */')
        self.h('typedef struct _%s%sClass %s%sClass;'
               % (self.Prefix, node_name_mixed, self.Prefix, node_name_mixed))
        self.h('')
        self.h('GType %s%s_get_type (void);'
               % (self.prefix_, node_name_lc))

        gtype = self.current_gtype = \
                self.MAIN_PREFIX_ + 'TYPE' + self._SUB_PREFIX_ + node_name_uc
        classname = self.Prefix + node_name_mixed

        self.h('#define %s \\\n  (%s%s_get_type ())'
               % (gtype, self.prefix_, node_name_lc))
        self.h('#define %s%s(obj) \\\n'
               '  (G_TYPE_CHECK_INSTANCE_CAST((obj), %s, %s))'
               % (self.PREFIX_, node_name_uc, gtype, classname))
        self.h('#define %sIS%s%s(obj) \\\n'
               '  (G_TYPE_CHECK_INSTANCE_TYPE((obj), %s))'
               % (self.MAIN_PREFIX_, self._SUB_PREFIX_, node_name_uc, gtype))
        self.h('#define %s%s_GET_CLASS(obj) \\\n'
               '  (G_TYPE_INSTANCE_GET_INTERFACE((obj), %s, %sClass))'
               % (self.PREFIX_, node_name_uc, gtype, classname))
        self.h('')
        self.h('')

        base_init_code = []

        for method in methods:
            self.do_method(method)

        for signal in signals:
            base_init_code.extend(self.do_signal(signal))

        self.b('static inline void')
        self.b('%s%s_base_init_once (gpointer klass G_GNUC_UNUSED)'
               % (self.prefix_, node_name_lc))
        self.b('{')
        self.b('  static TpDBusPropertiesMixinPropInfo properties[%d] = {'
               % (len(properties) + 1))

        for m in properties:
            access = m.getAttribute('access')
            assert access in ('read', 'write', 'readwrite')

            if access == 'read':
                flags = 'TP_DBUS_PROPERTIES_MIXIN_FLAG_READ'
            elif access == 'write':
                flags = 'TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE'
            else:
                flags = ('TP_DBUS_PROPERTIES_MIXIN_FLAG_READ | '
                         'TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE')

            self.b('      { 0, %s, "%s", 0, NULL, NULL }, /* %s */'
                   % (flags, m.getAttribute('type'), m.getAttribute('name')))

        self.b('      { 0, 0, NULL, 0, NULL, NULL }')
        self.b('  };')
        self.b('  static TpDBusPropertiesMixinIfaceInfo interface =')
        self.b('      { 0, properties, NULL, NULL };')
        self.b('')
        self.b('  interface.dbus_interface = g_quark_from_static_string '
               '("%s");' % self.iface_name)

        for i, m in enumerate(properties):
            self.b('  properties[%d].name = g_quark_from_static_string ("%s");'
                   % (i, m.getAttribute('name')))
            self.b('  properties[%d].type = %s;'
                   % (i, type_to_gtype(m.getAttribute('type'))[1]))

        self.b('  tp_svc_interface_set_dbus_properties_info (%s, &interface);'
               % self.current_gtype)

        self.b('')
        for s in base_init_code:
            self.b(s)
        self.b('  dbus_g_object_type_install_info (%s%s_get_type (),'
               % (self.prefix_, node_name_lc))
        self.b('      &_%s%s_object_info);'
               % (self.prefix_, node_name_lc))
        self.b('}')

        self.b('static void')
        self.b('%s%s_base_init (gpointer klass)'
               % (self.prefix_, node_name_lc))
        self.b('{')
        self.b('  static gboolean initialized = FALSE;')
        self.b('')
        self.b('  if (!initialized)')
        self.b('    {')
        self.b('      initialized = TRUE;')
        self.b('      %s%s_base_init_once (klass);'
               % (self.prefix_, node_name_lc))
        self.b('    }')
        # insert anything we need to do per implementation here
        self.b('}')

        self.h('')

        self.b('static const DBusGMethodInfo _%s%s_methods[] = {'
               % (self.prefix_, node_name_lc))

        method_blob, offsets = self.get_method_glue(methods)

        for method, offset in zip(methods, offsets):
            self.do_method_glue(method, offset)

        self.b('};')
        self.b('')

        self.b('static const DBusGObjectInfo _%s%s_object_info = {'
               % (self.prefix_, node_name_lc))
        self.b('  0,')  # version
        self.b('  _%s%s_methods,' % (self.prefix_, node_name_lc))
        self.b('  %d,' % len(methods))
        self.b('"' + method_blob.replace('\0', '\\0') + '",')
        self.b('"' + self.get_signal_glue(signals).replace('\0', '\\0') + '",')
        self.b('"' +
               self.get_property_glue(glue_properties).replace('\0', '\\0') +
               '",')
        self.b('};')
        self.b('')

        self.node_name_mixed = None
        self.node_name_lc = None
        self.node_name_uc = None

    def get_method_glue(self, methods):
        info = []
        offsets = []

        for method in methods:
            offsets.append(len(''.join(info)))

            info.append(self.iface_name + '\0')
            info.append(method.getAttribute('name') + '\0')

            info.append('A\0')    # async

            counter = 0
            for arg in method.getElementsByTagName('arg'):
                out = arg.getAttribute('direction') == 'out'

                name = arg.getAttribute('name')
                if not name:
                    assert out
                    name = 'arg%u' % counter
                counter += 1

                info.append(name + '\0')

                if out:
                    info.append('O\0')
                else:
                    info.append('I\0')

                if out:
                    info.append('F\0')    # not const
                    info.append('N\0')    # not error or return
                info.append(arg.getAttribute('type') + '\0')

            info.append('\0')


# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
from pathlib import Path
import pytest
import os.path as osp
from sisl import geom
from sisl import Geometry, Atom
from sisl.io import fdfSileSiesta, SileError
from sisl.messages import SislWarning
from sisl.unit.siesta import unit_convert
import numpy as np

pytestmark = [pytest.mark.io, pytest.mark.siesta, pytest.mark.fdf,
              pytest.mark.filterwarnings("ignore", message="*number of supercells")
]
_dir = osp.join('sisl', 'io', 'siesta')

def test_fdf1(sisl_tmp, sisl_system):
    f = sisl_tmp('gr.fdf', _dir)
    sisl_system.g.write(fdfSileSiesta(f, 'w'))

    fdf = fdfSileSiesta(f)
    str(fdf)
    with fdf:

        fdf.readline()

        # Be sure that we can read it in a loop
        assert fdf.get('LatticeConstant') > 0.
        assert fdf.get('LatticeConstant') > 0.
        assert fdf.get('LatticeConstant') > 0.

        fdf.read_supercell()
        fdf.read_geometry()

def test_fdf2(sisl_tmp, sisl_system):
    f = sisl_tmp('gr.fdf', _dir)
    sisl_system.g.write(fdfSileSiesta(f, 'w'))
    g = fdfSileSiesta(f).read_geometry()

    # Assert they are the same
    assert np.allclose(g.cell, sisl_system.g.cell)
    assert np.allclose(g.xyz, sisl_system.g.xyz)
    for ia in g:
        assert g.atoms[ia].Z == sisl_system.g.atoms[ia].Z
        assert g.atoms[ia].tag == sisl_system.g.atoms[ia].tag

def test_fdf_units(sisl_tmp, sisl_system):
    f = sisl_tmp('gr.fdf', _dir)
    fdf = fdfSileSiesta(f, 'w')
    g = sisl_system.g

    for unit in ['bohr', 'ang', 'fractional', 'frac']:
        fdf.write_geometry(g, unit=unit)
        g2 = fdfSileSiesta(f).read_geometry()
        assert np.allclose(g.cell, g2.cell)
        assert np.allclose(g.xyz, g2.xyz)
        for ia in g:
            assert g.atoms[ia].Z == g2.atoms[ia].Z
            assert g.atoms[ia].tag == g2.atoms[ia].tag

def test_supercell(sisl_tmp):
    f = sisl_tmp('file.fdf', _dir)
    lines = [
        'Latticeconstant 1. Ang',
        '%block Latticevectors',
        ' 1. 1. 1.',
        ' 0. 0. 1.',
        ' 1. 0. 1.',
        '%endblock',
    ]
    with open(f, 'w') as fh:
        fh.write('\n'.join(lines))

    cell = np.array([[1.]*3, [0, 0, 1], [1, 0, 1]])
    sc = fdfSileSiesta(f).read_supercell()
    assert np.allclose(sc.cell, cell)

    lines = [
        'Latticeconstant 1. Bohr',
        '%block Latticevectors',
        ' 1. 1. 1.',
        ' 0. 0. 1.',
        ' 1. 0. 1.',
        '%endblock',
    ]
    with open(f, 'w') as fh:
        fh.write('\n'.join(lines))

    sc = fdfSileSiesta(f).read_supercell()
    assert np.allclose(sc.cell, cell * unit_convert('Bohr', 'Ang'))

    cell = np.diag([2.] * 3)
    lines = [
        'Latticeconstant 2. Ang',
        '%block Latticeparameters',
        ' 1. 1. 1. 90. 90. 90.',
        '%endblock',
    ]
    with open(f, 'w') as fh:
        fh.write('\n'.join(lines))

    sc = fdfSileSiesta(f).read_supercell()
    assert np.allclose(sc.cell, cell)

def test_supercell_fail(sisl_tmp):
    f = sisl_tmp('file.fdf', _dir)
    lines = [
        '%block Latticevectors',
        ' 1. 1. 1.',
        ' 0. 0. 1.',
        ' 1. 0. 1.',
        '%endblock',
    ]
    with open(f, 'w') as fh:
        fh.write('\n'.join(lines))
    with pytest.raises(SileError):
        fdfSileSiesta(f).read_supercell()

def test_geometry(sisl_tmp):
    f = sisl_tmp('file.fdf', _dir)
    sc_lines = [
        'Latticeconstant 1. Ang',
        '%block latticeparameters',
        ' 1. 1. 1. 90. 90. 90.',
        '%endblock',
    ]
    lines = [
        'NumberOfAtoms 2',
        '%block chemicalSpeciesLabel',
        ' 1 6 C',
        ' 2 12 H',
        '%endblock',
        'AtomicCoordinatesFormat Ang',
        '%block atomiccoordinatesandatomicspecies',
        ' 1. 1. 1. 1',
        ' 0. 0. 1. 1',
        ' 1. 0. 1. 2',
        '%endblock',
    ]

    with open(f, 'w') as fh:
        fh.write('\n'.join(sc_lines) + '\n')
        fh.write('\n'.join(lines))

    fdf = fdfSileSiesta(f, base=sisl_tmp.getbase())
    g = fdf.read_geometry()
    assert g.na == 2
    assert np.allclose(g.xyz, [[1.] * 3,
                               [0, 0, 1]])
    assert g.atoms[0].Z == 6
    assert g.atoms[1].Z == 6

    # default read # of atoms from list
    with open(f, 'w') as fh:
        fh.write('\n'.join(sc_lines) + '\n')
        fh.write('\n'.join(lines[1:]))

    fdf = fdfSileSiesta(f, base=sisl_tmp.getbase())
    g = fdf.read_geometry()
    assert g.na == 3
    assert np.allclose(g.xyz, [[1.] * 3,
                               [0, 0, 1],
                               [1, 0, 1]])
    assert g.atoms[0].Z == 6
    assert g.atoms[1].Z == 6
    assert g.atoms[2].Z == 12

def test_re_read(sisl_tmp):
    f = sisl_tmp('file.fdf', _dir)
    with open(f, 'w') as fh:
        fh.write('Flag1 date\n')
        fh.write('Flag1 not-date\n')
        fh.write('Flag1 not-date-2\n')
        fh.write('Flag3 true\n')

    fdf = fdfSileSiesta(f)
    for i in range(10):
        assert fdf.get('Flag1') == 'date'
    assert fdf.get('Flag3')

def test_get_set(sisl_tmp):
    f = sisl_tmp('file.fdf', _dir)
    with open(f, 'w') as fh:
        fh.write('Flag1 date\n')

    fdf = fdfSileSiesta(f)
    assert fdf.get('Flag1') == 'date'
    fdf.set('Flag1', 'not-date')
    assert fdf.get('Flag1') == 'not-date'
    fdf.set('Flag1', 'date')
    assert fdf.get('Flag1') == 'date'
    fdf.set('Flag1', 'date-date')
    assert fdf.get('Flag1') == 'date-date'
    fdf.set('Flag1', 'date-date', keep=False)

def test_get_block(sisl_tmp):
    f = sisl_tmp('file.fdf', _dir)
    with open(f, 'w') as fh:
        fh.write('%block MyBlock\n  date\n%endblock\n')

    fdf = fdfSileSiesta(f)

    assert isinstance(fdf.get('MyBlock'), list)
    assert fdf.get('MyBlock')[0] == 'date'
    assert 'block' in fdf.print("MyBlock", fdf.get("MyBlock"))

def test_include(sisl_tmp):
    f = sisl_tmp('file.fdf', _dir)
    with open(f, 'w') as fh:
        fh.write('Flag1 date\n')
        fh.write('# Flag2 comment\n')
        fh.write('Flag2 date2\n')
        fh.write('# Flag3 is read through < from file hello\n')
        fh.write('Flag3 Sub < hello\n')
        fh.write('FakeInt 1\n')
        fh.write('Test 1. eV\n')
        fh.write(' %INCLUDE file2.fdf\n')
        fh.write('TestRy 1. Ry\n')
        fh.write('%block Hello < hello\n')
        fh.write('TestLast 1. eV\n')

    hello = sisl_tmp('hello', _dir)
    with open(hello, 'w') as fh:
        fh.write('Flag4 hello\n')
        fh.write('# Comments should be discarded\n')
        fh.write('Flag3 test\n')
        fh.write('Sub sub-test\n')

    file2 = sisl_tmp('file2.fdf', _dir)
    with open(file2, 'w') as fh:
        fh.write('Flag4 non\n')
        fh.write('FakeReal 2.\n')
        fh.write('  %incLude file3.fdf')

    file3 = sisl_tmp('file3.fdf', _dir)
    with open(file3, 'w') as fh:
        fh.write('Sub level\n')
        fh.write('Third level\n')
        fh.write('MyList [1 , 2 , 3]\n')

    fdf = fdfSileSiesta(f, base=sisl_tmp.getbase())
    assert fdf.includes() == [Path(hello), Path(file2), Path(file3)]
    assert fdf.get('Flag1') == 'date'
    assert fdf.get('Flag2') == 'date2'
    assert fdf.get('Flag3') == 'test'
    assert fdf.get('Flag4') == 'non'
    assert fdf.get('FLAG4') == 'non'
    assert fdf.get('Fakeint') == 1
    assert fdf.get('Fakeint', '0') == '1'
    assert fdf.get('Fakereal') == 2.
    assert fdf.get('Fakereal', 0.) == 2.
    assert fdf.get('test', 'eV') == pytest.approx(1.)
    assert fdf.get('test', with_unit=True)[0] == pytest.approx(1.)
    assert fdf.get('test', with_unit=True)[1] == 'eV'
    assert fdf.get('test', unit='Ry') == pytest.approx(unit_convert('eV', 'Ry'))
    assert fdf.get('testRy') == pytest.approx(unit_convert('Ry', 'eV'))
    assert fdf.get('testRy', with_unit=True)[0] == pytest.approx(1.)
    assert fdf.get('testRy', with_unit=True)[1] == 'Ry'
    assert fdf.get('testRy', unit='Ry') == pytest.approx(1.)
    assert fdf.get('Sub') == 'sub-test'
    assert fdf.get('Third') == 'level'
    assert fdf.get('test-last', with_unit=True)[0] == pytest.approx(1.)
    assert fdf.get('test-last', with_unit=True)[1] == 'eV'

    # Currently lists are not implemented
    #assert np.allclose(fdf.get('MyList'), np.arange(3) + 1)
    #assert np.allclose(fdf.get('MyList', []), np.arange(3) + 1)

    # Read a block
    ll = open(sisl_tmp('hello', _dir)).readlines()
    ll.pop(1)
    assert fdf.get('Hello') == [l.replace('\n', '').strip() for l in ll]

def test_xv_preference(sisl_tmp):
    g = geom.graphene()
    g.write(sisl_tmp('file.fdf', _dir))
    g.xyz[0, 0] += 1.
    g.write(sisl_tmp('siesta.XV', _dir))

    sc = fdfSileSiesta(sisl_tmp('file.fdf', _dir)).read_supercell(True)
    g2 = fdfSileSiesta(sisl_tmp('file.fdf', _dir)).read_geometry(True)
    assert np.allclose(sc.cell, g.cell)
    assert np.allclose(g.cell, g2.cell)
    assert np.allclose(g.xyz, g2.xyz)

    g2 = fdfSileSiesta(sisl_tmp('file.fdf', _dir)).read_geometry(order=['fdf'])
    assert np.allclose(g.cell, g2.cell)
    g2.xyz[0, 0] += 1.
    assert np.allclose(g.xyz, g2.xyz)

def test_geom_order(sisl_tmp):
    gfdf = geom.graphene()
    gxv = gfdf.copy()
    gxv.xyz[0, 0] += 0.5
    gnc = gfdf.copy()
    gnc.xyz[0, 0] += 0.5

    gfdf.write(sisl_tmp('siesta.fdf', _dir))

    # Create fdf-file
    fdf = fdfSileSiesta(sisl_tmp('siesta.fdf', _dir))
    assert fdf.read_geometry(True, order=['nc']) is None
    gxv.write(sisl_tmp('siesta.XV', _dir))
    gnc.write(sisl_tmp('siesta.nc', _dir))

    # Should read from XV
    g = fdf.read_geometry(True)
    assert np.allclose(g.xyz, gxv.xyz)
    g = fdf.read_geometry(order=['nc', 'fdf'])
    assert np.allclose(g.xyz, gnc.xyz)
    g = fdf.read_geometry(order=['fdf', 'nc'])
    assert np.allclose(g.xyz, gfdf.xyz)
    g = fdf.read_geometry(order=['xv', 'nc'])
    assert np.allclose(g.xyz, gxv.xyz)

def test_geom_constraints(sisl_tmp):
    gfdf = geom.graphene().tile(2, 0).tile(2, 1)
    gfdf['CONSTRAIN'] = 0
    gfdf['CONSTRAIN-x'] = 2
    gfdf['CONSTRAIN-y'] = [1, 3, 4, 5]
    gfdf['CONSTRAIN-z'] = range(len(gfdf))

    gfdf.write(sisl_tmp('siesta.fdf', _dir))

def test_h2_dynamical_matrix(sisl_files):
    si = fdfSileSiesta(sisl_files(_dir, 'H2_dynamical_matrix.fdf'))

    trans_inv = [True, False]
    sum0 = trans_inv[:]
    hermitian = trans_inv[:]

    eV2cm = 8065.54429
    hw_true = [-88.392650, -88.392650, -0.000038, -0.000001, 0.000025, 3797.431825]

    from itertools import product
    for ti, s0, herm in product(trans_inv, sum0, hermitian):
        dyn = si.read_dynamical_matrix(trans_inv=ti, sum0=s0, hermitian=herm)
        hw = dyn.eigenvalue().hw
        if ti and s0 and herm:
            assert np.allclose(hw * eV2cm, hw_true, atol=1e-4)

def test_dry_read(sisl_tmp):
    # This test runs the read-functions. They aren't expected to actually read anything,
    # it is only a dry-run.
    file = sisl_tmp('siesta.fdf', _dir)
    geom.graphene().write(file)
    fdf = fdfSileSiesta(file)

    read_methods = set(m for m in dir(fdf) if m.startswith("read_"))
    output = dict(output=True)
    kwargs = {
        "supercell": output,
        "geometry": output,
        "grid": dict(name="rho"),
    }

    with pytest.warns(SislWarning):