package com.orientechnologies.orient.core.sql.executor;

import com.orientechnologies.common.concur.OTimeoutException;
import com.orientechnologies.orient.core.command.OCommandContext;
import com.orientechnologies.orient.core.metadata.schema.OClass;
import com.orientechnologies.orient.core.metadata.schema.OProperty;
import com.orientechnologies.orient.core.metadata.security.OSecurity;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.record.impl.ODocumentInternal;
import com.orientechnologies.orient.core.sql.parser.OJson;

import java.util.Map;
import java.util.Optional;

/**
 * Created by luigidellaquila on 09/08/16.
 */
public class UpdateContentStep extends AbstractExecutionStep {
  private final OJson json;

  public UpdateContentStep(OJson json, OCommandContext ctx, boolean profilingEnabled) {
    super(ctx, profilingEnabled);
    this.json = json;

  }

  @Override public OResultSet syncPull(OCommandContext ctx, int nRecords) throws OTimeoutException {
    OResultSet upstream = getPrev().get().syncPull(ctx, nRecords);
    return new OResultSet() {
      @Override public boolean hasNext() {
        return upstream.hasNext();
      }

      @Override public OResult next() {
        OResult result = upstream.next();
        if (result instanceof OResultInternal) {
          if (!(result.getElement().get() instanceof ODocument)) {
            ((OResultInternal) result).setElement(result.getElement().get().getRecord());
          }
          if (!(result.getElement().get() instanceof ODocument)) {
            return result;
          }
          handleContent((ODocument) result.getElement().get(), ctx);
        }
        return result;
      }

      @Override public void close() {
        upstream.close();
      }

      @Override public Optional<OExecutionPlan> getExecutionPlan() {
        return null;
      }

      @Override public Map<String, Long> getQueryStats() {
        return null;
      }
    };
  }

  private boolean handleContent(ODocument record, OCommandContext ctx) {
    boolean updated = false;

    // REPLACE ALL THE CONTENT
    final ODocument fieldsToPreserve = new ODocument();

    final OClass restricted = ctx.getDatabase().getMetadata().getSchema().getClass(OSecurity.RESTRICTED_CLASSNAME);

    if (restricted != null && restricted.isSuperClassOf(record.getSchemaClass())) {
      for (OProperty prop : restricted.properties()) {
        fieldsToPreserve.field(prop.getName(), record.<Object>field(prop.getName()));
      }
    }

    OClass recordClass = ODocumentInternal.getImmutableSchemaClass(record);
    if (recordClass != null && recordClass.isSubClassOf("V")) {
      for (String fieldName : record.fieldNames()) {
        if (fieldName.startsWith("in_") || fieldName.startsWith("out_")) {
          fieldsToPreserve.field(fieldName, record.<Object>field(fieldName));
        }
      }
    } else if (recordClass != null && recordClass.isSubClassOf("E")) {
      for (String fieldName : record.fieldNames()) {
        if (fieldName.equals("in") || fieldName.equals("out")) {
          fieldsToPreserve.field(fieldName, record.<Object>field(fieldName));
        }
      }
    }
    record.merge(fieldsToPreserve, false, false);
    record.merge(json.toDocument(record, ctx), true, false);

    updated = true;

    return updated;
  }

  @Override public String prettyPrint(int depth, int indent) {
    String spaces = OExecutionStepInternal.getIndent(depth, indent);
    StringBuilder result = new StringBuilder();
    result.append(spaces);
    result.append("+ UPDATE CONTENT\n");
    result.append(spaces);
    result.append("  ");
    result.append(json);
    return result.toString();
  }
}

@@ -377,24 +377,82 @@
 .OSecurity;%0A
+import com.orientechnologies.orient.core.record.OElement;%0A
 import com.o
@@ -981,32 +981,34 @@
   %7D%0A%0A  @Override
+%0A 
  public OResultS
@@ -1185,32 +1185,38 @@
 %0A      @Override
+%0A     
  public boolean 
@@ -1278,32 +1278,38 @@
 %0A      @Override
+%0A     
  public OResult 
@@ -1454,36 +1454,35 @@
 t() instanceof O
-Docu
+Ele
 ment)) %7B%0A       
@@ -1630,20 +1630,19 @@
 anceof O
-Docu
+Ele
 ment)) %7B
@@ -1707,20 +1707,19 @@
 ntent((O
-Docu
+Ele
 ment) re
@@ -1798,32 +1798,38 @@
 %0A      @Override
+%0A     
  public void clo
@@ -1877,32 +1877,38 @@
 %0A      @Override
+%0A     
  public Optional
@@ -1982,32 +1982,38 @@
 %0A      @Override
+%0A     
  public Map%3CStri
@@ -2114,20 +2114,19 @@
 ontent(O
-Docu
+Ele
 ment rec
@@ -2460,22 +2460,34 @@
 etSchema
-Class(
+Type().orElse(null
 ))) %7B%0A  
@@ -2601,21 +2601,27 @@
 %3CObject%3E
-field
+getProperty
 (prop.ge
@@ -2719,16 +2719,28 @@
 s(record
+.getRecord()
 );%0A    i
@@ -2827,37 +2827,43 @@
 ldName : record.
-field
+getProperty
 Names()) %7B%0A     
@@ -2983,37 +2983,43 @@
  record.%3CObject%3E
-field
+getProperty
 (fieldName));%0A  
@@ -3142,21 +3142,27 @@
  record.
-field
+getProperty
 Names())
@@ -3288,21 +3288,27 @@
 %3CObject%3E
-field
+getProperty
 (fieldNa
@@ -3345,64 +3345,51 @@
     
-record.merge(fieldsToPreserve, false, false
+ODocument doc = record.getRecord(
 );%0A    
-record
+doc
 .mer
@@ -3420,16 +3420,63 @@
 d, ctx),
+ false, false);%0A    doc.merge(fieldsToPreserve,
  true, f
@@ -3540,16 +3540,18 @@
 Override
+%0A 
  public 

fix CREATE EDGE ... CONTENT