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

from conditions import *

"""Nodes for scons to gn

Nodes class provides a means to store, explore and write out a tree
describing the table generated by interating over the scons file.  The
tree is created in the form of:

  TopNode
    Condition
      Object
        Condition
          Property

"""


REMAP = {
  'arm' : 'arm',
  'AND' : 'android',
  'CHR' : 'chrome',
  'LIN' : 'linux',
  'MAC' : 'mac',
  'IOS' : 'ios',
  'WIN' : 'win',
  'x86' : 'x86',
  'x64' : 'x64',
  'glibc' : 'glibc',
  'newlib' : 'newlib',
  'pnacl' : 'pnacl'
}

def Express(use, avail, tag):
  use = sorted([REMAP[x] for x in use])
  avail = sorted([REMAP[x] for x in avail])
  if use == avail:
    return None

  if len(use) == 1:
    return '(%s == "%s")' % (tag, use[0])

  luse = len(use)
  lavail = len(avail)
  if luse > (lavail - luse):
    items = ['%s != "%s"' % (tag, i) for i in avail if i not in use]
    return '(' + ' || '.join(items) + ')'

  items = ['%s == "%s"' % (tag, i) for i in use]
  return '(' + ' || '.join(items) + ')'


def Condition(os_use, os_avail, cpu_use, cpu_avail):
  # We can not hash a list, so we sometimes use  a space sperated string.
  if isinstance(cpu_use, str):
    cpu_use = cpu_use.split(' ')
  if isinstance(cpu_avail, str):
    cpu_avail = cpu_avail.split(' ')

  o_cond = Express(os_use, os_avail, 'os')
  c_cond = Express(cpu_use, cpu_avail, 'cpu_arch')

  if not o_cond and not c_cond:
    return None

  if o_cond and c_cond:
    return 'if (%s && %s) {' % (o_cond, c_cond)

  if o_cond:
    return 'if %s {' % o_cond
  return 'if %s {' % c_cond

#
# TopNode
#   Condition
#     Object
#       Condition
#         Property
#
class Node(object):
  def __init__(self, name=''):
    self.children = []
    self.name = name
    self.parent = None

  def DumpInfo(self, depth=0):
    print '%s%s(%s)' % ('  ' * depth, str(type(self)), self.name)
    for child in self.children:
      child.DumpInfo(depth+1)

  def Write(self, fileobj, depth, text):
    for line in text.split('\n'):
      string = '  ' * depth + line + '\n'
      fileobj.write(string)

  def Dump(self, fileobj, depth):
    adjust = self.DumpStart(fileobj, depth)
    for idx, child in enumerate(self.children):
      self.DumpChild(fileobj, child, adjust)
      if idx != len(self.children) - 1:
        fileobj.write('\n')
    self.DumpEnd(fileobj, depth)

  def DumpStart(self, fileobj, depth):
    self.Write(fileobj, depth, self.name)
    return depth

  def DumpEnd(self, fileobj, depth):
    pass

  def DumpChild(self, fileobj, child, depth):
    child.Dump(fileobj, depth)

  def AddChild(self, child):
    self.children.append(child)
    child.parent = self

  def Examine(self, obj):
    obj.Enter(self)
    for child in self.children:
      child.Examine(obj)
    obj.Exit(self)


class TopNode(Node):
  def __init__(self, name):
    Node.__init__(self, name)

  def DumpStart(self, fileobj, depth):
    self.Write(fileobj, depth, "#  Autogenerated from %s.\n\n" % self.name)
    return depth


class ConditionNode(Node):
  def __init__(self, os_use, os_avail, cpu_use, cpu_avail):
    name = Condition(os_use, os_avail, cpu_use, cpu_avail)
    Node.__init__(self, name)

  def Dump(self, fileobj, depth):
    if self.name:
      self.Write(fileobj, depth, self.name)
      depth += 1
    for child in self.children:
      child.Dump(fileobj, depth)
    if self.name:
      self.Write(fileobj, depth - 1, '}')


class ObjectNode(Node):
  def __init__(self, name, obj_type):
    Node.__init__(self, name)
    self.obj_type = obj_type
    self.conditional = set()
    self.unconditional = set()

  def DumpStart(self, fileobj, depth):
    self.Write(fileobj, depth, '%s("%s") {' % (self.obj_type, self.name))
    depth += 1

    # For every conditional only property, set and empty array
    for cond in self.conditional:
      if cond not in self.unconditional:
        self.Write(fileobj, depth, '%s = []' % cond)
    return depth

  def DumpEnd(self, fileobj, depth):
    self.Write(fileobj, depth, '}')


class PropertyNode(Node):
  def __init__(self, name):
    Node.__init__(self, name)

  def Dump(self, fileobj, depth):
    if self.parent.name:
      self.Write(fileobj, depth, '%s += [' % self.name)
    else:
      self.Write(fileobj, depth, '%s = [' % self.name)

    for child in self.children:
      child.Dump(fileobj, depth + 1)

    self.Write(fileobj, depth, ']')


class ValueNode(Node):
  def __init__(self, name):
    Node.__init__(self,  name)

  def Dump(self, fileobj, depth):
    self.Write(fileobj, depth, '"%s",' % self.name)


class OrganizeProperties(object):
  def __init__(self):
    self.cond = None
    self.obj = None
    pass

  def Enter(self, node):
    if isinstance(node, ObjectNode):
      self.obj = node

    if isinstance(node, ConditionNode):
      self.cond = node.name

    if isinstance(node, PropertyNode):
      if self.cond == None:
        self.obj.unconditional |= set([node.name])
      else:
        self.obj.conditional |= set([node.name])
      node.children = sorted(node.children, key=lambda x: x.name)

  def Exit(self, node):
    pass
