/*
 * Copyright (c) 1998-2008 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.Quercus;
import com.caucho.quercus.expr.VarInfo;

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

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

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

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

  private ClassDef _classDef;
  private Function _fun;

  private boolean _hasThis; // if true, override default
  private boolean _isGlobal;
  
  private boolean _isPageMain;
  private boolean _isPageStatic;
  
  private boolean _isReturnsReference;
  private boolean _isVariableVar;
  private boolean _isOutUsed;

  private boolean _isVariableArgs;
  private boolean _isUsesSymbolTable;

  private boolean _isReadOnly = true;

  public FunctionInfo(Quercus quercus, String name)
  {
    _quercus = quercus;
    _name = name;
  }

  public FunctionInfo copy()
  {
    FunctionInfo copy = new FunctionInfo(_quercus, _name);

    copy._varMap.putAll(_varMap);
    copy._tempVarList.addAll(_tempVarList);
    copy._classDef = _classDef;
    copy._fun = _fun;
    copy._hasThis = _hasThis;
    copy._isGlobal = _isGlobal;
    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;
  }

  /**
   * Returns the owning quercus.
   */
  public Quercus getPhp()
  {
    return _quercus;
  }

  public String getName()
  {
    return _name;
  }

  /**
   * 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 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()
  {
    return _hasThis || (_classDef != null && ! _fun.isStatic());
  }
  
  /**
   * Sets the owning class.
   */
  public void setDeclaringClass(ClassDef classDef)
  {
    _classDef = classDef;
  }

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

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

  /**
   * 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;
  }

  /**
   * 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(String name)
  {
    VarInfo var = _varMap.get(name);

    if (var == null) {
      var = new VarInfo(name, this);

      if (_isGlobal)
	var.setGlobal();

      if (Quercus.isSuperGlobal(name))
	var.setGlobal();
      
      _varMap.put(name, var);
    }

    return var;
  }

  /**
   * 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 toString()
  {
    return "FunctionInfo[" + _name + "]";
  }

}

