package org.eclipse.xtend.lib.annotations;

import com.google.common.annotations.Beta;
import com.google.common.base.Objects;
import org.eclipse.xtend.lib.annotations.Data;
import org.eclipse.xtend.lib.annotations.ToString;
import org.eclipse.xtend.lib.annotations.ToStringConfiguration;
import org.eclipse.xtend.lib.macro.AbstractClassProcessor;
import org.eclipse.xtend.lib.macro.TransformationContext;
import org.eclipse.xtend.lib.macro.declaration.AnnotationReference;
import org.eclipse.xtend.lib.macro.declaration.ClassDeclaration;
import org.eclipse.xtend.lib.macro.declaration.FieldDeclaration;
import org.eclipse.xtend.lib.macro.declaration.MethodDeclaration;
import org.eclipse.xtend.lib.macro.declaration.MutableClassDeclaration;
import org.eclipse.xtend.lib.macro.declaration.MutableFieldDeclaration;
import org.eclipse.xtend.lib.macro.declaration.MutableMethodDeclaration;
import org.eclipse.xtend.lib.macro.declaration.TypeReference;
import org.eclipse.xtend2.lib.StringConcatenationClient;
import org.eclipse.xtext.xbase.lib.Extension;
import org.eclipse.xtext.xbase.lib.Functions.Function1;
import org.eclipse.xtext.xbase.lib.IterableExtensions;
import org.eclipse.xtext.xbase.lib.Procedures.Procedure1;
import org.eclipse.xtext.xbase.lib.Pure;
import org.eclipse.xtext.xbase.lib.util.ToStringBuilder;

/**
 * @since 2.7
 * @noextend
 * @noreference
 */
@Beta
@SuppressWarnings("all")
public class ToStringProcessor extends AbstractClassProcessor {
  /**
   * @since 2.7
   * @noextend
   * @noreference
   */
  @Beta
  public static class Util {
    @Extension
    private TransformationContext context;
    
    public Util(final TransformationContext context) {
      this.context = context;
    }
    
    public boolean hasToString(final ClassDeclaration it) {
      MethodDeclaration _findDeclaredMethod = it.findDeclaredMethod("toString");
      return (_findDeclaredMethod != null);
    }
    
    public ToStringConfiguration getToStringConfig(final ClassDeclaration it) {
      ToStringConfiguration _xblockexpression = null;
      {
        final AnnotationReference anno = it.findAnnotation(this.context.findTypeGlobally(ToString.class));
        ToStringConfiguration _xifexpression = null;
        if ((anno == null)) {
          _xifexpression = null;
        } else {
          _xifexpression = new ToStringConfiguration(anno);
        }
        _xblockexpression = _xifexpression;
      }
      return _xblockexpression;
    }
    
    public void addReflectiveToString(final MutableClassDeclaration cls, final ToStringConfiguration config) {
      final Procedure1<MutableMethodDeclaration> _function = (MutableMethodDeclaration it) -> {
        this.context.setPrimarySourceElement(it, this.context.getPrimarySourceElement(cls));
        it.setReturnType(this.context.getString());
        it.addAnnotation(this.context.newAnnotationReference(Override.class));
        it.addAnnotation(this.context.newAnnotationReference(Pure.class));
        StringConcatenationClient _client = new StringConcatenationClient() {
          @Override
          protected void appendTo(StringConcatenationClient.TargetStringConcatenation _builder) {
            _builder.append("return new ");
            _builder.append(ToStringBuilder.class);
            _builder.append("(this)");
            _builder.newLineIfNotEmpty();
            _builder.append("\t");
            _builder.append(".addAllFields()");
            _builder.newLine();
            _builder.append("\t");
            {
              boolean _isSkipNulls = config.isSkipNulls();
              if (_isSkipNulls) {
                _builder.append(".skipNulls()");
              }
            }
            _builder.newLineIfNotEmpty();
            _builder.append("\t");
            {
              boolean _isSingleLine = config.isSingleLine();
              if (_isSingleLine) {
                _builder.append(".singleLine()");
              }
            }
            _builder.newLineIfNotEmpty();
            _builder.append("\t");
            {
              boolean _isHideFieldNames = config.isHideFieldNames();
              if (_isHideFieldNames) {
                _builder.append(".hideFieldNames()");
              }
            }
            _builder.newLineIfNotEmpty();
            _builder.append("\t");
            {
              boolean _isVerbatimValues = config.isVerbatimValues();
              if (_isVerbatimValues) {
                _builder.append(".verbatimValues()");
              }
            }
            _builder.newLineIfNotEmpty();
            _builder.append("\t");
            _builder.append(".toString();");
            _builder.newLine();
          }
        };
        it.setBody(_client);
      };
      cls.addMethod("toString", _function);
    }
    
    public void addToString(final MutableClassDeclaration cls, final Iterable<? extends FieldDeclaration> fields, final ToStringConfiguration config) {
      final Procedure1<MutableMethodDeclaration> _function = (MutableMethodDeclaration it) -> {
        this.context.setPrimarySourceElement(it, this.context.getPrimarySourceElement(cls));
        it.setReturnType(this.context.getString());
        it.addAnnotation(this.context.newAnnotationReference(Override.class));
        it.addAnnotation(this.context.newAnnotationReference(Pure.class));
        StringConcatenationClient _client = new StringConcatenationClient() {
          @Override
          protected void appendTo(StringConcatenationClient.TargetStringConcatenation _builder) {
            _builder.append(ToStringBuilder.class);
            _builder.append(" b = new ");
            _builder.append(ToStringBuilder.class);
            _builder.append("(this);");
            _builder.newLineIfNotEmpty();
            {
              boolean _isSkipNulls = config.isSkipNulls();
              if (_isSkipNulls) {
                _builder.append("b.skipNulls();");
              }
            }
            _builder.newLineIfNotEmpty();
            {
              boolean _isSingleLine = config.isSingleLine();
              if (_isSingleLine) {
                _builder.append("b.singleLine();");
              }
            }
            _builder.newLineIfNotEmpty();
            {
              boolean _isHideFieldNames = config.isHideFieldNames();
              if (_isHideFieldNames) {
                _builder.append("b.hideFieldNames();");
              }
            }
            _builder.newLineIfNotEmpty();
            {
              boolean _isVerbatimValues = config.isVerbatimValues();
              if (_isVerbatimValues) {
                _builder.append("b.verbatimValues();");
              }
            }
            _builder.newLineIfNotEmpty();
            {
              for(final FieldDeclaration field : fields) {
                _builder.append("b.add(\"");
                String _simpleName = field.getSimpleName();
                _builder.append(_simpleName);
                _builder.append("\", this.");
                String _simpleName_1 = field.getSimpleName();
                _builder.append(_simpleName_1);
                _builder.append(");");
                _builder.newLineIfNotEmpty();
              }
            }
            _builder.append("return b.toString();");
            _builder.newLine();
          }
        };
        it.setBody(_client);
      };
      cls.addMethod("toString", _function);
    }
  }
  
  @Override
  public void doTransform(final MutableClassDeclaration it, @Extension final TransformationContext context) {
    AnnotationReference _findAnnotation = it.findAnnotation(context.findTypeGlobally(Data.class));
    boolean _tripleNotEquals = (_findAnnotation != null);
    if (_tripleNotEquals) {
      return;
    }
    @Extension
    final ToStringProcessor.Util util = new ToStringProcessor.Util(context);
    final AnnotationReference annotation = it.findAnnotation(context.findTypeGlobally(ToString.class));
    final ToStringConfiguration configuration = new ToStringConfiguration(annotation);
    boolean _hasToString = util.hasToString(it);
    if (_hasToString) {
      context.addWarning(annotation, "toString is already defined, this annotation has no effect.");
    } else {
      TypeReference _extendedClass = it.getExtendedClass();
      TypeReference _object = context.getObject();
      boolean _notEquals = (!Objects.equal(_extendedClass, _object));
      if (_notEquals) {
        util.addReflectiveToString(it, configuration);
      } else {
        final Function1<MutableFieldDeclaration, Boolean> _function = (MutableFieldDeclaration it_1) -> {
          return Boolean.valueOf(((context.isThePrimaryGeneratedJavaElement(it_1) && (!it_1.isStatic())) && (!it_1.isTransient())));
        };
        util.addToString(it, IterableExtensions.filter(it.getDeclaredFields(), _function), configuration);
      }
    }
  }
}