# RTEMS Tools Project (http://www.rtems.org/)
# Copyright 2010-2014 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#

#
# RTEMS Objects Support
#

import gdb
import itertools
import re


class infotables():
    """Manage the object information tables."""

    tables_types = {
        'internal/time': ('TOD_Control', '_TOD'),
        'internal/wdticks': ('Chain_Control', '_Watchdog_Ticks_chain'),
        'internal/wdseconds': ('Chain_Control', '_Watchdog_Seconds_chain'),
        'classic/tasks': ('Thread_Control', '_RTEMS_tasks_Information'),
        'classic/timers': ('Timer_Control', '_Timer_Information'),
        'classic/semaphores': ('Semaphore_Control', '_Semaphore_Information'),
        'classic/message_queues':
        ('Message_queue_Control', '_Message_queue_Information'),
        'classic/partitions': ('Partition_Control', '_Partition_Information'),
        'classic/regions': ('Region_Control', '_Region_Information'),
        'classic/ports': ('Port_Control', '_Port_Information'),
        'classic/periods': ('Period_Control', '_Period_Information'),
        'classic/extensions': ('Extension_Control', '_Extension_Information'),
        'classic/barriers': ('Barrier_Control', '_Barrier_Information')
    }

    def __init__(self):
        self.invalidate()

    def invalidate(self):
        self.tables = {}

    def name(self, api, _class):
        return '%s/%s' % (api, _class)

    def load(self, n):
        if n in self.tables_types:
            if n not in self.tables:
                self.tables[n] = gdb.parse_and_eval(self.tables_types[n][1])

    def get(self, api, _class):
        n = self.name(api, _class)
        self.load(n)
        if n in self.tables:
            return self.tables[n]
        return None

    def minimum_id(self, api, _class):
        n = self.name(api, _class)
        self.load(n)
        return int(self.tables[n]['minimum_id'])

    def maximum_id(self, api, _class):
        n = self.name(api, _class)
        self.load(n)
        return int(self.tables[n]['maximum_id'])

    def maximum(self, api, _class):
        n = self.name(api, _class)
        self.load(n)
        return int(self.tables[n]['maximum'])

    def object(self, id):
        if type(id) == gdb.Value:
            id = ident(id)
        if type(id) == tuple:
            api = id[0]
            _class = id[1]
            index = id[2]
        else:
            api = id.api()
            _class = id._class()
            index = id.index()
        return self.object_return(api, _class, index)

    def object_return(self, api, _class, index=-1):
        n = self.name(api, _class)
        self.load(n)
        table_type = self.tables_types[n]
        if api == 'internal':
            expr = '(%s) %s' % (table_type[0], table_type[1])
        else:
            max = self.maximum(api, _class)
            if index > max:
                raise IndexError('object index out of range (%d)' % (max))
            expr = '(%s*) %s.local_table[%d]' % (table_type[0], table_type[1],
                                                 index)
        return gdb.parse_and_eval(expr)

    def is_string(self, api, _class):
        n = self.name(api, _class)
        self.load(n)
        if n in self.tables:
            if self.tables[n]['is_string']:
                return True
        return False


#
# Global info tables. These are global in the target.
#
information = infotables()


class ident():
    "An RTEMS object id with support for its bit fields."

    bits = [{
        'index': (0, 15),
        'node': (0, 0),
        'api': (8, 10),
        'class': (11, 15)
    }, {
        'index': (0, 15),
        'node': (16, 23),
        'api': (24, 26),
        'class': (27, 31)
    }]

    OBJECT_16_BITS = 0
    OBJECT_32_BITS = 1

    api_labels = ['none', 'internal', 'classic', 'posix']

    class_labels = {
        'internal': ('threads', 'mutexes'),
        'classic': ('none', 'tasks', 'timers', 'semaphores', 'message_queues',
                    'partitions', 'regions', 'ports', 'periods', 'extensions',
                    'barriers'),
        'posix':
        ('none', 'threads', 'keys', 'interrupts', 'message_queue_fds',
         'message_queues', 'mutexes', 'semaphores', 'condition_variables',
         'timers', 'barriers', 'spinlocks', 'rwlocks'),
    }

    def __init__(self, id):
        if type(id) != gdb.Value and type(id) != int and type(id) != unicode:
            raise TypeError('%s: must be gdb.Value, int, unicoded int' %
                            (type(id)))
        if type(id) == int:
            id = gdb.Value(id)
        self.id = id
        if self.id.type.sizeof == 2:
            self.idSize = self.OBJECT_16_BITS
        else:
            self.idSize = self.OBJECT_32_BITS

    def get(self, field):
        if field in self.bits[self.idSize]:
            bits = self.bits[self.idSize][field]
            if bits[1] > 0:
                return (int(self.id) >> bits[0]) & (
                    (1 << (bits[1] - bits[0] + 1)) - 1)
        return 0

    def value(self):
        return int(self.id)

    def index(self):
        return self.get('index')

    def node(self):
        return self.get('node')

    def api_val(self):
        return self.get('api')

    def class_val(self):
        return self.get('class')

    def api(self):
        api = self.api_val()
        if api < len(self.api_labels):
            return self.api_labels[api]
        return 'none'

    def _class(self):
        api = self.api()
        if api == 'none':
            return 'invalid'
        _class = self.class_val()
        if _class < len(self.class_labels[api]):
            return self.class_labels[api][_class]
        return 'invalid'

    def valid(self):
        return self.api() != 'none' and self._class() != 'invalid'


class name():
    """The Objects_Name can either be told what the name is or can take a
    guess."""

    def __init__(self, name, is_string=None):
        self.name = name
        if is_string == None:
            self.is_string = 'auto'
            try:
                self.name_p = self.name['name_p']
            except gdb.Error:
                self.is_string = 'no'
        else:
            if is_string:
                self.is_string = 'yes'
            else:
                self.is_string = 'no'

    def __str__(self):
        return self.get()

    def get(self):
        if self.is_string != 'yes':
            u32 = int(self.name['name_u32'])
            if u32 != 0:
                s = chr((u32 >> 24) & 0xff) + \
                    chr((u32 >> 16) & 0xff) + \
                    chr((u32 >> 8) & 0xff) + \
                    chr(u32 & 0xff)
                for c in range(0, 4):
                    if s[c] < ' ' or s[c] > '~':
                        s = None
                        break
                if s:
                    return s
            if self.is_string == 'xno':
                return None
        try:
            name_p = self.name['name_p']
            return str(name_p.dereference())
        except gdb.Error:
            pass
        return None


class control():
    """The Objects_Control structure."""

    def __init__(self, object):
        self.object = object
        self._id = ident(self.object['id'])
        self._name = name(
            self.object['name'],
            information.is_string(self._id.api(), self._id._class()))

    def node(self):
        return self.object['Node']

    def id(self):
        return self.object['id']

    def name(self):
        return self._name.get()
