text: # Copyright 2013 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.
  
  """Generates JS source files from a mojom.Module."""
  
  import os
  import mojom
  import mojom_pack
  import mojom_generator
  
  from functools import partial
  from template_expander import UseJinja
  
  _kind_to_default_value = {
    mojom.BOOL:    "false",
    mojom.INT8:    "0",
    mojom.UINT8:   "0",
    mojom.INT16:   "0",
    mojom.UINT16:  "0",
    mojom.INT32:   "0",
    mojom.UINT32:  "0",
    mojom.FLOAT:   "0",
    mojom.HANDLE:  "core.kInvalidHandle",
    mojom.MSGPIPE: "core.kInvalidHandle",
    mojom.INT64:   "0",
    mojom.UINT64:  "0",
    mojom.DOUBLE:  "0",
    mojom.STRING:  '""',
  }
  
  
  def DefaultValue(field):
    if field.default:
      return field.default
    if field.kind in mojom.PRIMITIVES:
      return _kind_to_default_value[field.kind]
    if isinstance(field.kind, mojom.Struct):
      return "null";
    if isinstance(field.kind, mojom.Array):
      return "[]";
  
  
  def PayloadSize(packed):
    packed_fields = packed.packed_fields
    if not packed_fields:
      return 0;
    last_field = packed_fields[-1]
    offset = last_field.offset + last_field.size
    pad = mojom_pack.GetPad(offset, 8)
    return offset + pad;
  
  
  _kind_to_javascript_type = {
    mojom.BOOL:    "codec.Uint8",
    mojom.INT8:    "codec.Int8",
    mojom.UINT8:   "codec.Uint8",
    mojom.INT16:   "codec.Int16",
    mojom.UINT16:  "codec.Uint16",
    mojom.INT32:   "codec.Int32",
    mojom.UINT32:  "codec.Uint32",
    mojom.FLOAT:   "codec.Float",
    mojom.HANDLE:  "codec.Handle",
    mojom.MSGPIPE: "codec.Handle",
    mojom.INT64:   "codec.Int64",
    mojom.UINT64:  "codec.Uint64",
    mojom.DOUBLE:  "codec.Double",
    mojom.STRING:  "codec.String",
  }
  
  
  def GetJavaScriptType(kind):
    if kind in mojom.PRIMITIVES:
      return _kind_to_javascript_type[kind]
    if isinstance(kind, mojom.Struct):
      return "new codec.PointerTo(%s)" % GetJavaScriptType(kind.name)
    if isinstance(kind, mojom.Array):
      return "new codec.ArrayOf(%s)" % GetJavaScriptType(kind.kind)
    return kind
  
  
  _kind_to_decode_snippet = {
    mojom.BOOL:    "read8() & 1",
    mojom.INT8:    "read8()",
    mojom.UINT8:   "read8()",
    mojom.INT16:   "read16()",
    mojom.UINT16:  "read16()",
    mojom.INT32:   "read32()",
    mojom.UINT32:  "read32()",
    mojom.FLOAT:   "decodeFloat()",
    mojom.HANDLE:  "decodeHandle()",
    mojom.MSGPIPE: "decodeHandle()",
    mojom.INT64:   "read64()",
    mojom.UINT64:  "read64()",
    mojom.DOUBLE:  "decodeDouble()",
    mojom.STRING:  "decodeStringPointer()",
  }
  
  
  def DecodeSnippet(kind):
    if kind in mojom.PRIMITIVES:
      return _kind_to_decode_snippet[kind]
    if isinstance(kind, mojom.Struct):
      return "decodeStructPointer(%s)" % GetJavaScriptType(kind.name);
    if isinstance(kind, mojom.Array):
      return "decodeArrayPointer(%s)" % GetJavaScriptType(kind.kind);
  
  
  _kind_to_encode_snippet = {
    mojom.BOOL:    "write8(1 & ",
    mojom.INT8:    "write8(",
    mojom.UINT8:   "write8(",
    mojom.INT16:   "write16(",
    mojom.UINT16:  "write16(",
    mojom.INT32:   "write32(",
    mojom.UINT32:  "write32(",
    mojom.FLOAT:   "encodeFloat(",
    mojom.HANDLE:  "encodeHandle(",
    mojom.MSGPIPE: "encodeHandle(",
    mojom.INT64:   "write64(",
    mojom.UINT64:  "write64(",
    mojom.DOUBLE:  "encodeDouble(",
    mojom.STRING:  "encodeStringPointer(",
  }
  
  
  def EncodeSnippet(kind):
    if kind in mojom.PRIMITIVES:
      return _kind_to_encode_snippet[kind]
    if isinstance(kind, mojom.Struct):
      return "encodeStructPointer(%s, " % GetJavaScriptType(kind.name);
    if isinstance(kind, mojom.Array):
      return "encodeArrayPointer(%s, " % GetJavaScriptType(kind.kind);
  
  
  def GetStructInfo(exported, struct):
    packed_struct = mojom_pack.PackedStruct(struct)
    return {
      "name": struct.name,
      "packed": packed_struct,
      "bytes": mojom_pack.GetByteLayout(packed_struct),
      "exported": exported,
    }
  
  
  class JSGenerator(mojom_generator.Generator):
    filters = {
      "default_value": DefaultValue,
      "payload_size": PayloadSize,
      "decode_snippet": DecodeSnippet,
      "encode_snippet": EncodeSnippet,
      "stylize_method": mojom_generator.StudlyCapsToCamel,
    }
  
    def GetStructsFromMethods(self):
      result = []
      for interface in self.module.interfaces:
        for method in interface.methods:
          result.append(mojom_generator.GetStructFromMethod(interface, method))
      return map(partial(GetStructInfo, False), result)
  
    def GetStructs(self):
      return map(partial(GetStructInfo, True), self.module.structs)
  
    @UseJinja("js_templates/module.js.tmpl", filters=filters)
    def GenerateModule(self):
      return {
        "enums": self.module.enums,
        "structs": self.GetStructs() + self.GetStructsFromMethods(),
        "interfaces": self.module.interfaces,
      }
  
    def Write(self, contents):
      if self.output_dir is None:
        print contents
        return
      filename = "%s.js" % self.module.name
      with open(os.path.join(self.output_dir, filename), "w+") as f:
        f.write(contents)
  
    def GenerateFiles(self):
      self.Write(self.GenerateModule())
  