"""
Wire Renderer
"""

from . import wire_template
from .. import renderer_result
from ...visitors import data_visitor as dv

class WirePrimitiveRenderer(dv.DataVisitor):
    def __init__(self, string_stream, new_line, render_name=True):
        self.string_stream = string_stream
        self.new_line = new_line
        self.render_name = render_name

    def visit(self, data):
        super(WirePrimitiveRenderer, self).visit(data)

    def enter_primitive(self, data):
        self.string_stream.append(wire_template.level(data['level']))

        if self.render_name:
            self.string_stream.append(wire_template.render_name(data['name']))

        if data['type'] == 'int':
            self.string_stream.append(wire_template.int_type_spec())
        elif data['type'] == 'double':
            self.string_stream.append(wire_template.double_type_spec())
        elif data['type'] == 'float':
            self.string_stream.append(wire_template.float_type_spec())
        elif data['type'] == 'bool':
            self.string_stream.append(wire_template.boolean_type_spec())
        self.string_stream.append(wire_template.sep())
        self.string_stream.append(self.new_line)

class WireStringRenderer(dv.DataVisitor):
    def __init__(self, string_stream, new_line, render_name=True):
        self.string_stream = string_stream
        self.new_line = new_line
        self.render_name = render_name

    def visit(self, data):
        super(WireStringRenderer, self).visit(data)

    def enter_string(self, data):
        self.string_stream.append(wire_template.level(data['level']))
        self.string_stream.append("<<length>>: 16bit-integer")
        self.string_stream.append(wire_template.sep())
        self.string_stream.append(self.new_line)

        self.string_stream.append(wire_template.level(data['level']))
        if self.render_name:
            self.string_stream.append(wire_template.render_name(data['name']))

        self.string_stream.append(wire_template.string_type_spec())
        self.string_stream.append(wire_template.sep())
        self.string_stream.append(self.new_line)

class WireObjectRenderer(dv.DataVisitor):
    def __init__(self, string_stream, new_line):
        self.string_stream = string_stream
        self.new_line = new_line

    def visit(self, data):
        super(WireObjectRenderer, self).visit(data)

    def enter_userdefined(self, data):
        if (not data['is_vector_elt'] and
            dv.is_userdefined_root(data)):
            return

        self.string_stream.append(wire_template.level(data['level']))

        if not data['is_vector_elt']:
            self.string_stream.append(wire_template.render_name(
                data['name']))

        self.string_stream.append(wire_template.object_begin())
        self.string_stream.append(self.new_line)

    def exit_userdefined(self, data):
        if (not data['is_vector_elt'] and
            dv.is_userdefined_root(data)):
            return

        self.string_stream.pop_back(2)
        self.string_stream.append(self.new_line)
        self.string_stream.append(wire_template.level(data['level']))
        self.string_stream.append(wire_template.object_end())
        self.string_stream.append(wire_template.sep())
        self.string_stream.append(self.new_line)

    def enter_primitive(self, data):
        render_name = not data['is_vector_elt']
        r = WirePrimitiveRenderer(self.string_stream,
                self.new_line,
                render_name)
        return r

    def enter_string(self, data):
        render_name = not data['is_vector_elt']
        r = WireStringRenderer(self.string_stream,
                self.new_line,
                render_name)
        return r

    def enter_vector(self, data):
        self.string_stream.append(wire_template.level(data['level']))
        self.string_stream.append("<<#elements>>: 16bit-integer")
        self.string_stream.append(wire_template.sep())
        self.string_stream.append(self.new_line)

        self.string_stream.append(wire_template.level(data['level']))
        self.string_stream.append(wire_template.render_name(data['name']))
        self.string_stream.append(wire_template.array_begin())
        self.string_stream.append(self.new_line)

    def exit_vector(self, data):
        self.string_stream.pop_back(2)
        self.string_stream.append(self.new_line)
        self.string_stream.append(wire_template.level(data['level']))
        self.string_stream.append(wire_template.array_end())
        self.string_stream.append(wire_template.sep())
        self.string_stream.append(self.new_line)

class WireRootRenderer(dv.DataVisitor):
    def __init__(self, string_stream, new_line):
        self.string_stream = string_stream
        self.new_line = new_line

    def visit(self, data):
        super(WireRootRenderer, self).visit(data)

    def enter_root(self, data):
        r = WireObjectRenderer(self.string_stream, self.new_line)
        return r

class WireRenderer(dv.DataVisitor):
    def __init__(self, new_line=''):
        self.new_line = new_line
        self.string_stream = renderer_result.RendererResult()

    def visit(self, data):
        super(WireRenderer, self).visit(data)

    def enter_root(self, data):
        self.string_stream.append(wire_template.object_begin())
        self.string_stream.append(self.new_line)
        r =  WireRootRenderer(self.string_stream, self.new_line)
        return r

    def exit_root(self, data):
        self.string_stream.pop_back(2)
        self.string_stream.append(self.new_line)
        self.string_stream.append(wire_template.level(data['level']))
        self.string_stream.append(wire_template.object_end())

    def get_string(self):
        return self.string_stream.get_string()

    def dump(self, filename):
        with open(filename, 'w') as f:
            f.write(self.get_string())
            f.flush()

