/*
 * Copyright (c) 1998-2012 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Resin Open Source is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.quercus.program;

import com.caucho.quercus.QuercusContext;
import com.caucho.quercus.expr.VarInfo;
import com.caucho.quercus.env.StringValue;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;

/**
 * Information about a function.
 */
public class FunctionInfo
{
  private final QuercusContext _quercus;

  private final ClassDef _classDef;
  private final String _name;

  private final HashMap<StringValue,VarInfo> _varMap
    = new HashMap<StringValue,VarInfo>();

  private final ArrayList<String> _tempVarList
    = new ArrayList<String>();

  private Function _fun;

  private boolean _hasThis; // if true, override default
  private boolean _isGlobal;
  private boolean _isClosure;
  private boolean _isConstructor;
  private boolean _isStaticClassMethod;

  private boolean _isPageMain;
  private boolean _isPageStatic;

  private boolean _isReturnsReference;
  private boolean _isVariableVar;
  private boolean _isOutUsed;

  private boolean _isVariableArgs;
  private boolean _isUsesSymbolTable;
  private boolean _isUsesGlobal;

  private boolean _isReadOnly = true;

  public FunctionInfo(QuercusContext quercus, ClassDef classDef, String name)
  {
    _quercus = quercus;
    _classDef = classDef;
    _name = name;
  }

  public FunctionInfo copy()
  {
    FunctionInfo copy = createCopy();

    copy._varMap.putAll(_varMap);
    copy._tempVarList.addAll(_tempVarList);
    copy._fun = _fun;
    copy._hasThis = _hasThis;
    copy._isGlobal = _isGlobal;
    copy._isClosure = _isClosure;
    copy._isConstructor = _isConstructor;
    copy._isPageMain = _isPageMain;
    copy._isPageStatic = _isPageStatic;
    copy._isReturnsReference = _isReturnsReference;
    copy._isVariableVar = _isVariableVar;
    copy._isOutUsed = _isOutUsed;
    copy._isVariableArgs = _isVariableArgs;
    copy._isUsesSymbolTable = _isUsesSymbolTable;
    copy._isReadOnly = _isReadOnly;

    return copy;
  }

  protected FunctionInfo createCopy()
  {
    return new FunctionInfo(_quercus, _classDef, _name);
  }

  /**
   * Returns the owning quercus.
   */
  public QuercusContext getQuercus()
  {
    return _quercus;
  }

  public String getName()
  {
    return _name;
  }

  /**
   * Returns the actual function.
   */
  public Function getFunction()
  {
    return _fun;
  }

  /**
   * Sets the actual function.
   */
  public void setFunction(Function fun)
  {
    _fun = fun;
  }

  /**
   * True for a global function (top-level script).
   */
  public boolean isGlobal()
  {
    return _isGlobal;
  }

  /**
   * True for a global function.
   */
  public void setGlobal(boolean isGlobal)
  {
    _isGlobal = isGlobal;
  }

  /**
   * True for a closure.
   */
  public void setClosure(boolean isClosure)
  {
    _isClosure = isClosure;
  }

  /**
   * True for a closure function (top-level script).
   */
  public boolean isClosure()
  {
    return _isClosure;
  }

  /*
   * True for a final function.
   */
  public boolean isFinal()
  {
    return _fun.isFinal();
  }

  /**
   * True for a main function (top-level script).
   */
  public boolean isPageMain()
  {
    return _isPageMain;
  }

  /**
   * True for a main function (top-level script).
   */
  public void setPageMain(boolean isPageMain)
  {
    _isPageMain = isPageMain;
  }

  /**
   * True for a static function (top-level script).
   */
  public boolean isPageStatic()
  {
    return _isPageStatic;
  }

  /**
   * True for a static function (top-level script).
   */
  public void setPageStatic(boolean isPageStatic)
  {
    _isPageStatic = isPageStatic;
  }

  public void setHasThis(boolean hasThis)
  {
    _hasThis = hasThis;
  }

  /**
   * Return true if the function allows $this
   */
  public boolean hasThis()
  {
    // php/396z
    // return _hasThis || (_classDef != null && ! _fun.isStatic());
    return _hasThis || _classDef != null && ! _isStaticClassMethod;
  }

  /**
   * Gets the owning class.
   */
  public ClassDef getDeclaringClass()
  {
    return _classDef;
  }

  /**
   * True for a method.
   */
  public boolean isMethod()
  {
    return _classDef != null;
  }

  /**
   * True for a static class method.
   */
  public boolean isStaticClassMethod()
  {
    return _isStaticClassMethod;
  }

  /**
   * True for a static class method.
   */
  public void setStaticClassMethod(boolean isStaticClassMethod)
  {
    _isStaticClassMethod = isStaticClassMethod;
  }

  /**
   * True for a constructor
   */
  public boolean isConstructor()
  {
    return _isConstructor;
  }

  /**
   * True for a constructor.
   */
  public void setConstructor(boolean isConstructor)
  {
    _isConstructor = isConstructor;
  }

  /**
   * True if the function returns a reference.
   */
  public boolean isReturnsReference()
  {
    return _isReturnsReference;
  }

  /**
   * True if the function returns a reference.
   */
  public void setReturnsReference(boolean isReturnsReference)
  {
    _isReturnsReference = isReturnsReference;
  }

  /**
   * True if the function has variable vars.
   */
  public boolean isVariableVar()
  {
    return _isVariableVar;
  }

  /**
   * True if the function has variable vars
   */
  public void setVariableVar(boolean isVariableVar)
  {
    _isVariableVar = isVariableVar;
  }

  /**
   * True if the function has variable numbers of arguments
   */
  public boolean isVariableArgs()
  {
    return _isVariableArgs;
  }

  /**
   * True if the function has variable numbers of arguments
   */
  public void setVariableArgs(boolean isVariableArgs)
  {
    _isVariableArgs = isVariableArgs;
  }

  /**
   * True if the function uses the symbol table
   */
  public boolean isUsesSymbolTable()
  {
    return _isUsesSymbolTable;
  }

  /**
   * True if the function uses the symbol table
   */
  public void setUsesSymbolTable(boolean isUsesSymbolTable)
  {
    _isUsesSymbolTable = isUsesSymbolTable;
  }

  /**
   * True if the global statement is used.
   */
  public boolean isUsesGlobal()
  {
    return _isUsesGlobal;
  }

  /**
   * True if the global statement is used.
   */
  public void setUsesGlobal(boolean isUsesGlobal)
  {
    _isUsesGlobal = isUsesGlobal;
  }

  /**
   * Returns true if the out is used.
   */
  public boolean isOutUsed()
  {
    return _isOutUsed;
  }

  /**
   * Set true if the out is used.
   */
  public void setOutUsed()
  {
    _isOutUsed = true;
  }

  /**
   * Returns true for a read-only function, i.e. no values are changed.
   */
  public boolean isReadOnly()
  {
    return _isReadOnly;
  }

  /**
   * True for a non-read-only function
   */
  public void setModified()
  {
    _isReadOnly = false;
  }

  /**
   * Returns the variable.
   */
  public VarInfo createVar(StringValue name)
  {
    VarInfo var = _varMap.get(name);

    if (var == null) {
      var = createVarInfo(name);

      _varMap.put(name, var);
    }

    return var;
  }

  protected VarInfo createVarInfo(StringValue name)
  {
    return new VarInfo(name, this);
  }

  /**
   * Returns the variables.
   */
  public Collection<VarInfo> getVariables()
  {
    return _varMap.values();
  }

  /**
   * Adds a temp variable.
   */
  public void addTempVar(String name)
  {
    if (! _tempVarList.contains(name))
      _tempVarList.add(name);
  }

  /**
   * Returns the temp variables.
   */
  public Collection<String> getTempVariables()
  {
    return _tempVarList;
  }

  public int getTempIndex()
  {
    return _tempVarList.size();
  }

  public String createTempVar()
  {
    String name = "q_temp_" + getTempIndex();

    _tempVarList.add(name);

    return name;
  }

  public String toString()
  {
    return "FunctionInfo[" + _name + "]";
  }

}

