# Copyright (c) 2017 VMware, Inc.  All rights reserved. -- VMware Confidential

import xml.parsers.expat


def _Identity(x):
   return x


class BreakingChangeException(Exception):
   pass


class _AnyTranslator(object):
   def __init__(self, base):
      self.base = base
      self.srcPyVmomi = self.base.srcPyVmomi
      self.dstPyVmomi = self.base.dstPyVmomi
      self.stack = []

   def Translate(self, any):
      result = self._TranslateAny(any, type(any))

      while self.stack:
         srcTop, dstTop = self.stack.pop()
         self._TranslateDataObjectProperties(srcTop, dstTop)

      return result

   def _TranslateAny(self, any, srcType):
      # any
      if srcType == object:
         srcType = type(any)

      dstType = self.base.TRANSLATIONS.get(srcType)

      # primitive
      if dstType:
         if issubclass(srcType, self.srcPyVmomi.VmomiSupport.Array):
            dstItemType = self.base.TRANSLATIONS[srcType.Item]
            return dstType(dstItemType(item) for item in any)
         else:
            return dstType(any)
      # enum
      elif issubclass(srcType, self.srcPyVmomi.VmomiSupport.Enum):
         dstType = self.base.TranslateType(srcType)
         return dstType(any)
      # managed object reference
      elif issubclass(srcType, self.srcPyVmomi.types.ManagedObject):
         return self.base.TranslateManagedObject(any)
      # data object
      elif issubclass(srcType, self.srcPyVmomi.types.DataObject):
         return self._TranslateDataObject(any)
      # any array
      elif issubclass(srcType, self.srcPyVmomi.types.ObjectArray):
         dstType = self.dstPyVmomi.types.ObjectArray
         return dstType(self._TranslateAny(item, type(item)) for item in any)
      # array
      elif issubclass(srcType, self.srcPyVmomi.VmomiSupport.Array):
         dstType = self.base.TranslateType(srcType.Item).Array
         # enum
         if issubclass(srcType.Item, self.srcPyVmomi.VmomiSupport.Enum):
            return dstType(dstType.Item(item) for item in any)
         # managed object reference
         elif issubclass(srcType.Item, self.srcPyVmomi.types.ManagedObject):
            return dstType(self.base.TranslateManagedObject(item)
                           for item in any)
         # data object
         elif issubclass(srcType.Item, self.srcPyVmomi.types.DataObject):
            return dstType(self._TranslateDataObject(item) for item in any)

      raise Exception() # bug

   def _TranslateDataObject(self, dobj):
      srcType = type(dobj)
      dstType = self.base.TranslateType(srcType)
      result = dstType()
      self.stack.append((dobj, result))
      return result

   def _TranslateDataObjectProperties(self, srcObj, dstObj):
      processedProperties = self.base.TranslateDataObject(srcObj, dstObj)
      processedSrcProperties, processedDstProperties = processedProperties

      srcProperties = {p.name: p
                       for p in srcObj._GetPropertyList()
                       if p.name not in processedSrcProperties}
      dstProperties = {p.name: p
                       for p in dstObj._GetPropertyList()
                       if p.name not in processedDstProperties}

      for propertyName, srcProperty in srcProperties.items():
         dstProperty = dstProperties.pop(propertyName, None)
         srcValue = getattr(srcObj, propertyName)

         if srcValue is None:
            continue # unset

         if dstProperty.type != self.base.TranslateType(srcProperty.type):
            raise BreakingChangeException()

         setattr(dstObj, propertyName,
                 self._TranslateAny(srcValue, srcProperty.type))

      for propertyName, dstProperty in dstProperties.items():
         if not dstProperty.flags & self.dstPyVmomi.VmomiSupport.F_OPTIONAL:
            raise BreakingChangeException()


class AnyTranslator(object):
   def __init__(self, srcPyVmomi, dstPyVmomi):
      self.srcPyVmomi = srcPyVmomi
      self.dstPyVmomi = dstPyVmomi

      srcTypes = srcPyVmomi.VmomiSupport.types
      dstTypes = dstPyVmomi.VmomiSupport.types

      srcTextType = srcPyVmomi.VmomiSupport.six.text_type
      dstTextType = dstPyVmomi.VmomiSupport.six.text_type

      self.TRANSLATIONS = {
         type(None): _Identity,

         srcTypes.bool: dstTypes.bool,
         srcTypes.BoolArray: dstTypes.BoolArray,

         srcTypes.byte: dstTypes.byte,
         srcTypes.ByteArray: dstTypes.ByteArray,

         srcTypes.short: dstTypes.short,
         srcTypes.ShortArray: dstTypes.ShortArray,

         srcTypes.int: _Identity, # srcTypes.int == dstTypes.int
         srcTypes.IntArray: dstTypes.IntArray,

         srcTypes.long: dstTypes.long,
         srcTypes.LongArray: dstTypes.LongArray,

         srcTypes.float: _Identity, # srcTypes.float == dstTypes.float
         srcTypes.FloatArray: dstTypes.FloatArray,

         srcTypes.double: dstTypes.double,
         srcTypes.DoubleArray: dstTypes.DoubleArray,

         srcTypes.str: _Identity, # srcTypes.str == dstTypes.str
         srcTypes.StrArray: dstTypes.StrArray,

         srcTextType: dstTextType, # PR 1904488

         srcTypes.URI: dstTypes.URI,
         srcTypes.URIArray: dstTypes.URIArray,

         srcTypes.binary: dstTypes.binary,
         srcTypes.BinaryArray: dstTypes.BinaryArray,

         srcTypes.datetime: _Identity, # srcTypes.datetime == dstTypes.datetime
         srcTypes.DatetimeArray: dstTypes.DatetimeArray,

         srcTypes.Link: dstTypes.Link,
         srcTypes.LinkArray: dstTypes.LinkArray,

         srcTypes.type: self.TranslateType,
         srcTypes.TypeArray: dstTypes.TypeArray,
         srcPyVmomi.VmomiSupport.LazyType: self.TranslateType, # data objects

         srcTypes.ManagedMethod: self.TranslateManagedMethod,
         srcTypes.ManagedMethodArray: dstTypes.ManagedMethodArray,

         srcTypes.PropertyPath: dstTypes.PropertyPath,
         srcTypes.PropertyPathArray: dstTypes.PropertyPathArray,
      }

   def TranslateAny(self, any):
      return _AnyTranslator(self).Translate(any)

   def TranslateManagedMethod(self, mm):
      ns = self.srcPyVmomi.VmomiSupport.GetWsdlNamespace(mm.info.version)
      return self.dstPyVmomi.VmomiSupport.GetWsdlMethod(ns, mm.info.wsdlName)

   def TranslateManagedObject(self, mo):
      moType = self.TranslateType(type(mo))
      return moType(moId=mo._moId, serverGuid=mo._serverGuid)

   def TranslateType(self, t):
      name = self.srcPyVmomi.VmomiSupport.GetVmodlName(t)
      return self.dstPyVmomi.VmomiSupport.GetVmodlType(name)

   def TranslateDataObject(self, srcObj, dstObj):
      return [], []
