/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.uima.ruta.textruler.learner.trabal;

import org.apache.uima.ruta.textruler.core.TextRulerTarget;

public class ShiftAllRule extends ShiftingRule {

  private TrabalRuleItem errorBoundaryItem;

  public ShiftAllRule(TrabalLearner parentAlgorithm, TextRulerTarget target,
          TrabalAnnotation annotation, TrabalAnnotation targetAnnotation,
          AnnotationErrorType errorType) {
    super(parentAlgorithm, target, annotation, targetAnnotation, errorType);
  }

  public ShiftAllRule(ShiftAllRule copyFrom) {
    super(copyFrom);
    this.errorBoundaryItem = copyFrom.errorBoundaryItem;
  }

  @Override
  public void compileRuleString() {
    ruleString = "";

    // shift to left or right?
    boolean shiftToLeft = errorType == AnnotationErrorType.SHIFTING_LEFT;

    // should the boundary items be part of the new annotation?
    boolean frontItemInBorders = frontBoundaryItem.getAnnotation().getBegin() == targetAnnotation
            .getBegin();
    boolean rearItemInBorders = rearBoundaryItem.getAnnotation().getEnd() == targetAnnotation
            .getEnd();

    // this string will be attached to the rear boundary item
    String before = parseConditions(ConditionType.BEFORE);
    if (before.length() > 0)
      before = "{" + before + "}";

    // the action string will be attached to the brackets, containing the conditions and mark action
    String conditions = parseConditions(conditionTypes);
    if (conditions.length() > 0)
      conditions += " ";
    String mark;
    if (((TrabalLearner) algorithm).getEnableFeatures())
      mark = "-> CREATE(" + targetAnnotation.getType().getShortName() + parseFeatures() + ")";
    else
      mark = "-> MARK(" + targetAnnotation.getType().getShortName() + ")";
    String action = "{" + conditions + mark + "}";

    if (errorBoundaryItem == null) {
      // We only have 2 boundary items (reduction rule, where only the right boundary changes)
      compileWithoutErrorRuleItem(frontItemInBorders, rearItemInBorders, before, action);
    } else {
      // this string will be attached to the front boundary item
      String after = parseConditions(ConditionType.AFTER);
      if (after.length() > 0)
        after = "{" + after + "}";

      // this part will delete the original annotation
      String unmark = errorBoundaryItem + (frontItemInBorders ? "" : " ANY") + "{STARTSWITH("
              + annotation.getType().getShortName() + ") -> UNMARK("
              + annotation.getType().getShortName() + ", true)}";

      if (shiftToLeft)
        compileShiftToLeft(frontItemInBorders, rearItemInBorders, before, after, action, unmark);
      else
        compileShiftToRight(frontItemInBorders, rearItemInBorders, before, after, action, unmark);
    }
    setNeedsCompile(false);
  }

  private void compileShiftToLeft(boolean frontItemInBorders, boolean rearItemInBorders,
          String before, String after, String action, String unmark) {
    // If the front item is part of the future annotation, it has to be included in the
    // brackets.
    if (frontItemInBorders)
      ruleString += "(" + frontBoundaryItem + after + " ";
    else
      ruleString += frontBoundaryItem + after + " (";

    // We include all tokens between the boundaries.
    ruleString += "ANY*{-PARTOF(" + errorBoundaryItem + ")} " + unmark + " ANY*{-PARTOF("
            + rearBoundaryItem + ")}"; // like ANY*? but faster
//    ruleString += "#{-CONTAINS(" + errorBoundaryItem + ")} " + unmark + " #{-CONTAINS("
//            + rearBoundaryItem + ")}"; // like ANY*? but faster

    // Check, if the rear item should be included and mark all tokens between the brackets as
    // the
    // new annotation.
    if (rearItemInBorders)
      ruleString += " " + rearBoundaryItem + before + ")" + action + ";";
    else
      ruleString += ")" + action + " " + rearBoundaryItem + before + ";";
  }

  private void compileShiftToRight(boolean frontItemInBorders, boolean rearItemInBorders,
          String before, String after, String action, String unmark) {
    // The old annotation begins before the new annotation
    ruleString += unmark + " ANY*{-PARTOF(" + frontBoundaryItem + ")} ";
//    ruleString += unmark + " #{-CONTAINS(" + frontBoundaryItem + ")} ";

    // If the front item is part of the future annotation, it has to be included in the
    // brackets.
    if (frontItemInBorders)
      ruleString += "(" + frontBoundaryItem + after + " ";
    else
      ruleString += frontBoundaryItem + after + " (";

    // We include all tokens between the boundaries.
    ruleString += "ANY*{-PARTOF(" + rearBoundaryItem + ")}"; // like ANY*? but faster
//    ruleString += "#{-CONTAINS(" + rearBoundaryItem + ")}"; // like ANY*? but faster

    // Check, if the rear item should be included and mark all tokens between the brackets as
    // the
    // new annotation.
    if (rearItemInBorders)
      ruleString += " " + rearBoundaryItem + before + ")" + action + ";";
    else
      ruleString += ")" + action + " " + rearBoundaryItem + before + ";";
  }

  private void compileWithoutErrorRuleItem(boolean frontItemInBorders, boolean rearItemInBorders,
          String before, String action) {
    // this string will be attached to the front boundary item
    String after = parseConditions(ConditionType.AFTER);
    if (after.length() > 0)
      after = ", " + after;

    // this string deletes the original annotation
    String unmark = "{STARTSWITH(" + annotation.getType().getShortName() + ")" + after
            + " -> UNMARK(" + annotation.getType().getShortName() + ", true)}";

    if (frontItemInBorders)
      ruleString += "(" + frontBoundaryItem + unmark + " ";
    else
      ruleString += frontBoundaryItem + unmark + " (";

    ruleString += "ANY*{-PARTOF(" + rearBoundaryItem + ")}"; // like ANY*? but faster
//    ruleString += "#{-CONTAINS(" + rearBoundaryItem + ")}"; // like ANY*? but faster

    // Check, if the rear item should be included and mark all tokens between the brackets as the
    // new annotation.
    if (rearItemInBorders)
      ruleString += " " + rearBoundaryItem + before + ")" + action + ";";
    else
      ruleString += ")" + action + " " + rearBoundaryItem + before + ";";
  }

  @Override
  public boolean contains(TrabalAnnotation target) {
    if (target != null) {
      if (annotation.getType().getShortName().equals(target.getType().getShortName())) {
        return true;
      }
      if (targetAnnotation.getType().getShortName().equals(target.getType().getShortName())) {
        return true;
      }
      if (frontBoundaryItem != null)
        if (frontBoundaryItem.getAnnotation().equals(target))
          return true;
      if (errorBoundaryItem != null)
        if (errorBoundaryItem.getAnnotation().equals(target))
          return true;
      if (rearBoundaryItem != null)
        if (rearBoundaryItem.getAnnotation().equals(target))
          return true;
      for (Condition each : getConditions()) {
        if (each.getItem().getAnnotation().getType().getShortName()
                .equals(target.getType().getShortName())) {
          return true;
        }
      }
    }
    return false;
  }

  public void setErrorBoundaryItem(TrabalRuleItem errorBoundaryItem) {
    this.errorBoundaryItem = errorBoundaryItem;
  }

  @Override
  public TrabalRule copy() {
    return new ShiftAllRule(this);
  }

}
