/*
 * Copyright 2000-2012 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.designer.designSurface.tools;

import com.intellij.designer.DesignerBundle;
import com.intellij.designer.designSurface.EditableArea;
import com.intellij.designer.designSurface.ZoomType;
import com.intellij.designer.model.RadComponent;
import com.intellij.designer.propertyTable.InplaceContext;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.ActionPopupMenu;
import com.intellij.openapi.application.ApplicationManager;
import org.jetbrains.annotations.Nullable;

import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.util.List;

/**
 * @author Alexander Lobas
 */
public class SelectionTool extends InputTool {
  private InputTool myTracker;

  @Override
  public void deactivate() {
    deactivateTracker();
    super.deactivate();
  }

  @Override
  public void refreshCursor() {
    if (myTracker == null) {
      super.refreshCursor();
    }
  }

  @Override
  protected void handleButtonDown(int button) {
    if (myState == STATE_INIT) {
      myState = STATE_DRAG;
      deactivateTracker();

      if (handleTracker()) {
        return;
      }

      if (!myArea.isTree()) {
        InputTool tracker = myArea.findTargetTool(myCurrentScreenX, myCurrentScreenY);
        if (tracker != null) {
          setTracker(tracker);
          if (tracker instanceof ResizeTracker) {
            myArea.showSelection(false);
          }
          return;
        }
      }

      RadComponent component = myArea.findTarget(myCurrentScreenX, myCurrentScreenY, null);
      if (component == null || component.isBackground()) {
        if (!myArea.isTree()) {
          MarqueeTracker tracker = new MarqueeTracker();

          // Allow marquee dragging within the root (background) layout, and if you click
          // without dragging, select that background component
          tracker.setSelectBackground(component != null && component.isBackground());

          setTracker(tracker);
        }
      }
      else {
        Point location = component.convertPoint(myArea.getNativeComponent(), myCurrentScreenX, myCurrentScreenY);
        setTracker(component.getDragTracker(location, myInputEvent, myArea.isTree()));
      }
    }
  }

  protected boolean handleTracker() {
    if (!myArea.isTree() && myInputEvent.isAltDown()) {
      setTracker(new MarqueeTracker());
      return true;
    }
    return false;
  }

  @Override
  protected void handleButtonUp(int button) {
    myState = STATE_INIT;
    setTracker(null);
    handleMove(); // hack: update cursor
  }

  @Override
  protected void handlePopup() {
    ActionManager actionManager = ActionManager.getInstance();
    ActionPopupMenu popupMenu = actionManager.createActionPopupMenu(myArea.getPopupPlace(), myArea.getPopupActions());
    MouseEvent event = (MouseEvent)myInputEvent;
    popupMenu.getComponent().show(myArea.getNativeComponent(), event.getX(), event.getY());
  }

  @Override
  protected void handleDoubleClick(int button) {
    if (button == MouseEvent.BUTTON1 && myToolProvider != null && !myArea.isTree()) {
      myToolProvider.startInplaceEditing(null);
    }
  }

  @Override
  protected void handleMove() {
    if (myState == STATE_INIT) {
      InputTool tracker = myArea.findTargetTool(myCurrentScreenX, myCurrentScreenY);
      if (tracker == null) {
        refreshCursor();

        if (ApplicationManager.getApplication().isInternal()) {
          myArea.setDescription(DesignerBundle.message("selection.tool.location.0.x.1", myCurrentScreenX, myCurrentScreenY));
        }
        else {
          myArea.setDescription(null);
        }
      }
      else {
        if (myInputEvent instanceof MouseEvent) {
          try {
            tracker.mouseMove((MouseEvent)myInputEvent, myArea);
          }
          catch (Exception e) {
            // pass
          }
        }
        myArea.setCursor(tracker.getDefaultCursor());
        myArea.setDescription(tracker.getDescription());
      }
    }
  }

  private void setTracker(@Nullable InputTool tracker) {
    if (myTracker != tracker) {
      deactivateTracker();
      myTracker = tracker;
      refreshCursor();

      if (myTracker != null) {
        myToolProvider.hideInspections();
        myTracker.setToolProvider(myToolProvider);
        myTracker.setArea(myArea);
        myTracker.myModifiers = myModifiers;
        myTracker.activate();
      }
    }
  }

  private void deactivateTracker() {
    if (myTracker != null) {
      myTracker.deactivate();
      myTracker = null;
      if (myArea != null) {
        myArea.showSelection(true);
      }
    }
  }

  @Override
  public void mouseDown(MouseEvent event, EditableArea area) throws Exception {
    super.mouseDown(event, area);

    if (myTracker != null) {
      myTracker.mouseDown(event, area);
    }
  }

  @Override
  public void mouseUp(MouseEvent event, EditableArea area) throws Exception {
    if (myTracker != null) {
      myTracker.mouseUp(event, area);
    }

    super.mouseUp(event, area);
  }

  @Override
  public void mouseMove(MouseEvent event, EditableArea area) throws Exception {
    if (myTracker != null) {
      myTracker.mouseMove(event, area);
    }

    super.mouseMove(event, area);
  }

  @Override
  public void mouseDrag(MouseEvent event, EditableArea area) throws Exception {
    if (myTracker != null) {
      myTracker.mouseDrag(event, area);
    }

    super.mouseDrag(event, area);
  }


  @Override
  public void mousePopup(MouseEvent event, EditableArea area) throws Exception {
    if (myTracker != null) {
      myTracker.mousePopup(event, area);
    }

    super.mousePopup(event, area);
  }

  @Override
  public void mouseDoubleClick(MouseEvent event, EditableArea area) throws Exception {
    super.mouseDoubleClick(event, area);

    if (myTracker != null) {
      myTracker.mouseDoubleClick(event, area);
    }
  }

  @Override
  public void keyPressed(KeyEvent event, EditableArea area) throws Exception {
    super.keyTyped(event, area);

    if (myTracker != null) {
      myTracker.keyPressed(event, area);
    }
    else if (event.getKeyCode() == KeyEvent.VK_ESCAPE) {
      List<RadComponent> selection = area.getSelection();
      if (!selection.isEmpty()) {
        RadComponent component = selection.get(0).getParent();
        if (component != null) {
          area.select(component);
        }
      }
    }
  }

  @Override
  public void keyTyped(KeyEvent event, EditableArea area) throws Exception {
    super.keyTyped(event, area);

    if (myTracker != null) {
      myTracker.keyTyped(event, area);
    }
    else if (myToolProvider != null && !area.isTree()) {
      handleKeyTyped(event);
    }
  }

  protected void handleKeyTyped(KeyEvent event) {
    char keyChar = event.getKeyChar();
    switch (keyChar) {
      // Zoom
      case '-':
      case '+':
      case '0':
      case '1':
        ZoomType type;
        if (keyChar == '-') {
          type = ZoomType.OUT;
        }
        else if (keyChar == '+') {
          type = ZoomType.IN;
        }
        else if (keyChar == '0') {
          type = ZoomType.FIT;
        }
        else { // '1'
          type = ZoomType.ACTUAL;
        }
        if (myToolProvider.isZoomSupported()) {
          myToolProvider.zoom(type);
          event.consume();
          return;
        }
        // else: fall through
      default:
        if (Character.isLetterOrDigit(keyChar) &&
            (event.getModifiers() & (InputEvent.ALT_MASK | InputEvent.CTRL_MASK | InputEvent.META_MASK)) == 0) {
          myToolProvider.startInplaceEditing(new InplaceContext(keyChar));
        }
    }
  }

  @Override
  public void keyReleased(KeyEvent event, EditableArea area) throws Exception {
    super.keyReleased(event, area);

    if (myTracker != null) {
      myTracker.keyReleased(event, area);
    }
  }
}