# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Nodes for PPAPI IDL AST."""

from idl_namespace import IDLNamespace
from idl_node import IDLAttribute, IDLFile, IDLNode
from idl_option import GetOption
from idl_visitor import IDLVisitor
from idl_release import IDLReleaseList, IDLReleaseMap

#
# IDL Predefined types
#
BuiltIn = set(['int8_t', 'int16_t', 'int32_t', 'int64_t', 'uint8_t',
               'uint16_t', 'uint32_t', 'uint64_t', 'double_t', 'float_t',
               'handle_t', 'interface_t', 'char', 'mem_t', 'str_t', 'void'])


#
# IDLNamespaceLabelResolver
#
# Once the AST is build, we need to resolve the namespace and version
# information.
#
class IDLNamespaceLabelResolver(IDLVisitor):
  NamespaceSet = set(['AST', 'Callspec', 'Interface', 'Member', 'Struct'])
  #
  # When we arrive at a node we must assign it a namespace and if the
  # node is named, then place it in the appropriate namespace.
  #
  def Arrive(self, node, parent_namespace):
    # If we are entering a parent, clear the local Label\
    if node.IsA('File'): self.release_map = None

    # If this object is not a namespace aware object, use the parent's one
    if node.cls not in self.NamespaceSet:
      node.namespace = parent_namespace
    else:
    # otherwise create one.
      node.namespace = IDLNamespace(parent_namespace)
      node.namespace.name = node.GetName()

    # If this node is named, place it in its parent's namespace
    if parent_namespace and node.cls in IDLNode.NamedSet:
      # Set version min and max based on properties
      if self.release_map:
        vmin = node.GetProperty('version')
        vmax = node.GetProperty('deprecate')
        rmin = self.release_map.GetRelease(vmin)
        rmax = self.release_map.GetRelease(vmax)
        node.SetReleaseRange(rmin, rmax)
      parent_namespace.AddNode(node)

    # Pass this namespace to each child in case they inherit it
    return node.namespace

  #
  # As we return from a node, if the node is a LabelItem we pass back
  # the key=value pair representing the mapping of release to version.
  # If the node is a Label take the lists of mapping and generate a
  # version map which is assigned to the Labels parent as a property.
  #
  def Depart(self, node, data, childdata):
    if node.IsA('LabelItem'):
      return (node.GetName(), node.GetProperty('VALUE'))
    if node.IsA('Label') and node.GetName() == GetOption('label'):
      try:
        self.release_map = IDLReleaseMap(childdata)
        node.parent.release_map = self.release_map
      except Exception as err:
        node.Error('Unable to build release map: %s' % str(err))
    return None


class IDLFileTypeResolver(IDLVisitor):
  def VisitFilter(self, node, data):
    return not node.IsA('Comment', 'Copyright')

  def Arrive(self, node, filenode):
    # Track the file node to update errors
    if node.IsA('File'):
      node.SetProperty('FILE', node)

    # If this node has a TYPEREF, resolve it to a version list
    typeref = node.property_node.GetPropertyLocal('TYPEREF')
    if typeref:
      node.typelist = node.parent.namespace.FindList(typeref)
      if not node.typelist:
        node.Error('Could not resolve %s.' % typeref)
    else:
      node.typelist = None
    return filenode


#
# IDLAst
#
# A specialized version of the IDLNode for containing the whole of the
# AST.  The specialized BuildTree function pulls the per file namespaces
# into the global AST namespace and checks for collisions.
#
class IDLAst(IDLNode):
  def __init__(self, children):
    IDLNode.__init__(self, 'AST', 'BuiltIn', 1, 0, children)
    self.Resolve()

  def Resolve(self):
    self.namespace = IDLNamespace(None)
    self.namespace.name = 'AST'
    IDLNamespaceLabelResolver().Visit(self, self.namespace)
    IDLFileTypeResolver().Visit(self, None)

    # Build an ordered list of all releases
    self.releases = set()
    for filenode in self.GetListOf('File'):
      self.releases |= set(filenode.release_map.GetReleases())
    self.releases = sorted(self.releases)

  def SetTypeInfo(self, name, properties):
    node = self.namespace[name]
    for prop in properties:
      node.properties[prop] = properties[prop]
