#!/usr/bin/env python3
# This file is a part of marzer/soagen and is subject to the the terms of the MIT license.
# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
# See https://github.com/marzer/soagen/blob/master/LICENSE for the full license text.
# SPDX-License-Identifier: MIT

from xml.sax.saxutils import escape

from . import utils
from .column import *
from .configurable import Configurable
from .metavars import *
from .writer import *
from .version import *


class NatvisFile(Configurable):
    def __init__(self, config, structs):
        super().__init__(config)
        self.path = self.config.path.with_suffix('.natvis')
        self.structs = utils.coerce_collection(structs)

        self.meta = MetaVars()
        for prefix in (r'file_', r'file::'):
            self.meta.push(rf'{prefix}name', self.path.name)
            self.meta.push(rf'{prefix}path', str(self.path))

    def write(self, o: Writer):
        with MetaScope(self):
            o(
                rf'''
            <?xml version="1.0" encoding="utf-8"?>
            <!--
            // This file was generated by soagen v{VERSION_STRING} - do not modify it directly
            // https://marzer.github.io/soagen
            -->
            <AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
            '''
            )

            with Indent(o):
                for struct in self.structs:
                    with MetaScope(struct):
                        o(
                            rf'''
                        <!--{"="*(120 - o.indent_width - 4)}
                        {struct.name}
                        {"="*(120 - o.indent_width - 3)}-->'''
                        )
                        with Indent(o, pre=f'\n\n<Type Name="{escape(struct.qualified_type)}">', post='</Type>'):
                            o(
                                r'''
                            <Intrinsic Name="size" Expression="table_.count_" />
                            <Intrinsic Name="size_bytes" Expression="table_.alloc_.size_in_bytes" />
                            <Intrinsic Name="capacity" Expression="table_.capacity_.first_" />
                            '''
                            )
                            for col in struct.columns:
                                o(
                                    rf'''
                                <Intrinsic
                                    Name="get_{col.index}"
                                    Expression="reinterpret_cast&lt;{escape(col.type)}*&gt;(table_.alloc_.columns[{col.index}])"
                                />
                                '''
                                )
                            o(r'<DisplayString>{{ size={size()} }}</DisplayString>')
                            with Indent(o, pre='<Expand>', post='</Expand>\n'):
                                o(
                                    r'''
                                <Item Name="[size]">size()</Item>
                                <Item Name="[capacity]">capacity()</Item>
                                <Item Name="[allocation_size]">size_bytes()</Item>
                                '''
                                )
                                for col in struct.columns:
                                    get = rf'get_{col.index}()'
                                    o(
                                        rf'''
                                    <Synthetic Name="{col.name}">
                                        <DisplayString Condition="size() &gt; 3u">{{{{ {{*({get})}}, {{*({get} + 1)}}, {{*({get} + 2)}}, ... }}}}</DisplayString>
                                        <DisplayString Condition="size() == 3u">{{{{ {{*({get})}}, {{*({get} + 1)}}, {{*({get} + 2)}} }}}}</DisplayString>
                                        <DisplayString Condition="size() == 2u">{{{{ {{*({get})}}, {{*({get} + 1)}} }}}}</DisplayString>
                                        <DisplayString Condition="size() == 1u">{{{{ {{*({get})}} }}}}</DisplayString>
                                        <DisplayString Condition="size() == 0u"></DisplayString>
                                        <Expand>
                                            <ArrayItems>
                                                <Size>size()</Size>
                                                <ValuePointer>{get}</ValuePointer>
                                            </ArrayItems>
                                        </Expand>
                                    </Synthetic>
                                    '''
                                    )

                        row_types = [
                            rf'soagen::row<{struct.qualified_type}, {struct.column_indices}>',
                            rf'soagen::row<{struct.qualified_type}&, {struct.column_indices}>',
                            rf'soagen::row<{struct.qualified_type}&&, {struct.column_indices}>',
                            rf'soagen::row<const {struct.qualified_type}, {struct.column_indices}>',
                            rf'soagen::row<const {struct.qualified_type}&, {struct.column_indices}>',
                            rf'soagen::row<const {struct.qualified_type}&&, {struct.column_indices}>',
                        ]
                        with Indent(o, pre=f'\n\n<Type Name="{escape(row_types[0])}">', post='</Type>'):
                            for i in range(1, len(row_types)):
                                o(rf'<AlternativeType Name="{escape(row_types[i])}" />')

                            s = r''
                            if len(struct.columns) > 1:
                                s += r'{{ '
                            s += r'{'
                            s += r'}, {'.join(col.name for col in struct.columns[:8])
                            s += r'}'
                            if len(struct.columns) > 8:
                                s += r', ...'
                            if len(struct.columns) > 1:
                                s += r' }}'
                            o(rf'<DisplayString>{s}</DisplayString>')
                            with Indent(o, pre='<Expand>', post='</Expand>\n'):
                                for col in struct.columns:
                                    o(rf'<Item Name="{col.name}">{col.name}</Item>')

            o(r'</AutoVisualizer>')


__all__ = [r'NatvisFile']
