'''
Generates dumper for the SVGA 3D command stream using pygccxml.

Jose Fonseca <jfonseca@vmware.com>
'''

copyright = '''
/**********************************************************
 * Copyright 2009 VMware, Inc.  All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 **********************************************************/
 '''

'''
The script was reworked for Python 3 by VirtualBox.
'''

import os
import sys

from pygccxml import parser
from pygccxml import declarations
from pygccxml import utils

from pygccxml.declarations import algorithm
from pygccxml.declarations import decl_visitor
from pygccxml.declarations import type_traits
from pygccxml.declarations import type_visitor


enums = True


class decl_dumper_t(decl_visitor.decl_visitor_t):

    def __init__(self, instance = '', decl = None):
        decl_visitor.decl_visitor_t.__init__(self)
        self._instance = instance
        self.decl = decl

    def clone(self):
        return decl_dumper_t(self._instance, self.decl)

    def visit_class(self):
        class_ = self.decl
        assert self.decl.class_type in ('struct', 'union')

        for variable in class_.variables():
            if variable.name != '':
                #print 'variable = %r' % variable.name
                dump_type(self._instance + '.' + variable.name, variable.decl_type)

    def visit_enumeration(self):
        if enums:
            print('   switch(%s) {' % ("(*cmd)" + self._instance,))
            for name, value in self.decl.values:
                if name.endswith('_MIN') or name.endswith('_MAX') or name == 'SVGA3D_R11G11B10_TYPELESS':
                    continue
                print('   case %s:' % (name,))
                print('      _debug_printf("\\t\\t%s = %s\\n");' % (self._instance, name))
                print('      break;')
            print('   default:')
            print('      _debug_printf("\\t\\t%s = %%i\\n", %s);' % (self._instance, "(*cmd)" + self._instance))
            print('      break;')
            print('   }')
        else:
            print('   _debug_printf("\\t\\t%s = %%i\\n", %s);' % (self._instance, "(*cmd)" + self._instance))


def dump_decl(instance, decl):
    dumper = decl_dumper_t(instance, decl)
    algorithm.apply_visitor(dumper, decl)


class type_dumper_t(type_visitor.type_visitor_t):

    def __init__(self, instance, type_):
        type_visitor.type_visitor_t.__init__(self)
        self.instance = instance
        self.type = type_

    def clone(self):
        return type_dumper_t(self.instance, self.type)

    def visit_char(self):
        self.print_instance('%i')

    def visit_unsigned_char(self):
        self.print_instance('%u')

    def visit_signed_char(self):
        self.print_instance('%i')

    def visit_wchar(self):
        self.print_instance('%i')

    def visit_short_int(self):
        self.print_instance('%i')

    def visit_short_unsigned_int(self):
        self.print_instance('%u')

    def visit_bool(self):
        self.print_instance('%i')

    def visit_int(self):
        self.print_instance('%i')

    def visit_unsigned_int(self):
        self.print_instance('%u')

    def visit_long_int(self):
        self.print_instance('%li')

    def visit_long_unsigned_int(self):
        self.print_instance('%lu')

    def visit_long_long_int(self):
        self.print_instance('%lli')

    def visit_long_long_unsigned_int(self):
        self.print_instance('%llu')

    def visit_float(self):
        self.print_instance('%f')

    def visit_double(self):
        self.print_instance('%f')

    def visit_array(self):
        for i in range(type_traits.array_size(self.type)):
            dump_type(self.instance + '[%i]' % i, type_traits.base_type(self.type))

    def visit_pointer(self):
        self.print_instance('%p')

    def visit_declarated(self):
        #print 'decl = %r' % self.type.decl_string
        decl = type_traits.remove_declarated(self.type)
        dump_decl(self.instance, decl)

    def print_instance(self, format):
        print('   _debug_printf("\\t\\t%s = %s\\n", %s);' % (self.instance, format, "(*cmd)" + self.instance))


def dump_type(instance, type_):
    type_ = type_traits.remove_alias(type_)
    visitor = type_dumper_t(instance, type_)
    algorithm.apply_visitor(visitor, type_)


def dump_struct(decls, class_):
    print('static void')
    print('dump_%s(const %s *cmd)' % (class_.name, class_.name))
    print('{')
    dump_decl('', class_)
    print('}')
    print('')


cmds = [
    ('SVGA_3D_CMD_SURFACE_DEFINE', 'SVGA3dCmdDefineSurface', (), 'SVGA3dSize'),
    ('SVGA_3D_CMD_SURFACE_DESTROY', 'SVGA3dCmdDestroySurface', (), None),
    ('SVGA_3D_CMD_SURFACE_COPY', 'SVGA3dCmdSurfaceCopy', (), 'SVGA3dCopyBox'),
    ('SVGA_3D_CMD_SURFACE_STRETCHBLT', 'SVGA3dCmdSurfaceStretchBlt', (), None),
    ('SVGA_3D_CMD_SURFACE_DMA', 'SVGA3dCmdSurfaceDMA', (), 'SVGA3dCopyBox'),
    ('SVGA_3D_CMD_CONTEXT_DEFINE', 'SVGA3dCmdDefineContext', (), None),
    ('SVGA_3D_CMD_CONTEXT_DESTROY', 'SVGA3dCmdDestroyContext', (), None),
    ('SVGA_3D_CMD_SETTRANSFORM', 'SVGA3dCmdSetTransform', (), None),
    ('SVGA_3D_CMD_SETZRANGE', 'SVGA3dCmdSetZRange', (), None),
    ('SVGA_3D_CMD_SETRENDERSTATE', 'SVGA3dCmdSetRenderState', (), 'SVGA3dRenderState'),
    ('SVGA_3D_CMD_SETRENDERTARGET', 'SVGA3dCmdSetRenderTarget', (), None),
    ('SVGA_3D_CMD_SETTEXTURESTATE', 'SVGA3dCmdSetTextureState', (), 'SVGA3dTextureState'),
    ('SVGA_3D_CMD_SETMATERIAL', 'SVGA3dCmdSetMaterial', (), None),
    ('SVGA_3D_CMD_SETLIGHTDATA', 'SVGA3dCmdSetLightData', (), None),
    ('SVGA_3D_CMD_SETLIGHTENABLED', 'SVGA3dCmdSetLightEnabled', (), None),
    ('SVGA_3D_CMD_SETVIEWPORT', 'SVGA3dCmdSetViewport', (), None),
    ('SVGA_3D_CMD_SETCLIPPLANE', 'SVGA3dCmdSetClipPlane', (), None),
    ('SVGA_3D_CMD_CLEAR', 'SVGA3dCmdClear', (), 'SVGA3dRect'),
    ('SVGA_3D_CMD_PRESENT', 'SVGA3dCmdPresent', (), 'SVGA3dCopyRect'),
    ('SVGA_3D_CMD_SHADER_DEFINE', 'SVGA3dCmdDefineShader', (), None),
    ('SVGA_3D_CMD_SHADER_DESTROY', 'SVGA3dCmdDestroyShader', (), None),
    ('SVGA_3D_CMD_SET_SHADER', 'SVGA3dCmdSetShader', (), None),
    ('SVGA_3D_CMD_SET_SHADER_CONST', 'SVGA3dCmdSetShaderConst', (), None),
    ('SVGA_3D_CMD_DRAW_PRIMITIVES', 'SVGA3dCmdDrawPrimitives', (('SVGA3dVertexDecl', 'numVertexDecls'), ('SVGA3dPrimitiveRange', 'numRanges')), 'SVGA3dVertexDivisor'),
    ('SVGA_3D_CMD_SETSCISSORRECT', 'SVGA3dCmdSetScissorRect', (), None),
    ('SVGA_3D_CMD_BEGIN_QUERY', 'SVGA3dCmdBeginQuery', (), None),
    ('SVGA_3D_CMD_END_QUERY', 'SVGA3dCmdEndQuery', (), None),
    ('SVGA_3D_CMD_WAIT_FOR_QUERY', 'SVGA3dCmdWaitForQuery', (), None),
    #('SVGA_3D_CMD_PRESENT_READBACK', None, (), None),
    ('SVGA_3D_CMD_SET_OTABLE_BASE64', 'SVGA3dCmdSetOTableBase64', (), None),
    ('SVGA_3D_CMD_DEFINE_GB_MOB64', 'SVGA3dCmdDefineGBMob64', (), None),
    ('SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN', 'SVGA3dCmdBlitSurfaceToScreen', (), 'SVGASignedRect'),
    ('SVGA_3D_CMD_DEFINE_GB_SCREENTARGET', 'SVGA3dCmdDefineGBScreenTarget', (), None),
    ('SVGA_3D_CMD_BIND_GB_SCREENTARGET', 'SVGA3dCmdBindGBScreenTarget', (), None),
    ('SVGA_3D_CMD_UPDATE_GB_SCREENTARGET', 'SVGA3dCmdUpdateGBScreenTarget', (), None),
    ('SVGA_3D_CMD_DESTROY_GB_SCREENTARGET', 'SVGA3dCmdDestroyGBScreenTarget', (), None),
    ('SVGA_3D_CMD_UPDATE_GB_IMAGE', 'SVGA3dCmdUpdateGBImage', (), None),
    ('SVGA_3D_CMD_DEFINE_GB_SURFACE', 'SVGA3dCmdDefineGBSurface', (), None),
    ('SVGA_3D_CMD_BIND_GB_SURFACE', 'SVGA3dCmdBindGBSurface', (), None),
    ('SVGA_3D_CMD_INVALIDATE_GB_SURFACE', 'SVGA3dCmdInvalidateGBSurface', (), None),
    ('SVGA_3D_CMD_DESTROY_GB_SURFACE', 'SVGA3dCmdDestroyGBSurface', (), None),
]

def dump_cmds():
    print(r'''
void
svga_dump_command(uint32_t cmd_id, const void *data, uint32_t size)
{
   const uint8_t *body = (const uint8_t *)data;
   const uint8_t *next = body + size;
''')
    print('   switch(cmd_id) {')
    indexes = 'ijklmn'
    for id, header, body, footer in cmds:
        print('   case %s:' % id)
        print('      _debug_printf("\\t%s\\n");' % id)
        print('      {')
        print('         const %s *cmd = (const %s *)body;' % (header, header))
        if len(body):
            print('         unsigned ' + ', '.join(indexes[:len(body)]) + ';')
        print('         dump_%s(cmd);' % header)
        print('         body = (const uint8_t *)&cmd[1];')
        for i in range(len(body)):
            struct, count = body[i]
            idx = indexes[i]
            print('         for(%s = 0; %s < cmd->%s; ++%s) {' % (idx, idx, count, idx))
            print('            dump_%s((const %s *)body);' % (struct, struct))
            print('            body += sizeof(%s);' % struct)
            print('         }')
        if footer is not None:
            print('         while(body + sizeof(%s) <= next) {' % footer)
            print('            dump_%s((const %s *)body);' % (footer, footer))
            print('            body += sizeof(%s);' % footer)
            print('         }')
        if id == 'SVGA_3D_CMD_SHADER_DEFINE':
            print('         //svga_shader_dump((const uint32_t *)body,')
            print('         //                 (unsigned)(next - body)/sizeof(uint32_t),')
            print('         //                 FALSE);')
            print('         body = next;')
        print('      }')
        print('      break;')
    print('   default:')
    print('      _debug_printf("\\t0x%08x\\n", cmd_id);')
    print('      break;')
    print('   }')
    print(r'''
   while(body + sizeof(uint32_t) <= next) {
      _debug_printf("\t\t0x%08x\n", *(const uint32_t *)body);
      body += sizeof(uint32_t);
   }
   while(body + sizeof(uint32_t) <= next)
      _debug_printf("\t\t0x%02x\n", *body++);
}
''')
    print(r'''
void
svga_dump_commands(const void *commands, uint32_t size)
{
   const uint8_t *next = commands;
   const uint8_t *last = next + size;

   //assert(size % sizeof(uint32_t) == 0);

   while(next < last) {
      const uint32_t cmd_id = *(const uint32_t *)next;

      if(SVGA_3D_CMD_BASE <= cmd_id && cmd_id < SVGA_3D_CMD_MAX) {
         const SVGA3dCmdHeader *header = (const SVGA3dCmdHeader *)next;
         const uint8_t *body = (const uint8_t *)&header[1];

         next = body + header->size;
         if(next > last)
            break;

         svga_dump_command(cmd_id, body, header->size);
      }
      else if(cmd_id == SVGA_CMD_FENCE) {
         _debug_printf("\tSVGA_CMD_FENCE\n");
         _debug_printf("\t\t0x%08x\n", ((const uint32_t *)next)[1]);
         next += 2*sizeof(uint32_t);
      }
      else {
         _debug_printf("\t0x%08x\n", cmd_id);
         next += sizeof(uint32_t);
      }
   }
}
''')

def main():
    print(copyright.strip())
    print()
    print('/**')
    print(' * @file')
    print(' * Dump SVGA commands.')
    print(' *')
    print(' * Generated automatically from svga3d_reg.h by svga_dump.py.')
    print(' * Modified for VirtualBox.')
    print(' */')
    print(r'''
#include "svga_types.h"
#include "vmsvga_headers_begin.h"
#pragma pack(1) /* VMSVGA structures are '__packed'. */
#include "svga3d_reg.h"
#pragma pack()
#include "vmsvga_headers_end.h"

#include "svga_dump.h"

#define LOG_GROUP LOG_GROUP_DEV_VMSVGA
#include <VBox/log.h>

#define _debug_printf(...) Log7((__VA_ARGS__));

#ifdef LOG_ENABLED''')

    generator_path, generator_name = utils.find_xml_generator()

    # Configure the xml generator
    config = parser.xml_generator_configuration_t(
        xml_generator_path = generator_path,
        xml_generator = generator_name,
        include_paths = [ '../vmsvga_include/',
                          '../../../../../include/'],
        cflags   = '-D IN_RING3 -D UTIL_ARCH_LITTLE_ENDIAN=1 -D UTIL_ARCH_BIG_ENDIAN=0',
        compiler = 'gcc'
        )

    headers = [
        'svga_types.h',
        'svga3d_reg.h',
    ]

    decls = parser.parse(headers, config, parser.COMPILATION_MODE.ALL_AT_ONCE)
    global_ns = declarations.get_global_namespace(decls)

    names = set()
    for id, header, body, footer in cmds:
        names.add(header)
        for struct, count in body:
            names.add(struct)
        if footer is not None:
            names.add(footer)

    for class_ in global_ns.classes(lambda decl: decl.name in names):
        dump_struct(decls, class_)

    dump_cmds()
    print('#endif //LOG_ENABLED')


if __name__ == '__main__':
    main()

