//
// Copyright (c) 2009, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
//   9 Jul 09  Andy Frank  Creation
//

using compiler

**
** JsNode translates a compiler::Node into the equivalent JavaScript
** source code.
**
abstract class JsNode
{

//////////////////////////////////////////////////////////////////////////
// Constructor
//////////////////////////////////////////////////////////////////////////

  new make(JsCompilerSupport support, Node? node := null)
  {
    this.support = support
    this.nodeRef = node
    this.loc = node?.loc
  }

  virtual Node? node() { nodeRef }
  private Node? nodeRef

  Loc? loc

//////////////////////////////////////////////////////////////////////////
// Write
//////////////////////////////////////////////////////////////////////////

  **
  ** Write the JavaScript source code for this node.
  **
  abstract Void write(JsWriter out)

//////////////////////////////////////////////////////////////////////////
// JavaScript
//////////////////////////////////////////////////////////////////////////

  **
  ** Return the JavaScript qname for this CType.
  **
  Str qnameToJs(CType ctype)
  {
    // use this method as a hook to look for synthentic types
    // used in compiled types that we need to emit
    /*
    if (ctype.isSynthetic)
    {
      if (ctype.qname.contains("Curry\$"))
      {
        list := (support.compiler as JsCompiler).synth
        if (!list.contains(ctype)) list.add(ctype)
      }
    }
    */
    /*
    else
    {
      // also use this method to verify referenced types
      // have been configured to be compiled to js as well
      if (!Type.find(ctype.qname).facet(@js, false))
        support.err("Type not available in JavaScript: $ctype.qname")
    }
    */


    js := "fan.${ctype.pod.name}.$ctype.name"

    // make it so java FFI calls parse in js runtimes
    // code will parse but fail if actually invoked
    if (js.contains(".[java].")) js = js.replace(".[java].", ".")
    else if (js.contains("[java]")) js = js.replace("[java]", "")

    return js
  }

  **
  ** Return the JavaScript variable name for the given Fan
  ** variable name.
  **
  Str vnameToJs(Str name)
  {
    if (vnames.get(name, false)) return "\$$name";
    return name;
  }

  // must keep in sync with fan.sys.Slot.prototype.$$name
  private const Str:Bool vnames :=
  [
    "char":   true,
    "delete": true,
    "enum":   true,
    "export": true,
    "fan":    true,
    "float":  true,
    "import": true,
    "in":     true,
    "int":    true,
    "name":   true,
    "self":   true,
    "typeof": true,
    "var":    true,
    "with":   true
  ].toImmutable

  **
  ** Return true if the type is a primitive type:
  **  - Bool
  **  - Decimal
  **  - Float
  **  - Int
  **  - Num
  **  - Str
  **
  Bool isPrimitive(CType ctype) { return pmap.get(ctype.qname, false) }
  const Str:Bool pmap :=
  [
    "sys::Bool":    true,
    "sys::Decimal": true,
    "sys::Float":   true,
    "sys::Int":     true,
    "sys::Num":     true,
    "sys::Str":     true
  ]
/*
  **
  ** The name of the 'this' var.
  **
  Str thisName
  {
    get { Actor.locals["compilerJs.this"] ?: "this" }
    set { Actor.locals["compilerJs.this"] = it }
  }

  **
  ** Return a unique identifier name.
  **
  Str unique()
  {
    Int id := Actor.locals["compilerJs.lastId"] ?: 0
    Actor.locals["compilerJs.lastId"] = id + 1
    return "\$_u$id"
  }
*/
  JsCompilerSupport support

}