/*
 * Copyright 2013 Guidewire Software, Inc.
 */

package gw.internal.gosu.ir.transform;

import gw.internal.gosu.parser.DynamicFunctionSymbol;
import gw.internal.gosu.parser.TypeVariableType;
import gw.lang.ir.IRSymbol;
import gw.lang.ir.IRType;
import gw.lang.parser.Keyword;
import gw.util.Stack;

import java.util.List;

/**
 */
public class FunctionBodyTransformationContext
{
  public static final String TEMP_VAR_PREFIX = "*temp";
  private int _tempVarCount = 0;

  private Stack<IRScope> _scopes;
  private ConstructorState _constructorState;

  protected TopLevelTransformationContext _context;
  private boolean _isStatic;

  public FunctionBodyTransformationContext(TopLevelTransformationContext context, boolean isStatic) {
    _context = context;
    _isStatic = isStatic;
  }

  private enum ConstructorState {
    PRE_SUPER,
    SUPER_INVOKED,
    CLEAR
  }

  public void updateSuperInvokedAfterLastExpressionCompiles()
  {
    if( _constructorState == ConstructorState.SUPER_INVOKED )
    {
      _constructorState = ConstructorState.CLEAR;
    }
  }

  public boolean hasSuperBeenInvoked()
  {
    return _constructorState == ConstructorState.CLEAR;
  }

  public void markSuperInvoked()
  {
    _constructorState = ConstructorState.SUPER_INVOKED;
  }

  public IRSymbol makeAndIndexTempSymbol( IRType type )
  {
    return makeAndIndexTempSymbol( null, type );
  }
  public IRSymbol makeAndIndexTempSymbol( String strNameSuffix, IRType type )
  {
    String strName = strNameSuffix != null ? TEMP_VAR_PREFIX + strNameSuffix : TEMP_VAR_PREFIX + _tempVarCount++;
    IRSymbol symbol = new IRSymbol( strName, type, true);
    _scopes.peek().addSymbol( symbol );
    return symbol;
  }

  public String makeTempSymbolName( )
  {
    String strName = TEMP_VAR_PREFIX + _tempVarCount;
    _tempVarCount ++;
    return strName;
  }

  public void pushScope( boolean bInitialInstanceMethodScope )
  {
    if( _scopes == null )
    {
      _scopes = new Stack<IRScope>();
    }
    IRScope parent = _scopes.isEmpty() ? null : _scopes.peek();
    _scopes.push( new IRScope( parent ) );
    if( bInitialInstanceMethodScope )
    {
      assert parent == null;
      _scopes.peek().addSymbol( Keyword.KW_this.getName(), _context.getIRTypeForCurrentClass() );
    }
  }

  public void popScope()
  {
    _scopes.pop();
  }

  public IRSymbol getTypeParamIndex( TypeVariableType type )
  {
    return _scopes.peek().getSymbol( AbstractElementTransformer.TYPE_PARAM_PREFIX + type.getRelativeName() );
  }

  public IRSymbol getSymbol(String symbolName) {
    IRSymbol symbol = _scopes.peek().getSymbol( symbolName );
    if (symbol == null) {
      throw new IllegalStateException("No symbol found named " + symbolName);
    }
    return symbol;
  }

  public boolean hasSymbol(String symbolName)
  {
    return _scopes.peek().getSymbol( symbolName ) != null;
  }

  public void putSymbols(List<IRSymbol> symbols) {
    for (IRSymbol symbol : symbols) {
      putSymbol(symbol);
    }
  }

  public void putSymbol(IRSymbol symbol) {
    _scopes.peek().addSymbol(symbol);
  }

  public IRSymbol createSymbol(String name, IRType type) {
    return _scopes.peek().addSymbol(name, type);
  }

  public Stack<IRScope> getScopes()
  {
    return _scopes;
  }

  public boolean isBlockInvoke()
  {
    return false;
  }

  public DynamicFunctionSymbol getCurrentDFS()
  {
    return null;
  }

  public boolean isStatic() {
    return _isStatic;
  }
}
