/*
 * Copyright 2000-2017 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 com.intellij.openapi.editor.impl;

import com.intellij.openapi.editor.Caret;
import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.openapi.editor.VisualPosition;
import com.intellij.testFramework.TestFileType;

import java.awt.*;
import java.io.IOException;
import java.util.ArrayList;

/**
 * To simplify the representation of input text, 'R' character in these tests represents an RTL character.
 */
public abstract class AbstractRtlTest extends AbstractEditorTest {
  private static final char RTL_CHAR_REPRESENTATION = 'R';
  private static final char RTL_CHAR = '\u05d0'; // Hebrew 'aleph' letter
  private static final char BIDI_BOUNDARY_MARKER = '|';

  protected void prepareText(String text) throws IOException {
    prepare(text, TestFileType.TEXT);
  }
  
  protected void prepare(String text, TestFileType fileType) throws IOException {
    init(text.replace(RTL_CHAR_REPRESENTATION, RTL_CHAR), fileType);
  }
  
  protected void checkResult(String text) {
    checkResultByText(text.replace(RTL_CHAR_REPRESENTATION, RTL_CHAR));
  }
  
  protected static void checkOffsetConversions(int offset,
                                             LogicalPosition logicalPosition,
                                             VisualPosition visualPositionTowardsSmallerOffsets,
                                             VisualPosition visualPositionTowardsLargerOffsets,
                                             Point xy) {
    checkOffsetConversions(offset, logicalPosition, visualPositionTowardsSmallerOffsets, visualPositionTowardsLargerOffsets, xy, xy);
  }

  protected static void checkOffsetConversions(int offset,
                                             LogicalPosition logicalPosition, 
                                             VisualPosition visualPositionTowardsSmallerOffsets, 
                                             VisualPosition visualPositionTowardsLargerOffsets, 
                                             Point xyTowardsSmallerOffsets, 
                                             Point xyTowardsLargerOffsets) {
    assertLogicalPositionsEqual("Wrong offset->logicalPosition calculation", logicalPosition, myEditor.offsetToLogicalPosition(offset));
    assertVisualPositionsEqual("Wrong beforeOffset->visualPosition calculation",
                               visualPositionTowardsSmallerOffsets, myEditor.offsetToVisualPosition(offset, false, false));
    assertVisualPositionsEqual("Wrong afterOffset->visualPosition calculation",
                               visualPositionTowardsLargerOffsets, myEditor.offsetToVisualPosition(offset, true, false));
    assertEquals("Wrong afterOffset->visualLine calculation", 
                 visualPositionTowardsLargerOffsets.line, ((EditorImpl)myEditor).offsetToVisualLine(offset));
    assertEquals("Wrong beforeOffset->xy calculation", xyTowardsSmallerOffsets, ((EditorImpl)myEditor).offsetToXY(offset, false));
    assertEquals("Wrong afterOffset->xy calculation", xyTowardsLargerOffsets, ((EditorImpl)myEditor).offsetToXY(offset, true));
  }

  protected static void checkLPConversions(int logicalColumn, int offset,
                                         VisualPosition visualPositionForPrecedingLp, VisualPosition visualPositionForSucceedingLp) {
    checkLPConversions(lB(logicalColumn), offset, visualPositionForPrecedingLp);
    checkLPConversions(lF(logicalColumn), offset, visualPositionForSucceedingLp);
    
  }
  
  protected static void checkLPConversions(LogicalPosition logicalPosition, int offset, VisualPosition visualPosition) {
    assertEquals("Wrong logicalPosition->offset calculation", offset, myEditor.logicalPositionToOffset(logicalPosition));
    assertVisualPositionsEqual("Wrong logicalPosition->visualPosition calculation",
                               visualPosition, myEditor.logicalToVisualPosition(logicalPosition));
  }

  protected static void checkVPConversions(int visualColumn, LogicalPosition logicalPositionForLeftLeaningVp,
                                         LogicalPosition logicalPositionForRightLeaningVp, Point xy) {
    checkVPConversions(vL(visualColumn), logicalPositionForLeftLeaningVp, xy);
    checkVPConversions(vR(visualColumn), logicalPositionForRightLeaningVp, xy);
  }

  protected static void checkVPConversions(VisualPosition visualPosition, LogicalPosition logicalPosition, Point xy) {
    assertLogicalPositionsEqual("Wrong visualPosition->logicalPosition calculation",
                                logicalPosition, myEditor.visualToLogicalPosition(visualPosition));
    assertEquals("Wrong visualPosition->xy calculation", xy, myEditor.visualPositionToXY(visualPosition));
  }
  
  protected static void checkXYConversion(Point xy,
                                       VisualPosition visualPosition) {
    assertVisualPositionsEqual("Wrong xy->visualPosition calculation", visualPosition, myEditor.xyToVisualPosition(xy));
  }

  protected static void assertLogicalPositionsEqual(String message, LogicalPosition expectedPosition, LogicalPosition actualPosition) {
    assertEquals(message, expectedPosition, actualPosition);
    assertEquals(message + " (direction flag)", expectedPosition.leansForward, actualPosition.leansForward);
  }

  protected static void assertVisualPositionsEqual(String message, VisualPosition expectedPosition, VisualPosition actualPosition) {
    assertEquals(message, expectedPosition, actualPosition);
    assertEquals(message + " (direction flag)", expectedPosition.leansRight, actualPosition.leansRight);
  }

  protected static void assertCaretPosition(VisualPosition visualPosition) {
    assertVisualPositionsEqual("Wrong caret position", visualPosition, myEditor.getCaretModel().getVisualPosition());
  }

  protected static void assertVisualCaretLocation(int visualColumn, boolean reversedDirection) {
    assertVisualCaretLocation(0, visualColumn, reversedDirection);
  }
  
  protected static void assertVisualCaretLocation(int visualLine, int visualColumn, boolean reversedDirection) {
    assertEquals(1, myEditor.getCaretModel().getCaretCount());
    Caret caret = myEditor.getCaretModel().getPrimaryCaret();
    assertEquals(visualLine, caret.getVisualPosition().line);
    assertEquals(visualColumn, caret.getVisualPosition().column);
    assertEquals(reversedDirection, caret.isAtRtlLocation());
  }

  /**
   * Text should contain {@link #BIDI_BOUNDARY_MARKER} characters at expected bidi run boundaries' positions.
   */
  protected void checkBidiRunBoundaries(String textWithBoundaryMarkers, String fileExtension) {
    java.util.List<Integer> expectedBoundaryPositions = new ArrayList<>();
    StringBuilder rawTextBuilder = new StringBuilder();
    for (int i = 0; i < textWithBoundaryMarkers.length(); i++) {
      char c = textWithBoundaryMarkers.charAt(i);
      if (c == BIDI_BOUNDARY_MARKER) {
        expectedBoundaryPositions.add(rawTextBuilder.length());
      }
      else {
        rawTextBuilder.append(c);
      }
    }
    String rawText = rawTextBuilder.toString();
    configureFromFileText(getTestName(false) + "." + fileExtension, rawText.replace(RTL_CHAR_REPRESENTATION, RTL_CHAR));

    java.util.List<Integer> actualBoundaryPositions = new ArrayList<>();
    for (int i = 1; i < rawText.length(); i++) {
      if (!myEditor.offsetToVisualPosition(i, false, false).equals(
        myEditor.offsetToVisualPosition(i, true, false))) {
        actualBoundaryPositions.add(i);
      }
    }
    assertEquals("Unexpected bidi regions boundaries' positions", expectedBoundaryPositions, actualBoundaryPositions);
  }

  // logical position leaning backward
  protected static LogicalPosition lB(int column) {
    return new LogicalPosition(0, column);
  }
  
  // logical position leaning backward
  protected static LogicalPosition lB(int line, int column) {
    return new LogicalPosition(line, column);
  }

  // logical position leaning forward
  protected static LogicalPosition lF(int column) {
    return new LogicalPosition(0, column, true);
  }
  
  // logical position leaning forward
  protected static LogicalPosition lF(int line, int column) {
    return new LogicalPosition(line, column, true);
  }
 
  // visual position leaning to the left
  protected static VisualPosition vL(int column) {
    return new VisualPosition(0, column);
  }

  // visual position leaning to the left
  protected static VisualPosition vL(int line, int column) {
    return new VisualPosition(line, column);
  }

  // visual position leaning to the right
  protected static VisualPosition vR(int column) {
    return new VisualPosition(0, column, true);
  }

  // visual position leaning to the right
  protected static VisualPosition vR(int line, int column) {
    return new VisualPosition(line, column, true);
  }

  protected static Point xy(int x) {
    return new Point(x, 0);
  }
  
  protected static Point xy(int x, int y) {
    return new Point(x, y);
  }
}
