// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.streetside.mode;

import static org.openstreetmap.josm.tools.I18n.tr;

import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.util.Objects;
import java.util.concurrent.ConcurrentSkipListSet;

import javax.swing.SwingUtilities;

import org.openstreetmap.josm.data.Bounds;
import org.openstreetmap.josm.data.coor.LatLon;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
import org.openstreetmap.josm.gui.MainApplication;
import org.openstreetmap.josm.gui.MapView;
import org.openstreetmap.josm.gui.layer.OsmDataLayer;
import org.openstreetmap.josm.plugins.streetside.StreetsideAbstractImage;
import org.openstreetmap.josm.plugins.streetside.StreetsideData;
import org.openstreetmap.josm.plugins.streetside.StreetsideImage;
import org.openstreetmap.josm.plugins.streetside.StreetsideLayer;
import org.openstreetmap.josm.plugins.streetside.gui.StreetsideMainDialog;
import org.openstreetmap.josm.plugins.streetside.history.StreetsideRecord;
import org.openstreetmap.josm.plugins.streetside.history.commands.CommandMove;
import org.openstreetmap.josm.plugins.streetside.history.commands.CommandTurn;
import org.openstreetmap.josm.plugins.streetside.utils.StreetsideProperties;

/**
 * Handles the input event related with the layer. Mainly clicks.
 *
 * @author nokutu
 */
public class SelectMode extends AbstractMode {
  private StreetsideAbstractImage closest;
  private StreetsideAbstractImage lastClicked;
  private final StreetsideRecord record;
  private boolean nothingHighlighted;
  private boolean imageHighlighted;

  /**
   * Main constructor.
   */
  public SelectMode() {
    record = StreetsideRecord.getInstance();
  }

  @Override
  public void mousePressed(MouseEvent e) {
    if (e.getButton() != MouseEvent.BUTTON1) {
      return;
    }
    StreetsideAbstractImage closest = getClosest(e.getPoint());
    if (!(MainApplication.getLayerManager().getActiveLayer() instanceof StreetsideLayer)
            && closest != null && MainApplication.getMap().mapMode == MainApplication.getMap().mapModeSelect) {
      lastClicked = this.closest;
      StreetsideLayer.getInstance().getData().setSelectedImage(closest);
      return;
    } else if (MainApplication.getLayerManager().getActiveLayer() != StreetsideLayer.getInstance()) {
      return;
    }
    // Double click
    if (e.getClickCount() == 2 && StreetsideLayer.getInstance().getData().getSelectedImage() != null && closest != null) {
      closest.getSequence().getImages().forEach(StreetsideLayer.getInstance().getData()::addMultiSelectedImage);
    }
    lastClicked = this.closest;
    this.closest = closest;
    if (closest != null && StreetsideLayer.getInstance().getData().getMultiSelectedImages().contains(closest)) {
      return;
    }
    // ctrl+click
    if (e.getModifiers() == (InputEvent.BUTTON1_MASK | InputEvent.CTRL_MASK) && closest != null) {
      StreetsideLayer.getInstance().getData().addMultiSelectedImage(closest);
      // shift + click
    } else if (
            e.getModifiers() == (InputEvent.BUTTON1_MASK | InputEvent.SHIFT_MASK)
                    && lastClicked instanceof StreetsideImage
            ) {
      if (this.closest != null
              && this.closest.getSequence() == (lastClicked).getSequence()) {
        int i = this.closest.getSequence().getImages().indexOf(this.closest);
        int j = lastClicked.getSequence().getImages().indexOf(lastClicked);
        StreetsideLayer.getInstance().getData().addMultiSelectedImage(
                i < j
                        ? new ConcurrentSkipListSet<>(this.closest.getSequence().getImages().subList(i, j + 1))
                        : new ConcurrentSkipListSet<>(this.closest.getSequence().getImages().subList(j, i + 1))
        );
      }
      // click
    } else {
      StreetsideLayer.getInstance().getData().setSelectedImage(closest);
    }
  }

  @Override
  public void mouseDragged(MouseEvent e) {
    StreetsideAbstractImage highlightImg = StreetsideLayer.getInstance().getData().getHighlightedImage();
    if (
            MainApplication.getLayerManager().getActiveLayer() == StreetsideLayer.getInstance()
                && SwingUtilities.isLeftMouseButton(e)
                && highlightImg != null && highlightImg.getLatLon() != null
            ) {
      Point highlightImgPoint = MainApplication.getMap().mapView.getPoint(highlightImg.getTempLatLon());
      if (e.isShiftDown()) { // turn
        StreetsideLayer.getInstance().getData().getMultiSelectedImages().parallelStream().filter(img -> !(img instanceof StreetsideImage) || StreetsideProperties.DEVELOPER.get())
                .forEach(img -> img.turn(Math.toDegrees(Math.atan2(e.getX() - highlightImgPoint.getX(), -e.getY() + highlightImgPoint.getY())) - highlightImg.getTempHe()));
      } else { // move
        LatLon eventLatLon = MainApplication.getMap().mapView.getLatLon(e.getX(), e.getY());
        LatLon imgLatLon = MainApplication.getMap().mapView.getLatLon(highlightImgPoint.getX(), highlightImgPoint.getY());
        StreetsideLayer.getInstance().getData().getMultiSelectedImages().parallelStream().filter(img -> !(img instanceof StreetsideImage) || StreetsideProperties.DEVELOPER.get())
                .forEach(img -> img.move(eventLatLon.getX() - imgLatLon.getX(), eventLatLon.getY() - imgLatLon.getY()));
      }
      StreetsideLayer.invalidateInstance();
    }
  }

  @Override
  public void mouseReleased(MouseEvent e) {
    final StreetsideData data = StreetsideLayer.getInstance().getData();
    if (data.getSelectedImage() == null) {
      return;
    }
    if (!Objects.equals(data.getSelectedImage().getTempHe(), data.getSelectedImage().getMovingHe())) {
      double from = data.getSelectedImage().getTempHe();
      double to = data.getSelectedImage().getMovingHe();
      record.addCommand(new CommandTurn(data.getMultiSelectedImages(), to - from));
    } else if (!Objects.equals(data.getSelectedImage().getTempLatLon(), data.getSelectedImage().getMovingLatLon())) {
      LatLon from = data.getSelectedImage().getTempLatLon();
      LatLon to = data.getSelectedImage().getMovingLatLon();
      record.addCommand(new CommandMove(data.getMultiSelectedImages(), to.getX() - from.getX(), to.getY() - from.getY()));
    }
    data.getMultiSelectedImages().parallelStream().filter(Objects::nonNull).forEach(StreetsideAbstractImage::stopMoving);
    StreetsideLayer.invalidateInstance();
  }

  /**
   * Checks if the mouse is over pictures.
   */
  @Override
  public void mouseMoved(MouseEvent e) {
    if (MainApplication.getLayerManager().getActiveLayer() instanceof OsmDataLayer
            && MainApplication.getMap().mapMode != MainApplication.getMap().mapModeSelect) {
      return;
    }
    if (!StreetsideProperties.HOVER_ENABLED.get()) {
      return;
    }

    StreetsideAbstractImage closestTemp = getClosest(e.getPoint());

    final OsmDataLayer editLayer = MainApplication.getLayerManager().getEditLayer();
    if (editLayer != null) {
      if (closestTemp != null && !imageHighlighted) {
        if (MainApplication.getMap().mapMode != null) {
          MainApplication.getMap().mapMode.putValue("active", Boolean.FALSE);
        }
        imageHighlighted = true;
      } else if (closestTemp == null && imageHighlighted && nothingHighlighted) {
        if (MainApplication.getMap().mapMode != null) {
          MainApplication.getMap().mapMode.putValue("active", Boolean.TRUE);
        }
        nothingHighlighted = false;
      } else if (imageHighlighted && !nothingHighlighted && editLayer.data != null) {
        for (OsmPrimitive primivitive : MainApplication.getLayerManager().getEditLayer().data.allPrimitives()) {
          primivitive.setHighlighted(false);
        }
        imageHighlighted = false;
        nothingHighlighted = true;
      }
    }

    if (StreetsideLayer.getInstance().getData().getHighlightedImage() != closestTemp && closestTemp != null) {
      StreetsideLayer.getInstance().getData().setHighlightedImage(closestTemp);
      StreetsideMainDialog.getInstance().setImage(closestTemp);
      StreetsideMainDialog.getInstance().updateImage(false);

    } else if (StreetsideLayer.getInstance().getData().getHighlightedImage() != closestTemp && closestTemp == null) {
      StreetsideLayer.getInstance().getData().setHighlightedImage(null);
      StreetsideMainDialog.getInstance().setImage(StreetsideLayer.getInstance().getData().getSelectedImage());
      StreetsideMainDialog.getInstance().updateImage();
    }

    StreetsideLayer.invalidateInstance();
  }

  @Override
  public void paint(Graphics2D g, MapView mv, Bounds box) {
  }

  @Override
  public String toString() {
    return tr("Select mode");
  }
}
