/**
 * generated by Xtext
 */
package rethink.uml.generator;

import com.google.common.base.Objects;
import com.google.common.collect.Iterators;
import com.google.inject.Inject;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.xtend2.lib.StringConcatenation;
import org.eclipse.xtext.generator.IFileSystemAccess;
import org.eclipse.xtext.generator.IGenerator;
import org.eclipse.xtext.naming.IQualifiedNameProvider;
import org.eclipse.xtext.naming.QualifiedName;
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.IteratorExtensions;
import rethink.uml.classDiagram.CPackage;
import rethink.uml.classDiagram.Clazz;
import rethink.uml.classDiagram.DataType;
import rethink.uml.classDiagram.Element;
import rethink.uml.classDiagram.Entity;
import rethink.uml.classDiagram.EntityList;
import rethink.uml.classDiagram.Enumer;
import rethink.uml.classDiagram.NativeType;
import rethink.uml.classDiagram.Node;
import rethink.uml.classDiagram.Property;
import rethink.uml.classDiagram.Relation;
import rethink.uml.classDiagram.RelationParse;

/**
 * Generates code from your model files on save.
 * 
 * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#code-generation
 */
@SuppressWarnings("all")
public class ClassDiagramGenerator implements IGenerator {
  @Inject
  @Extension
  private IQualifiedNameProvider _iQualifiedNameProvider;
  
  @Override
  public void doGenerate(final Resource resource, final IFileSystemAccess fsa) {
    TreeIterator<EObject> _allContents = resource.getAllContents();
    Iterator<CPackage> _filter = Iterators.<CPackage>filter(_allContents, CPackage.class);
    Iterable<CPackage> _iterable = IteratorExtensions.<CPackage>toIterable(_filter);
    for (final CPackage pack : _iterable) {
      EList<Element> _elements = pack.getElements();
      final Function1<Element, Boolean> _function = (Element it) -> {
        return Boolean.valueOf((it instanceof Entity));
      };
      boolean _exists = IterableExtensions.<Element>exists(_elements, _function);
      if (_exists) {
        QualifiedName _fullyQualifiedName = this._iQualifiedNameProvider.getFullyQualifiedName(pack);
        String _string = _fullyQualifiedName.toString();
        String _plus = (_string + ".json");
        String _compile = this.compile(resource, pack);
        fsa.generateFile(_plus, _compile);
      }
    }
  }
  
  public String compile(final Resource resource, final CPackage pack) {
    LinkedList<String> cPack = new LinkedList<String>();
    TreeIterator<EObject> _eAllContents = pack.eAllContents();
    Iterator<Clazz> _filter = Iterators.<Clazz>filter(_eAllContents, Clazz.class);
    Iterable<Clazz> _iterable = IteratorExtensions.<Clazz>toIterable(_filter);
    for (final Clazz clazz : _iterable) {
      {
        TreeIterator<EObject> _allContents = resource.getAllContents();
        Iterator<Relation> _filter_1 = Iterators.<Relation>filter(_allContents, Relation.class);
        final Function1<Relation, Entity> _function = (Relation it) -> {
          boolean _and = false;
          boolean _and_1 = false;
          Node _leftRef = it.getLeftRef();
          if (!(_leftRef instanceof Clazz)) {
            _and_1 = false;
          } else {
            Node _rightRef = it.getRightRef();
            boolean _equals = Objects.equal(_rightRef, clazz);
            _and_1 = _equals;
          }
          if (!_and_1) {
            _and = false;
          } else {
            RelationParse _relType = it.getRelType();
            boolean _isExt = _relType.isExt();
            boolean _equals_1 = (_isExt == true);
            _and = _equals_1;
          }
          if (_and) {
            Node _leftRef_1 = it.getLeftRef();
            return ((Entity) _leftRef_1);
          }
          return null;
        };
        Iterator<Entity> _map = IteratorExtensions.<Relation, Entity>map(_filter_1, _function);
        Iterator<Entity> _filterNull = IteratorExtensions.<Entity>filterNull(_map);
        final List<Entity> invHierarchy = IteratorExtensions.<Entity>toList(_filterNull);
        final String cClass = this.compileClass(pack, clazz, invHierarchy);
        StringConcatenation _builder = new StringConcatenation();
        {
          String _name = clazz.getName();
          String _name_1 = pack.getName();
          boolean _equals = Objects.equal(_name, _name_1);
          if (_equals) {
            _builder.append("\"id\": \"");
            String _name_2 = clazz.getName();
            _builder.append(_name_2, "");
            _builder.append("\",");
            _builder.newLineIfNotEmpty();
            _builder.append(cClass, "");
            _builder.newLineIfNotEmpty();
          } else {
            _builder.append("\"");
            String _name_3 = clazz.getName();
            _builder.append(_name_3, "");
            _builder.append("\": {");
            _builder.newLineIfNotEmpty();
            _builder.append("\t");
            _builder.append(cClass, "\t");
            _builder.newLineIfNotEmpty();
            _builder.append("}");
            _builder.newLine();
          }
        }
        cPack.add(_builder.toString());
      }
    }
    StringConcatenation _builder = new StringConcatenation();
    _builder.append("{");
    _builder.newLine();
    _builder.append("\t");
    _builder.append("\"$schema\": \"http://json-schema.org/draft-04/schema#\",");
    _builder.newLine();
    _builder.newLine();
    {
      boolean _hasElements = false;
      for(final String item : cPack) {
        if (!_hasElements) {
          _hasElements = true;
        } else {
          _builder.appendImmediate(", ", "\t");
        }
        _builder.append("\t");
        _builder.append(item, "\t");
        _builder.newLineIfNotEmpty();
      }
    }
    _builder.append("}");
    _builder.newLine();
    return _builder.toString();
  }
  
  public String compileClass(final CPackage pack, final Clazz clazz, final List<Entity> invHierarchy) {
    EList<Property> _properties = clazz.getProperties();
    final Function1<Property, Boolean> _function = (Property it) -> {
      boolean _isOptional = it.isOptional();
      return Boolean.valueOf((!_isOptional));
    };
    Iterable<Property> _filter = IterableExtensions.<Property>filter(_properties, _function);
    final Function1<Property, String> _function_1 = (Property it) -> {
      return it.getName();
    };
    Iterable<String> _map = IterableExtensions.<Property, String>map(_filter, _function_1);
    final List<String> required = IterableExtensions.<String>toList(_map);
    StringConcatenation _builder = new StringConcatenation();
    _builder.append("\"type\": \"object\",");
    _builder.newLine();
    {
      boolean _isEmpty = required.isEmpty();
      boolean _not = (!_isEmpty);
      if (_not) {
        _builder.append("\"required\": [");
        {
          boolean _hasElements = false;
          for(final String prop : required) {
            if (!_hasElements) {
              _hasElements = true;
            } else {
              _builder.appendImmediate(", ", "");
            }
            _builder.append("\"");
            _builder.append(prop, "");
            _builder.append("\"");
          }
        }
        _builder.append("],");
        _builder.newLineIfNotEmpty();
      }
    }
    {
      boolean _isEmpty_1 = invHierarchy.isEmpty();
      boolean _not_1 = (!_isEmpty_1);
      if (_not_1) {
        CharSequence _processRefList = this.processRefList(invHierarchy, pack);
        _builder.append(_processRefList, "");
        _builder.append(",");
        _builder.newLineIfNotEmpty();
      }
    }
    _builder.append("\"properties\": {");
    _builder.newLine();
    {
      EList<Property> _properties_1 = clazz.getProperties();
      boolean _hasElements_1 = false;
      for(final Property prop_1 : _properties_1) {
        if (!_hasElements_1) {
          _hasElements_1 = true;
        } else {
          _builder.appendImmediate(",", "\t");
        }
        _builder.append("\t");
        _builder.append("\"");
        String _name = prop_1.getName();
        _builder.append(_name, "\t");
        _builder.append("\": {");
        _builder.newLineIfNotEmpty();
        {
          boolean _isConstant = prop_1.isConstant();
          if (_isConstant) {
            _builder.append("\t");
            _builder.append("\t");
            _builder.append("\"constant\": ");
            String _value = prop_1.getValue();
            _builder.append(_value, "\t\t");
            _builder.newLineIfNotEmpty();
          } else {
            {
              EntityList _entityList = prop_1.getEntityList();
              boolean _equals = Objects.equal(_entityList, null);
              if (_equals) {
                _builder.append("\t");
                _builder.append("\t");
                DataType _type = prop_1.getType();
                CharSequence _processDataType = this.processDataType(_type, pack);
                _builder.append(_processDataType, "\t\t");
                _builder.newLineIfNotEmpty();
              } else {
                _builder.append("\t");
                _builder.append("\t");
                _builder.append("\"type\": \"object\",");
                _builder.newLine();
                _builder.append("\t");
                _builder.append("\t");
                EntityList _entityList_1 = prop_1.getEntityList();
                EList<Entity> _refs = _entityList_1.getRefs();
                List<Entity> _list = IterableExtensions.<Entity>toList(_refs);
                CharSequence _processRefList_1 = this.processRefList(_list, pack);
                _builder.append(_processRefList_1, "\t\t");
                _builder.newLineIfNotEmpty();
              }
            }
          }
        }
        _builder.append("\t");
        _builder.append("}");
        _builder.newLine();
      }
    }
    _builder.append("}");
    _builder.newLine();
    return _builder.toString();
  }
  
  public CharSequence processRefList(final List<Entity> list, final CPackage pack) {
    StringConcatenation _builder = new StringConcatenation();
    _builder.append("\"anyOf\": [");
    _builder.newLine();
    {
      boolean _hasElements = false;
      for(final Entity ref : list) {
        if (!_hasElements) {
          _hasElements = true;
        } else {
          _builder.appendImmediate(",", "\t");
        }
        _builder.append("\t");
        _builder.append("{ ");
        String _processRef = this.processRef(ref, pack);
        _builder.append(_processRef, "\t");
        _builder.append(" }");
        _builder.newLineIfNotEmpty();
      }
    }
    _builder.append("]");
    _builder.newLine();
    return _builder;
  }
  
  public String processRef(final Entity ref, final CPackage pack) {
    if ((ref instanceof Enumer)) {
      StringConcatenation _builder = new StringConcatenation();
      _builder.append("\"enum\": [");
      _builder.newLine();
      {
        EList<String> _types = ((Enumer)ref).getTypes();
        boolean _hasElements = false;
        for(final String eType : _types) {
          if (!_hasElements) {
            _hasElements = true;
          } else {
            _builder.appendImmediate(",", "\t");
          }
          _builder.append("\t");
          _builder.append("\"");
          String _lowerCase = eType.toLowerCase();
          _builder.append(_lowerCase, "\t");
          _builder.append("\"");
          _builder.newLineIfNotEmpty();
        }
      }
      _builder.append("]");
      _builder.newLine();
      return _builder.toString();
    } else {
      if ((ref instanceof Clazz)) {
        String _stereotype = ((Clazz)ref).getStereotype();
        boolean _equals = Objects.equal(_stereotype, "(T,orchid)");
        if (_equals) {
          return "\"type\": \"string\"";
        } else {
          EObject _eContainer = ((Clazz)ref).eContainer();
          final CPackage refPack = ((CPackage) _eContainer);
          boolean _equals_1 = Objects.equal(refPack, pack);
          if (_equals_1) {
            StringConcatenation _builder_1 = new StringConcatenation();
            _builder_1.append("\"$ref\": \"#/");
            String _name = ((Clazz)ref).getName();
            _builder_1.append(_name, "");
            _builder_1.append("\"");
            return _builder_1.toString();
          } else {
            StringConcatenation _builder_2 = new StringConcatenation();
            _builder_2.append("\"$ref\": \"");
            QualifiedName _fullyQualifiedName = this._iQualifiedNameProvider.getFullyQualifiedName(refPack);
            _builder_2.append(_fullyQualifiedName, "");
            _builder_2.append(".json#/");
            String _name_1 = ((Clazz)ref).getName();
            _builder_2.append(_name_1, "");
            _builder_2.append("\"");
            return _builder_2.toString();
          }
        }
      }
    }
    return null;
  }
  
  public CharSequence processDataType(final DataType type, final CPackage pack) {
    CharSequence _xblockexpression = null;
    {
      boolean _equals = Objects.equal(type, null);
      if (_equals) {
        return "\"type\": \"null\"";
      }
      CharSequence _xifexpression = null;
      boolean _isIsArray = type.isIsArray();
      if (_isIsArray) {
        StringConcatenation _builder = new StringConcatenation();
        _builder.append("\"type\": \"array\",");
        _builder.newLine();
        _builder.append("\"items\": {");
        _builder.newLine();
        _builder.append("\t");
        String _processSimpleType = this.processSimpleType(type, pack);
        _builder.append(_processSimpleType, "\t");
        _builder.newLineIfNotEmpty();
        _builder.append("}");
        _builder.newLine();
        _xifexpression = _builder;
      } else {
        return this.processSimpleType(type, pack);
      }
      _xblockexpression = _xifexpression;
    }
    return _xblockexpression;
  }
  
  public String processSimpleType(final DataType type, final CPackage pack) {
    Entity _entity = type.getEntity();
    boolean _equals = Objects.equal(_entity, null);
    if (_equals) {
      NativeType _native = type.getNative();
      return this.genNative(_native);
    } else {
      Entity _entity_1 = type.getEntity();
      return this.processRef(_entity_1, pack);
    }
  }
  
  public String genNative(final NativeType value) {
    boolean _equals = Objects.equal(value, NativeType.DATE);
    if (_equals) {
      StringConcatenation _builder = new StringConcatenation();
      _builder.append("\"type\": \"string\",");
      _builder.newLine();
      _builder.append("\"format\": \"date-time\"");
      _builder.newLine();
      return _builder.toString();
    } else {
      StringConcatenation _builder_1 = new StringConcatenation();
      _builder_1.append("\"type\": \"");
      String _name = value.getName();
      String _lowerCase = _name.toLowerCase();
      _builder_1.append(_lowerCase, "");
      _builder_1.append("\"");
      return _builder_1.toString();
    }
  }
}
