package org.aikodi.jlo.translate;

import java.util.Set;

import org.aikodi.chameleon.core.document.Document;
import org.aikodi.chameleon.exception.ChameleonProgrammerException;
import org.aikodi.chameleon.oo.expression.NameExpression;
import org.aikodi.chameleon.oo.member.Member;
import org.aikodi.chameleon.oo.method.Method;
import org.aikodi.chameleon.oo.method.RegularImplementation;
import org.aikodi.chameleon.oo.statement.Block;
import org.aikodi.chameleon.oo.type.Type;
import org.aikodi.chameleon.oo.type.inheritance.SubtypeRelation;
import org.aikodi.chameleon.oo.variable.MemberVariable;
import org.aikodi.chameleon.oo.variable.VariableDeclaration;
import org.aikodi.chameleon.support.expression.AssignmentExpression;
import org.aikodi.chameleon.support.member.simplename.variable.MemberVariableDeclarator;
import org.aikodi.chameleon.support.modifier.Private;
import org.aikodi.chameleon.support.modifier.Public;
import org.aikodi.chameleon.support.statement.ReturnStatement;
import org.aikodi.chameleon.support.statement.StatementExpression;
import org.aikodi.jlo.model.component.Subobject;
import org.aikodi.jlo.model.component.SubobjectType;

import be.kuleuven.cs.distrinet.jnome.core.language.Java7;
import be.kuleuven.cs.distrinet.jnome.core.modifier.Implements;

public class Java8ClassGenerator extends AbstractJava8Generator {

  protected Document createImplementation(Document result) {
    implementOwnInterfaces(result);
    removeNormalMethods(result);
    replaceSubobjects(result);
    addFields(result);
    renameConstructorCalls(result);
    return result;
  }

  protected void replaceSubobjects(Document result) {
    result.apply(Subobject.class, s -> {
      MemberVariableDeclarator field = new MemberVariableDeclarator(s.clone(s.superClassReference()));
      field.add(new VariableDeclaration(subobjectFieldName(s)));
      field.addModifier(new Private());
      s.nearestAncestor(Type.class).add(field);
    	s.disconnect();
    });
  }

  protected void removeNormalMethods(Document result) {
    result.apply(Method.class, m -> {
      Member origin = (Member) m.origin();
      if (!origin.isTrue(jlo(origin).CONSTRUCTOR)) {
        m.disconnect();
      }
    });
  }

  protected void implementOwnInterfaces(Document result) {
    result.apply(Type.class, t -> {
      SubtypeRelation relation = new SubtypeRelation(t.language(Java7.class).createTypeReference(t.name()));
      relation.addModifier(new Implements());
      t.addInheritanceRelation(relation);
      t.setName(implementationName(t));
    });
  }

  protected void addFields(Document target) {
    target.apply(MemberVariableDeclarator.class, m -> {
			if(! isGenerated(m)) {
    		m.disconnect();
    	}
    });
    target.apply(Type.class, t -> {
    	Type originalType;
    	if(t instanceof SubobjectType) {
    		Subobject originalSubobect = (Subobject) t.nearestAncestor(Subobject.class).origin();
    		try {
					originalType = originalSubobect.componentType();
				} catch (Exception e) {
					throw new ChameleonProgrammerException(e);
				}
    	} else {
    		originalType = (Type) t.origin();
    	}
      try {
        Set<Type> allSuperTypes = originalType.getAllSuperTypes();
        allSuperTypes.add((Type) t.origin());
        allSuperTypes.stream().<MemberVariable>flatMap(x -> {
          try {
            return x.localMembers(MemberVariable.class).stream();
          } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            throw new ChameleonProgrammerException(e);
          }
        }).forEach(v -> {
          MemberVariableDeclarator decl = v.nearestAncestor(MemberVariableDeclarator.class);
          MemberVariableDeclarator f = new MemberVariableDeclarator(decl.clone(decl.typeReference()));
          VariableDeclaration variableDeclaration = (VariableDeclaration) v.origin();
          f.add(new VariableDeclaration(fieldName(variableDeclaration)));
          f.addModifier(new Private());
          t.add(f);
          Method getter = createGetterTemplate(decl);
          getter.addModifier(new Public());
          Block getterBody = new Block();
          getter.setImplementation(new RegularImplementation(getterBody));
          getterBody.addStatement(new ReturnStatement(new NameExpression(fieldName(variableDeclaration))));
          t.add(getter);
          Method setter = createSetterTemplate(decl);
          setter.addModifier(new Public());
          Block setterBody = new Block();
          setter.setImplementation(new RegularImplementation(setterBody));
          setterBody.addStatement(new StatementExpression(new AssignmentExpression(new NameExpression(fieldName(variableDeclaration)), new NameExpression("value"))));
          t.add(setter);
        });
      } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
    });
  }


}

@@ -1895,16 +1895,28 @@
 );%0A     
+ Type type =
  s.neare
@@ -1937,16 +1937,25 @@
 e.class)
+;%0A%09%09%09type
 .add(fie
@@ -1959,16 +1959,161 @@
 field);%0A
+      Method getter = createSubobjectGetterTemplate(s);%0A      createGetterImplementation(subobjectFieldName(s), getter);%0A      type.add(getter);%0A
     %09s.d
@@ -4046,24 +4046,80 @@
 ;%0A          
+String fieldName = fieldName(variableDeclaration);%0A%09%09%09%09%09
 f.add(new Va
@@ -4145,37 +4145,16 @@
 ieldName
-(variableDeclaration)
 ));%0A    
@@ -4277,264 +4277,52 @@
     
-getter.addModifier(new Public());%0A          Block getterBody = new Block();%0A          getter.setImplementation(new RegularImplementation(getterBody));%0A          getterBody.addStatement(new ReturnStatement(new NameExpression(fieldName(variableDeclaration)))
+createGetterImplementation(fieldName, getter
 );%0A 
@@ -4671,37 +4671,16 @@
 ieldName
-(variableDeclaration)
 ), new N
@@ -4866,11 +4866,307 @@
 );%0A  %7D%0A%0A
+%09private void createGetterImplementation(String fieldName, Method getter) %7B%0A%09%09getter.addModifier(new Public());%0A%09%09Block getterBody = new Block();%0A%09%09getter.setImplementation(new RegularImplementation(getterBody));%0A%09%09getterBody.addStatement(new ReturnStatement(new NameExpression(fieldName)));%0A%09%7D%0A%0A
 %0A%7D%0A

add subobject getters