//
// 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

**
** JsWriter.
**
class JsWriter
{

//////////////////////////////////////////////////////////////////////////
// Construction
//////////////////////////////////////////////////////////////////////////

  **
  ** Make for specified output stream
  **
  new make(OutStream out)
  {
    this.out = out
  }

  new makeSourceMap(OutStream out, SourceMap sourcemap)
  {
    this.out = out
    this.sourcemap = sourcemap
  }

//////////////////////////////////////////////////////////////////////////
// Methods
//////////////////////////////////////////////////////////////////////////

  **
  ** Write and then return this. If loc is not null, the text will be
  ** added to the generated source map.
  **
  JsWriter w(Obj o, Loc? loc := null, Str? name := null)
  {
    if (needIndent)
    {
      spaces := indentation * 2
      out.writeChars(Str.spaces(spaces))
      col += spaces
      needIndent = false
    }
    str := o.toStr
    if (str.containsChar('\n')) throw Err("w str with newline: ${str}")
    if (loc != null)
    {
      sourcemap?.add(str, Loc(loc.file, line, col), loc, name)
    }
    out.writeChars(str)
    col += str.size
    return this
  }

  JsWriter sig(JsMethodParam[] pars)
  {
    w("(")
    pars.each |p,i|
    {
      if (i > 0) w(",")
      p.write(this)
    }
    w(")")
    return this
  }

  **
  ** Write newline and then return this.
  **
  public JsWriter nl()
  {
    out.writeChar('\n')
    ++line
    col = 0
    needIndent = true
    out.flush
    return this
  }

  **
  ** Increment the indentation.
  **
  JsWriter indent()
  {
    indentation++
    return this
  }

  **
  ** Decrement the indentation.
  **
  JsWriter unindent()
  {
    indentation--
    if (indentation < 0) indentation = 0
    return this
  }

//////////////////////////////////////////////////////////////////////////
// Minify
//////////////////////////////////////////////////////////////////////////

  **
  ** Write the minified content of the InSteam.
  **
  Void minify(InStream in)
  {
    inBlock := false
    in.readAllLines.each |line|
    {
      // TODO: temp hack for inlining already minified js
      if (line.size > 1024) { w(line).nl; return }

      s := line
      // line comments
      if (s.size > 1 && (s[0] == '/' && s[1] == '/')) return
// need to check if inside str
//      i := s.index("//")
//      if (i != null) s = s[0..<i]
      // block comments
      temp := s
      a := temp.index("/*")
      if (a != null)
      {
        s = temp[0..<a]
        inBlock = true
      }
      if (inBlock)
      {
        b := temp.index("*/")
        if (b != null)
        {
          s = (a == null) ? temp[b+2..-1] : s + temp[b+2..-1]
          inBlock = false
        }
      }
      // trim and print
      s = s.trimEnd
      if (inBlock) return
      if (s.size == 0) return
      w(s).nl
    }
  }

//////////////////////////////////////////////////////////////////////////
// Fields
//////////////////////////////////////////////////////////////////////////

  private OutStream out
  SourceMap? sourcemap
  Int indentation := 0
  Bool needIndent := false
  Int line := 0
  Int col  := 0

}