/*
 * Copyright 2003-2016 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package jetbrains.mps.generator.impl;

import jetbrains.mps.generator.runtime.NodePostProcessor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.mps.openapi.model.SNode;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Post-processing (end of generation step) code
 */
public class DelayedChanges {

  private static final String MAP_SRC_TEMP_NODE = "mapSrcTempNode";

  private final List<NodePostProcessor> myPostProcessors = new ArrayList<NodePostProcessor>();

  public DelayedChanges() {
  }

  public boolean isEmpty() {
    return myPostProcessors.isEmpty();
  }

  public void add(@NotNull NodePostProcessor postProcessor) {
    // FIXME unlike old code, we mark as temp nodes with post-processing only. Likely, shall get rid of this temp notion altogether,
    // or let NodePostProcessor tell me here whether it substitutes the node or not
    markNodeAsTemp(postProcessor.getOutputAnchor());
    synchronized (myPostProcessors) {
      myPostProcessors.add(postProcessor);
    }
  }

  public void doAllChanges(@NotNull TemplateGenerator generator) throws GenerationFailureException {
    SNode[] newOutputNodes = new SNode[myPostProcessors.size()];
    int i = 0;
    for (NodePostProcessor p : myPostProcessors) {
      SNode child = p.substitute();
      if (child == null) {
        generator.getLogger().error(p.getTemplateNode(), "Unexpected null value. Transform function of MAP-SRC didn't produce any result. Please check the function and make sure it always supplies a node");
        continue;
      }
      if (child != p.getOutputAnchor()) {
        ChildAdopter ca = new ChildAdopter(generator);
        ca.checkIsExpectedLanguage(Collections.singletonList(child), p.getTemplateNode(), p.getTemplateContext());
        child = ca.adopt(child, p.getTemplateContext());
        generator.replacePlaceholderNode(p, child);
      }

      newOutputNodes[i++] = child;
    }
    i = 0;
    for (NodePostProcessor p : myPostProcessors) {
      if (newOutputNodes[i] != null) {
        p.postProcess(newOutputNodes[i++]);
      }
    }
    myPostProcessors.clear();
  }

  public static boolean isTempNode(@NotNull SNode node) {
    return node.getUserObject(MAP_SRC_TEMP_NODE) != null;
  }

  private void markNodeAsTemp(SNode childToReplace) {
    childToReplace.putUserObject(MAP_SRC_TEMP_NODE, MAP_SRC_TEMP_NODE);
  }
}
