package magic.ui.widget.duel.viewer;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Graphics;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import magic.data.GeneralConfig;
import magic.data.MagicIcon;
import magic.model.MagicType;
import magic.ui.MagicImages;
import magic.ui.duel.viewerinfo.PermanentViewerInfo;
import magic.ui.dialog.prefs.ImageSizePresets;
import magic.ui.theme.Theme;
import magic.ui.theme.ThemeFactory;
import magic.ui.helpers.ImageHelper;
import magic.ui.utility.ImageDrawingUtils;
import magic.ui.utility.MagicStyle;
import magic.ui.FontsAndBorders;
@SuppressWarnings("serial")
public class ImagePermanentViewer extends JPanel {
private static final GeneralConfig CONFIG = GeneralConfig.getInstance();
private static final Color MOUSE_OVER_COLOR = MagicStyle.getRolloverColor();
private static final Color MOUSE_OVER_TCOLOR = MagicStyle.getTranslucentColor(MOUSE_OVER_COLOR, 30);
private static final Dimension LOGICAL_IMAGE_SIZE = ImageSizePresets.getDefaultSize();
private static final int LOGICAL_X_MARGIN = 50;
private static final int LOGICAL_Y_MARGIN = 70;
private final ImagePermanentsViewer viewer;
public final PermanentViewerInfo permanentInfo;
public final List<PermanentViewerInfo> linkedInfos;
private final Dimension logicalSize;
private final List<Rectangle> linkedLogicalRectangles;
private List<Rectangle> linkedScreenRectangles;
private Point logicalPosition;
private int logicalRow=1;
private boolean isMouseOver = false;
private static int currentCardIndex = -1;
private long highlightedId = 0;
public ImagePermanentViewer(final ImagePermanentsViewer viewer,final PermanentViewerInfo permanentInfo) {
this.viewer=viewer;
this.permanentInfo=permanentInfo;
linkedInfos=new ArrayList<PermanentViewerInfo>();
buildLinkedPermanents(linkedInfos,permanentInfo);
linkedLogicalRectangles=new ArrayList<Rectangle>();
logicalSize=calculateLogicalSize(linkedLogicalRectangles);
linkedScreenRectangles=Collections.emptyList();
setOpaque(false);
setMouseListener();
setMouseMotionListener();
setMouseWheelListener();
}
private void setMouseWheelListener() {
addMouseWheelListener(new MouseWheelListener() {
@Override
public void mouseWheelMoved(MouseWheelEvent event) {
final int cardIndex = getPermanentInfoIndexAt(event.getX(), event.getY());
if (cardIndex >= 0) {
if (event.getWheelRotation() < 0) { // rotate mousewheel forward
showCardPopup(cardIndex);
} else if (event.getWheelRotation() > 0) { // rotate mousewheel back
viewer.getController().hideInfo();
}
}
}
});
}
private void setMouseMotionListener() {
addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseMoved(final MouseEvent event) {
final int cardIndex = getPermanentInfoIndexAt(event.getX(), event.getY());
final boolean isCardChanged = (currentCardIndex != cardIndex);
if (cardIndex >= 0) {
if (isCardChanged) {
if (!CONFIG.isMouseWheelPopup() || viewer.getController().isPopupVisible()) {
showCardPopup(cardIndex);
}
}
} else {
viewer.getController().hideInfo();
}
currentCardIndex = cardIndex;
if (linkedScreenRectangles.size() > 1) {
repaint();
}
}
});
}
private void setMouseListener() {
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(final MouseEvent event) {
if (CONFIG.isTouchscreen()) {
final int cardIndex = getPermanentInfoIndexAt(event.getX(), event.getY());
final boolean isDoubleClick = event.getClickCount() == 2;
if (cardIndex >= 0 && isDoubleClick) {
viewer.getController().processClick(linkedInfos.get(cardIndex).permanent);
viewer.getController().hideInfo();
}
}
}
@Override
public void mouseReleased(MouseEvent event) {
if (!CONFIG.isTouchscreen()) {
final int cardIndex = getPermanentInfoIndexAt(event.getX(), event.getY());
if (cardIndex >= 0 && SwingUtilities.isLeftMouseButton(event)) {
viewer.getController().processClick(linkedInfos.get(cardIndex).permanent);
}
}
}
@Override
public void mouseExited(final MouseEvent event) {
viewer.getController().hideInfo();
currentCardIndex = -1;
isMouseOver = false;
repaint();
}
@Override
public void mouseEntered(MouseEvent e) {
isMouseOver = true;
repaint();
}
});
}
private void showCardPopup(int index) {
final PermanentViewerInfo info=linkedInfos.get(index);
final Point pointOnScreen=getLocationOnScreen();
final Rectangle rect=new Rectangle(linkedScreenRectangles.get(index));
rect.x+=pointOnScreen.x;
rect.y+=pointOnScreen.y;
viewer.getController().viewCardPopup(info.permanent, rect, true);
}
private int getPermanentInfoIndexAt(final int x,final int y) {
for (int index=linkedScreenRectangles.size()-1;index>=0;index--) {
final Rectangle rect=linkedScreenRectangles.get(index);
if (x>=rect.x&&y>=rect.y&&x<rect.x+rect.width&&y<rect.y+rect.height) {
return index;
}
}
return -1;
}
private void buildLinkedPermanents(final List<PermanentViewerInfo> aLinkedInfos,final PermanentViewerInfo info) {
for (final PermanentViewerInfo blocker : info.blockers) {
buildLinkedPermanents(aLinkedInfos,blocker);
}
aLinkedInfos.addAll(info.linked);
aLinkedInfos.add(info);
}
private Dimension calculateLogicalSize(final List<Rectangle> aLinkedLogicalRectangles) {
int width=0;
int height=0;
int x=-LOGICAL_X_MARGIN;
for (final PermanentViewerInfo linkedInfo : linkedInfos) {
x+=LOGICAL_X_MARGIN;
final int y=linkedInfo.lowered?LOGICAL_Y_MARGIN:0;
final Rectangle rect;
if (linkedInfo.tapped) {
width = Math.max(width, x + LOGICAL_IMAGE_SIZE.height);
height = Math.max(height, y + LOGICAL_IMAGE_SIZE.width);
rect = new Rectangle(x, y, LOGICAL_IMAGE_SIZE.height, LOGICAL_IMAGE_SIZE.width);
} else {
width = Math.max(width, x + LOGICAL_IMAGE_SIZE.width);
height = Math.max(height, y + LOGICAL_IMAGE_SIZE.height);
rect = new Rectangle(x, y, LOGICAL_IMAGE_SIZE.width, LOGICAL_IMAGE_SIZE.height);
}
aLinkedLogicalRectangles.add(rect);
}
return new Dimension(width,height);
}
@Override
public void setSize(final int width,final int height) {
super.setSize(width,height);
linkedScreenRectangles=new ArrayList<Rectangle>();
for (final Rectangle logicalRect : linkedLogicalRectangles) {
final Rectangle screenRect=new Rectangle();
screenRect.x=(logicalRect.x*width)/logicalSize.width;
screenRect.y=(logicalRect.y*height)/logicalSize.height;
screenRect.width=(logicalRect.width*width)/logicalSize.width;
screenRect.height=(logicalRect.height*height)/logicalSize.height;
linkedScreenRectangles.add(screenRect);
}
}
public int getPosition() {
return permanentInfo.position;
}
public Dimension getLogicalSize() {
return logicalSize;
}
public void setLogicalPosition(final Point logicalPosition) {
this.logicalPosition=logicalPosition;
}
public Point getLogicalPosition() {
return logicalPosition;
}
public void setLogicalRow(final int logicalRow) {
this.logicalRow=logicalRow;
}
public int getLogicalRow() {
return logicalRow;
}
private void drawTappedImage(Graphics2D g2d, BufferedImage image, int x1, int y1) {
g2d.translate(x1, y1);
g2d.rotate(Math.PI / 2);
g2d.drawImage(image, 0, -image.getHeight(), null);
g2d.rotate(-Math.PI / 2);
g2d.translate(-x1, -y1);
}
@Override
public void paintComponent(final Graphics g) {
g.setFont(FontsAndBorders.FONT1);
final FontMetrics metrics = g.getFontMetrics();
final Graphics2D g2d = (Graphics2D)g;
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
final Stroke defaultStroke = g2d.getStroke();
for (int index = 0; index < linkedScreenRectangles.size(); index++) {
final PermanentViewerInfo linkedInfo = linkedInfos.get(index);
final Rectangle linkedRect = linkedScreenRectangles.get(index);
final int x1 = linkedRect.x;
final int y1 = linkedRect.y;
final int x2 = x1 + linkedRect.width - 1;
final int y2 = y1 + linkedRect.height - 1;
final BufferedImage image = ImageHelper.scale(
MagicImages.getCardImage(linkedInfo.permanent),
linkedInfo.tapped ? linkedRect.height : linkedRect.width,
linkedInfo.tapped ? linkedRect.width : linkedRect.height
);
if (linkedInfo.tapped) {
drawTappedImage(g2d, image, x1, y1);
} else {
g2d.drawImage(image, x1, y1, this);
}
ImageDrawingUtils.drawCardId(g, linkedInfo.permanent.getCard().getId(), 0, 0);
// Add overlays, unless card image size is so small the overlays would be unreadable.
if (linkedRect.height > CONFIG.getOverlayMinimumHeight()) {
int ax = x1 + 1;
final int ay = y2 - 17;
// Counters
if (linkedInfo.permanent.hasCounters()) {
ax = ImageDrawingUtils.drawCountersInfo(g, this, linkedInfo.permanent, ax, ay);
}
// Ability icons.
if (linkedInfo.canNotTap) {
g.drawImage(MagicImages.getIcon(MagicIcon.CANNOTTAP).getImage(), ax, ay, this);
ax += 16;
}
ax = ImageDrawingUtils.drawAbilityInfo(g, this, linkedInfo.abilityFlags, ax, ay);
// Mana symbols
if (linkedInfo.permanent.getManaActivations().size() > 0) {
ax = ImageDrawingUtils.drawManaInfo(
g,
this,
linkedInfo.permanent.getManaActivations(),
linkedInfo.permanent.hasType(MagicType.Snow),
ax,
ay
);
}
// Power, toughness, damage
final String pt = linkedInfo.powerToughness;
if (!pt.isEmpty()) {
final String damage = linkedInfo.damage > 0 ? String.valueOf(linkedInfo.damage) : "";
final String shield = linkedInfo.shield > 0 ? String.valueOf(linkedInfo.shield) : "";
final boolean isShieldDamage = damage.length() + shield.length() > 0;
final int ptWidth = metrics.stringWidth(pt);
if (linkedInfo.blocking) {
ImageDrawingUtils.drawCreatureInfo(
g,
metrics,
pt,
ptWidth,
shield,
damage,
x1,
y1,
false
);
} else {
ImageDrawingUtils.drawCreatureInfo(
g,
metrics,
pt,
ptWidth,
shield,
damage,
x2 - ptWidth - 4,
y2 - (isShieldDamage ? 32 : 18),
true
);
}
}
}
// Valid choice selection highlight
if (viewer.isValidChoice(linkedInfo)) {
if (CONFIG.isHighlightOverlay() ||
(CONFIG.isHighlightTheme() &&
ThemeFactory.getInstance().getCurrentTheme().getOptionUseOverlay())) {
final Color choiceColor = viewer.getController().isCombatChoice() ?
ThemeFactory.getInstance().getCurrentTheme().getColor(Theme.COLOR_COMBAT_CHOICE) :
ThemeFactory.getInstance().getCurrentTheme().getChoiceColor();
//draw a transparent overlay of choiceColor
g2d.setPaint(choiceColor);
g2d.fillRect(x1-1,y1-1,x2-x1+2,y2-y1+2);
} else if (!CONFIG.isHighlightNone()) {
final Color choiceColor = viewer.getController().isCombatChoice() ?
ThemeFactory.getInstance().getCurrentTheme().getColor(Theme.COLOR_COMBAT_CHOICE_BORDER) :
ThemeFactory.getInstance().getCurrentTheme().getColor(Theme.COLOR_CHOICE_BORDER);
//draw a one pixel border of choiceColor
g2d.setPaint(new Color(choiceColor.getRGB()));
g2d.setStroke(new BasicStroke(2));
g2d.drawRect(x1+1,y1+1,x2-x1-1,y2-y1-1);
g2d.setStroke(defaultStroke);
}
}
if (isMouseOver) {
paintMouseOverHighlight(g2d, getMouseOverRectangle());
}
if (highlightedId == linkedInfo.permanent.getCard().getId()) {
g2d.setPaint(MagicStyle.getRolloverColor());
g2d.setStroke(new BasicStroke(4));
g2d.drawRect(x1 + 2, y1 + 2, x2 - x1 - 2, y2 - y1 - 2);
g2d.setStroke(defaultStroke);
}
}
}
/**
* draw filled rectangle using translucent color over visible portion of card.
*/
private void paintMouseOverHighlight(final Graphics2D g2d, final Rectangle rect) {
g2d.setPaint(MOUSE_OVER_TCOLOR);
g2d.fillRect(rect.x + 1, rect.y + 1, rect.width - 2, rect.height - 2);
if (linkedScreenRectangles.size() > 1) {
g2d.setPaint(MOUSE_OVER_COLOR);
g2d.drawRect(rect.x + 1, rect.y + 1, rect.width - 2, rect.height - 2);
}
}
private Rectangle getMouseOverRectangle() {
final Point mousePoint = MouseInfo.getPointerInfo().getLocation();
SwingUtilities.convertPointFromScreen(mousePoint, this);
for (int i = linkedScreenRectangles.size()-1; i >= 0; i--) {
final Rectangle rect = linkedScreenRectangles.get(i);
if (rect.contains(mousePoint)) {
return rect;
}
}
return linkedScreenRectangles.get(0);
}
void doShowHighlight(long id) {
highlightedId = id;
repaint();
}
}