/*
 * Copyright 2003-2011 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.nodeEditor.cellMenu;

import com.intellij.util.ui.UIUtil;
import jetbrains.mps.nodeEditor.EditorComponent;
import jetbrains.mps.nodeEditor.cells.TextLine;
import jetbrains.mps.nodeEditor.ui.InputMethodListenerImpl;
import jetbrains.mps.openapi.editor.style.StyleRegistry;
import org.jetbrains.annotations.NotNull;

import javax.swing.JPanel;
import javax.swing.JWindow;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.event.InputMethodEvent;
import java.awt.event.KeyEvent;

/**
 * Author: Sergey Dmitriev.
 * Time: Oct 20, 2003 1:45:39 PM
 */
public class NodeSubstitutePatternEditor {
  private EditorWindow myEditorWindow;
  private boolean myEditorActivated;

  private String myCachedText = "";
  private int myCachedCaretPosition;
  private int mySavedCaretPosition = 0;

  public void setText(String text) {
    if (myEditorActivated) {
      myEditorWindow.myTextLine.setText(text);
      myEditorWindow.relayout();
      myEditorWindow.repaint();
    } else {
      myCachedText = text;
    }
  }

  public boolean isActivated() {
    return myEditorActivated;
  }

  public String getText() {
    if (myEditorActivated) {
      return myEditorWindow.myTextLine.getText();
    }
    return myCachedText;
  }

  public void setCaretPosition(int caretPosition) {
    if (myEditorActivated) {
      myEditorWindow.myTextLine.setCaretPosition(caretPosition);
      myEditorWindow.repaint();
    } else {
      myCachedCaretPosition = caretPosition;
    }
  }

  public int getCaretPosition() {
    return myEditorWindow.myTextLine.getCaretPosition();
  }

  public boolean processKeyPressed(KeyEvent keyEvent) {
    if (myEditorActivated) {
      if (keyEvent.getKeyCode() == KeyEvent.VK_SPACE && keyEvent.isControlDown()) {
        toggleReplaceMode();
        return true;
      }
      return myEditorWindow.processKeyPressed(keyEvent);
    }
    return false;
  }

  public void toggleReplaceMode() {
    if (myEditorActivated) {
      TextLine textLine = myEditorWindow.myTextLine;
      if (mySavedCaretPosition != 0) {
        textLine.setCaretPosition(mySavedCaretPosition);
        mySavedCaretPosition = 0;
      } else {
        mySavedCaretPosition = textLine.getCaretPosition();
        textLine.setCaretPosition(0);
      }
      myEditorWindow.repaint();
    } else {
      if (mySavedCaretPosition != 0) {
        myCachedCaretPosition = mySavedCaretPosition;
        mySavedCaretPosition = 0;
      } else {
        mySavedCaretPosition = myCachedCaretPosition;
        myCachedCaretPosition = 0;
      }
    }
  }

  public boolean processKeyTyped(KeyEvent keyEvent) {
    if (myEditorActivated && myEditorWindow.processKeyTyped(keyEvent)) {
      mySavedCaretPosition = 0;
      return true;
    }
    return false;
  }

  public boolean processTextChanged(InputMethodEvent inputEvent) {
    if (myEditorActivated && myEditorWindow.processTextChanged(inputEvent)) {
      mySavedCaretPosition = 0;
      return true;
    }
    return false;
  }

  @NotNull
  public String getPattern() {
    if (myEditorActivated) {
      TextLine textLine = myEditorWindow.myTextLine;
      int caretPosition = textLine.getCaretPosition();
      String text = textLine.getText();
      return text.substring(0, caretPosition);
    }

    if (myCachedText == null) {
      return "";
    }
    int caretPos = Math.min(myCachedText.length(), Math.max(myCachedCaretPosition, 0));
    return myCachedText.substring(0, caretPos);
  }

  // ------------------

  public void activate(Window owner, Point location, Dimension size, boolean show) {
    if (!myEditorActivated) {
      myEditorActivated = true;
      myEditorWindow = new EditorWindow(owner);
      myEditorWindow.setLocation(location);
      myEditorWindow.setMinimalSize(size);
      myEditorWindow.myTextLine.setText(myCachedText);
      myEditorWindow.myTextLine.setCaretPosition(myCachedCaretPosition);
      if (show) {
        myEditorWindow.relayout();
        myEditorWindow.setVisible(true);
      }
    }
  }

  public void setLocation(Point point) {
    myEditorWindow.setLocation(point);
  }

  public Point getLeftBottomPosition() {
    Point location = myEditorWindow.getLocation();
    location.translate(0, myEditorWindow.getSize().height);
    return location;
  }

  public int getHeight() {
    return myEditorWindow.getSize().height;
  }

  public void done() {
    if (myEditorActivated) {
      myEditorWindow.dispose();
      myEditorActivated = false;
      mySavedCaretPosition = 0;
    }
  }

  private static class EditorWindow extends JWindow {
    private TextLine myTextLine;
    private Dimension myMinimalSize;
    private JPanel myPanel = new EditorPanel();

    public EditorWindow(Window owner) {
      super(owner);
      myTextLine = new TextLine("");
      add(myPanel);
    }

    public void setMinimalSize(Dimension size) {
      myMinimalSize = size;
    }

    public void relayout() {
      myTextLine.relayout();
      int h = Math.max(myMinimalSize.height, myTextLine.getHeight());
      int w = Math.max(myMinimalSize.width, getWidth());
      w = Math.max(w, myTextLine.getWidth());
      setSize(w, h);
    }

    public boolean processKeyTyped(KeyEvent keyEvent) {
      if (processKeyTypedInternal(keyEvent)) {
        relayout();
        repaint();
        return true;
      }
      return false;
    }

    public boolean processTextChanged(InputMethodEvent inputEvent) {
      String oldText = myTextLine.getText();
      int caretPosition = myTextLine.getCaretPosition();
      if (caretPosition > 0) {
        // replacing last symbol before the caret with the text from input method
        caretPosition--;
      }

      String text = InputMethodListenerImpl.getText(inputEvent);
      changeText(oldText.substring(0, caretPosition) + text);
      myTextLine.setCaretPosition(caretPosition + text.length());
      relayout();
      repaint();
      return true;
    }

    private boolean processKeyTypedInternal(KeyEvent keyEvent) {
      String oldText = myTextLine.getText();
      int caretPosition = myTextLine.getCaretPosition();

      char keyChar = keyEvent.getKeyChar();
      if (UIUtil.isReallyTypedEvent(keyEvent)) {
        changeText(oldText.substring(0, caretPosition) + keyChar/* + myText.substring(caretPosition)*/);
        myTextLine.setCaretPosition(caretPosition + 1);
        return true;
      }

      return false;
    }

    public boolean processKeyPressed(KeyEvent keyEvent) {
      if (processKeyPressedInternal(keyEvent)) {
        relayout();
        repaint();
        return true;
      }
      return false;
    }

    private boolean processKeyPressedInternal(KeyEvent keyEvent) {
      if (keyEvent.isControlDown()) {
        return false;
      }

      String oldText = myTextLine.getText();
      int caretPosition = myTextLine.getCaretPosition();
      if (keyEvent.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
        if (caretPosition > 0) {
          changeText(oldText.substring(0, caretPosition - 1) + oldText.substring(caretPosition));
          myTextLine.setCaretPosition(caretPosition - 1);
          return true;
        } else {
          return false;
        }
      }

      if (keyEvent.getKeyCode() == KeyEvent.VK_DELETE) {
        if (caretPosition < oldText.length()) {
          changeText(oldText.substring(0, caretPosition) + oldText.substring(caretPosition + 1));
          return true;
        } else {
          return false;
        }
      }

      if (keyEvent.getKeyCode() == KeyEvent.VK_LEFT) {
        if (caretPosition > 0) {
          myTextLine.setCaretPosition(caretPosition - 1);
          return true;
        } else {
          return false;
        }
      }

      if (keyEvent.getKeyCode() == KeyEvent.VK_RIGHT) {
        if (caretPosition < oldText.length()) {
          myTextLine.setCaretPosition(caretPosition + 1);
          return true;
        } else {
          return false;
        }
      }

      return false;
    }

    protected void changeText(String text) {
      myTextLine.setText(text);
    }

    private class EditorPanel extends JPanel {
      @Override
      protected void paintComponent(Graphics g) {
        // COLORS: move colors to properties
        Rectangle bounds = g.getClipBounds();
        g.setColor(StyleRegistry.getInstance().getSimpleColor(Color.YELLOW));
        g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
        g.setColor(StyleRegistry.getInstance().getSimpleColor(Color.GRAY));
        g.drawRect(bounds.x, bounds.y, bounds.width - 1, bounds.height - 1);
        EditorComponent.turnOnAliasingIfPossible((Graphics2D) g);

        TextLine textLine = myTextLine;
        textLine.setSelected(false);
        textLine.setShowCaret(true);
        textLine.paint(g, 0, 0);
      }
    }
  } // private class EditorWindow
}
