package com.tibbo.aggregate.common.binding;

import java.text.MessageFormat;
import java.util.List;

import com.tibbo.aggregate.common.Cres;
import com.tibbo.aggregate.common.Log;
import com.tibbo.aggregate.common.device.DisconnectionException;
import com.tibbo.aggregate.common.expression.ExpressionUtils;
import com.tibbo.aggregate.common.expression.Reference;
import com.tibbo.aggregate.common.util.ErrorCollector;
import com.tibbo.aggregate.common.util.SyntaxErrorException;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.log4j.Level;

public abstract class AbstractBindingProvider<T> implements BindingProvider<T>
{

  private ErrorCollector errorCollector;
  
  public AbstractBindingProvider()
  {
    super();
  }
  
  public AbstractBindingProvider(ErrorCollector errorCollector)
  {
    super();
    this.errorCollector = errorCollector;
  }
  
  @Override
  public void processExecution(int method, Binding binding, EvaluationOptions options, Reference cause, Object result)
  {
    if (Log.BINDINGS.isDebugEnabled())
    {
      Log.BINDINGS.debug(buildExecutionMessage(method, binding, options, cause, result));
    }
  }
  
  protected String buildExecutionMessage(int method, Binding binding, EvaluationOptions options, Reference cause, Object result)
  {
    String res = "";
    switch (method)
    {
      case EvaluationOptions.STARTUP:
        res = "Evaluating '" + binding.getExpression() + "' on startup and writing result (" + result + ") into '" + binding.getTarget() + "'";
        break;
      case EvaluationOptions.EVENT:
        res = "Change of '" + cause + "' caused evaluation of '" + binding.getExpression() + "' and writing result (" + result + ") into '" + binding.getTarget() + "'";
        break;
      case EvaluationOptions.PERIODIC:
        res = "Periodical evaluation of '" + binding.getExpression() + "' caused writing result (" + result + ") into '" + binding.getTarget() + "'";
        break;
    }
    return res;
  }
  
  protected String buildErrorMessage(Binding binding, int method, Reference cause, Exception error)
  {
    return MessageFormat.format(Cres.get().getString("binBindingError"), binding) + error.getMessage();
  }
  
  @Override
  public void processError(Binding binding, int method, Reference cause, Exception error)
  {
    String message = buildErrorMessage(binding, method, cause, error);
    if (errorCollector != null)
    {
      errorCollector.addError(new BindingException(message, error));
    }
    else
    {
      boolean disconnected = ExceptionUtils.indexOfType(error, DisconnectionException.class) != -1;
      if (disconnected)
      {
        if (Log.BINDINGS.isDebugEnabled())
        {
          Log.BINDINGS.log(Level.DEBUG, message, error);
        }
      }
      else
      {
        Log.BINDINGS.log(Level.ERROR, message);
        if (Log.BINDINGS.isDebugEnabled())
        {
          Log.BINDINGS.log(Level.DEBUG, error);
        }
      }
    }
  }
  
  public List<Reference> getReferences(Binding binding) throws BindingException
  {
    try
    {
      return ExpressionUtils.findReferences(binding.getExpression());
    }
    catch (SyntaxErrorException ex)
    {
      throw new BindingException(ex.getMessage(), ex);
    }
  }
}
