diff --git a/Mage.Plugins/Mage.Rating.Plugin/pom.xml b/Mage.Plugins/Mage.Rating.Plugin/pom.xml
new file mode 100644
index 0000000000..38ab09278a
--- /dev/null
+++ b/Mage.Plugins/Mage.Rating.Plugin/pom.xml
@@ -0,0 +1,77 @@
+
+
+ 4.0.0
+
+
+ org.mage
+ Mage-Plugins
+ 0.3
+
+
+ org.mage
+ Mage-Rating-Plugin
+ jar
+ ${plugin-version}
+ Mage Rating Plugin
+ Plugin that rates cards
+
+
+
+ org.mage
+ Mage-Card-Plugin
+ 0.3
+
+
+ org.mage
+ Mage-Sets
+ 0.3
+
+
+ com.googlecode.jspf
+ jspf-core
+ ${jspf-version}
+
+
+ log4j
+ log4j
+ 1.2.9
+
+
+ com.google.collections
+ google-collections
+ 1.0
+
+
+ com.mortennobel
+ java-image-scaling
+ 0.8.4
+
+
+ org.swinglabs
+ swingx
+ 1.6.1
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ 1.6
+
+
+
+
+
+ mage-card-plugin
+
+
+
+ 0.1
+ 0.9.1
+
+
diff --git a/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/RateCallback.java b/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/RateCallback.java
new file mode 100644
index 0000000000..2f22f93aba
--- /dev/null
+++ b/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/RateCallback.java
@@ -0,0 +1,65 @@
+package org.mage.plugins.rating;
+
+import java.awt.Image;
+import java.awt.event.MouseEvent;
+import java.awt.image.BufferedImage;
+
+import mage.cards.Card;
+import mage.cards.MageCard;
+import mage.cards.action.ActionCallback;
+import mage.cards.action.TransferData;
+
+import org.jdesktop.swingx.JXPanel;
+import org.mage.plugins.rating.ui.BigCard;
+import org.mage.plugins.rating.ui.GuiDisplayUtil;
+import org.mage.plugins.rating.ui.ImageHelper;
+
+public class RateCallback implements ActionCallback {
+
+ private Card card1;
+ private Card card2;
+ private RateThread callback;
+ private BigCard bigCard;
+
+ public RateCallback(Card card1, Card card2, RateThread callback, BigCard bigCard) {
+ this.card1 = card1;
+ this.card2 = card2;
+ this.callback = callback;
+ this.bigCard = bigCard;
+ }
+
+ @Override
+ public void mouseClicked(MouseEvent arg0, TransferData arg1) {
+ this.callback.reportResult(card1, card2);
+ }
+
+ @Override
+ public void mouseEntered(MouseEvent arg0, TransferData arg1) {
+ MageCard card = (MageCard)arg1.component;
+ Image image = card.getImage();
+ if (image != null && image instanceof BufferedImage) {
+ image = ImageHelper.getResizedImage((BufferedImage) image, bigCard.getWidth(), bigCard.getHeight());
+ bigCard.setCard(card.getOriginal().getId(), image, card.getOriginal().getRules());
+ bigCard.showTextComponent();
+ if (card.getOriginal().isAbility()) {
+ bigCard.showTextComponent();
+ } else {
+ bigCard.hideTextComponent();
+ };
+ } else {
+ JXPanel panel = GuiDisplayUtil.getDescription(card.getOriginal(), bigCard.getWidth(), bigCard.getHeight());
+ panel.setVisible(true);
+ bigCard.hideTextComponent();
+ bigCard.addJXPanel(card.getOriginal().getId(), panel);
+ }
+ }
+
+ @Override
+ public void mouseExited(MouseEvent arg0, TransferData arg1) {
+ }
+
+ @Override
+ public void mouseMoved(MouseEvent arg0, TransferData arg1) {
+ }
+
+}
diff --git a/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/RateFrame.java b/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/RateFrame.java
new file mode 100644
index 0000000000..5f232727f5
--- /dev/null
+++ b/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/RateFrame.java
@@ -0,0 +1,88 @@
+package org.mage.plugins.rating;
+
+import java.awt.Color;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+
+import org.apache.log4j.Logger;
+import org.mage.plugins.rating.results.ResultHandler;
+import org.mage.plugins.rating.ui.BigCard;
+
+public class RateFrame extends JFrame {
+
+ private static Logger log = Logger.getLogger(RateFrame.class);
+ private BigCard bigCard;
+
+ public RateFrame() {
+ setTitle("Mage Rate Cards, version 0.1");
+
+ try {
+ UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
+ } catch (Exception ex) {
+ log.error(ex.getMessage(), ex);
+ }
+
+ int width = 621;
+ int height = 384;
+ setSize(width, height);
+ int w = getGraphicsConfiguration().getBounds().width;
+ int h = getGraphicsConfiguration().getBounds().height;
+ setLocation((w - width)/2, (h - height)/2);
+ setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+ setLayout(null);
+
+ bigCard = new BigCard();
+ bigCard.setBounds(20, 10, RateThread.bigCardDimension.frameWidth, RateThread.bigCardDimension.frameHeight);
+ bigCard.setBorder(BorderFactory.createLineBorder(Color.gray));
+ add(bigCard);
+
+ JLabel label = new JLabel("The results are stored for every 10 compare.");
+ label.setBounds(290, 270, 300, 30);
+ add(label);
+
+ JButton rate = new JButton("Create results.txt");
+ rate.setBounds(340, 230, 120, 25);
+ rate.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ try {
+ ResultHandler.getInstance().rate();
+ JOptionPane.showMessageDialog(null, "Done! Find results in ratings.txt file.");
+ } catch (Exception e1) {
+ e1.printStackTrace();
+ JOptionPane.showMessageDialog(null, "Some error occured! Find more details in logs.");
+ }
+ }
+ });
+ add(rate);
+ }
+
+ public void startRating() {
+ RateThread.getInstance().start(this, this.bigCard);
+ }
+
+ public static void main(String args[]) {
+ Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+ public void uncaughtException(Thread t, Throwable e) {
+ log.error(e.getMessage(), e);
+ }
+ });
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ RateFrame frame = new RateFrame();
+ frame.setVisible(true);
+ frame.startRating();
+ }
+ });
+ }
+
+ private static final long serialVersionUID = -5836021378309984439L;
+}
diff --git a/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/RateThread.java b/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/RateThread.java
new file mode 100644
index 0000000000..9fee2c1463
--- /dev/null
+++ b/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/RateThread.java
@@ -0,0 +1,109 @@
+package org.mage.plugins.rating;
+
+import java.awt.Component;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import javax.swing.JFrame;
+
+import mage.Constants.CardType;
+import mage.cards.Card;
+import mage.cards.CardDimensions;
+import mage.cards.MageCard;
+import mage.view.CardView;
+
+import org.mage.plugins.card.CardPluginImpl;
+import org.mage.plugins.rating.cards.CardsStorage;
+import org.mage.plugins.rating.results.Rating;
+import org.mage.plugins.rating.results.ResultHandler;
+import org.mage.plugins.rating.ui.BigCard;
+
+public class RateThread extends Thread {
+
+ private static RateThread fInstance = new RateThread();
+ private CardPluginImpl impl = new CardPluginImpl();
+ public static CardDimensions dimensions = new CardDimensions(0.4);
+ public static CardDimensions bigCardDimension = new CardDimensions(0.8);
+ private JFrame frame;
+ private MageCard mageCard1;
+ private MageCard mageCard2;
+ private BigCard bigCard;
+ private boolean stop = false;
+
+ private static List results = new ArrayList();
+
+ public RateThread() {
+ setDaemon(true);
+ start();
+ }
+
+ public static RateThread getInstance() {
+ return fInstance;
+ }
+
+ @Override
+ public synchronized void run() {
+ while (!stop) {
+ try {
+ Card card1 = getRandomUniqueNonLandCard(null);
+ Card card2 = getRandomUniqueNonLandCard(card1);
+
+ mageCard1 = impl.getMageCard(new CardView(card1), dimensions, UUID.randomUUID(), new RateCallback(card1, card2, this, bigCard));
+ mageCard1.setCardBounds(bigCardDimension.frameWidth + 80, 10, dimensions.frameWidth, dimensions.frameHeight);
+ frame.add(mageCard1);
+
+ mageCard2 = impl.getMageCard(new CardView(card2), dimensions, UUID.randomUUID(), new RateCallback(card2, card1, this, bigCard));
+ mageCard2.setCardBounds(bigCardDimension.frameWidth + 80 + dimensions.frameWidth + 30, 10, dimensions.frameWidth, dimensions.frameHeight);
+ frame.add(mageCard2);
+
+ frame.validate();
+
+ wait();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ protected Card getRandomUniqueNonLandCard(Card previousCard) {
+ int count = CardsStorage.getAllCards().size();
+ Card card1 = CardsStorage.getAllCards().get((int)(Math.random()*count));
+ while (card1.getCardType().contains(CardType.LAND) || card1.getName().equals(previousCard)) {
+ card1 = CardsStorage.getAllCards().get((int)(Math.random()*count));
+ }
+ return card1;
+ }
+
+ public void start(JFrame frame, BigCard bigCard) {
+ this.frame = frame;
+ this.bigCard = bigCard;
+ }
+
+ protected synchronized void generateNext() {
+ notify();
+ }
+
+ public void reportResult(Card card1, Card card2) {
+ results.add(new Rating(card1.getName(), card2.getName()));
+ removeCard(mageCard1);
+ removeCard(mageCard2);
+ frame.validate();
+ if (results.size() == 10) {
+ ResultHandler.getInstance().save(results);
+ results.clear();
+ }
+ generateNext();
+ }
+
+ private void removeCard(Component component) {
+ if (component != null) {
+ frame.remove(component);
+ }
+ }
+
+ public synchronized void stopRating() {
+ this.stop = true;
+ notify();
+ }
+}
diff --git a/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/cards/CardsStorage.java b/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/cards/CardsStorage.java
new file mode 100644
index 0000000000..80a3dd8e1b
--- /dev/null
+++ b/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/cards/CardsStorage.java
@@ -0,0 +1,22 @@
+package org.mage.plugins.rating.cards;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import mage.cards.Card;
+import mage.cards.ExpansionSet;
+import mage.sets.Sets;
+
+public class CardsStorage {
+ private static List allCards = new ArrayList();
+
+ static {
+ for (ExpansionSet set: Sets.getInstance().values()) {
+ allCards.addAll(set.createCards());
+ }
+ }
+
+ public static List getAllCards() {
+ return allCards;
+ }
+}
diff --git a/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/results/Rating.java b/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/results/Rating.java
new file mode 100644
index 0000000000..72c2848c16
--- /dev/null
+++ b/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/results/Rating.java
@@ -0,0 +1,11 @@
+package org.mage.plugins.rating.results;
+
+public class Rating {
+ public String winnerCardName;
+ public String loserCardName;
+
+ public Rating(String win, String lose) {
+ this.winnerCardName = win;
+ this.loserCardName = lose;
+ }
+}
diff --git a/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/results/ResultHandler.java b/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/results/ResultHandler.java
new file mode 100644
index 0000000000..7e8b541a1f
--- /dev/null
+++ b/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/results/ResultHandler.java
@@ -0,0 +1,122 @@
+package org.mage.plugins.rating.results;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.UUID;
+import java.util.Map.Entry;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.log4j.Logger;
+import org.mage.plugins.rating.util.MapSorter;
+
+public class ResultHandler {
+
+ private static ResultHandler fInstance = new ResultHandler();
+ private static Map ratings = new LinkedHashMap();
+ private static String newLine = System.getProperty("line.separator");
+ private static Pattern scorePattern = Pattern.compile("([^|]*)[|]+ > [|]+([^|]*)");
+ private static Logger log = Logger.getLogger(ResultHandler.class);
+
+ static {
+ File file = new File("results");
+ if (!file.exists()) {
+ file.mkdir();
+ }
+ }
+
+ public static ResultHandler getInstance() {
+ return fInstance;
+ }
+
+ public void save(List results) {
+ File f = new File("results" + File.separator + UUID.randomUUID() + ".txt");
+ try {
+ if (f.createNewFile()) {
+ FileOutputStream fos = new FileOutputStream(f);
+ BufferedOutputStream b = new BufferedOutputStream(fos);
+ for (Rating r : results) {
+ String line = r.winnerCardName + "| > |" + r.loserCardName + newLine;
+ b.write(line.getBytes());
+ }
+ b.close();
+ fos.close();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void rate() throws Exception {
+ ratings.clear();
+ File file = new File("results");
+ File ratingFile = new File("ratings.txt");
+ if (ratingFile.exists()) {
+ if (!ratingFile.delete()) {
+ throw new RuntimeException("Couldn't delete previous ratings.txt file");
+ }
+ }
+ if (ratingFile.createNewFile()) {
+ for (File f : file.listFiles()) {
+ if (!f.getName().equals("rating.txt")) {
+ parseFile(f);
+ }
+ }
+ ratings = MapSorter.sortByValue(ratings);
+ FileOutputStream fos = new FileOutputStream(ratingFile);
+ BufferedOutputStream b = new BufferedOutputStream(fos);
+ for (Entry entry : ratings.entrySet()) {
+ String line = entry.getValue() + " : " + entry.getKey() + newLine;
+ b.write(line.getBytes());
+ }
+ b.close();
+ fos.close();
+ }
+ }
+
+ private void parseFile(File f) throws Exception {
+ Scanner s = new Scanner(f);
+ while (s.hasNextLine()) {
+ String line = s.nextLine();
+ Matcher m = scorePattern.matcher(line);
+ if (m.matches()) {
+ String winner = m.group(1);
+ String loser = m.group(2);
+ Integer winnerRating = ratings.get(winner);
+ if (winnerRating == null)
+ winnerRating = 1000;
+ Integer loserRating = ratings.get(loser);
+ if (loserRating == null)
+ loserRating = 1000;
+ Integer newWinnerRating = coundEloRating(winnerRating, loserRating, true);
+ Integer newLoserRating = coundEloRating(winnerRating, loserRating, false);
+ log.info("Winner: " + winnerRating + " >> " + newWinnerRating);
+ log.info("Loser: " + loserRating + " >> " + newLoserRating);
+ ratings.put(winner, newWinnerRating);
+ ratings.put(loser, newLoserRating);
+ } else {
+ log.warn("Doesn't match rate pattern: " + line);
+ }
+ }
+ s.close();
+ }
+
+ /**
+ * Count rating using Elo Rating System.
+ *
+ * @param ra
+ * @param rb
+ * @return
+ */
+ private Integer coundEloRating(Integer ra, Integer rb, boolean firstWon) {
+ double d = (rb - ra) / 400.0;
+ double expected = 1.0d / (1 + Math.pow(10, d));
+ double actual = firstWon ? 1 : 0;
+ return Integer.valueOf((int) Math.round(ra + 32 * (actual - expected)));
+ }
+}
\ No newline at end of file
diff --git a/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/ui/BigCard.java b/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/ui/BigCard.java
new file mode 100644
index 0000000000..8abf2b4c98
--- /dev/null
+++ b/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/ui/BigCard.java
@@ -0,0 +1,166 @@
+/*
+* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without modification, are
+* permitted provided that the following conditions are met:
+*
+* 1. Redistributions of source code must retain the above copyright notice, this list of
+* conditions and the following disclaimer.
+*
+* 2. Redistributions in binary form must reproduce the above copyright notice, this list
+* of conditions and the following disclaimer in the documentation and/or other materials
+* provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+* The views and conclusions contained in the software and documentation are those of the
+* authors and should not be interpreted as representing official policies, either expressed
+* or implied, of BetaSteward_at_googlemail.com.
+*/
+
+/*
+ * BigCard.java
+ *
+ * Created on Jan 18, 2010, 3:21:33 PM
+ */
+
+package org.mage.plugins.rating.ui;
+
+import static mage.constants.Constants.CONTENT_MAX_XOFFSET;
+import static mage.constants.Constants.FRAME_MAX_HEIGHT;
+import static mage.constants.Constants.FRAME_MAX_WIDTH;
+import static mage.constants.Constants.TEXT_MAX_HEIGHT;
+import static mage.constants.Constants.TEXT_MAX_WIDTH;
+import static mage.constants.Constants.TEXT_MAX_YOFFSET;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.Rectangle;
+import java.util.List;
+import java.util.UUID;
+
+import javax.swing.text.BadLocationException;
+import javax.swing.text.StyledDocument;
+
+import org.jdesktop.swingx.JXPanel;
+
+/**
+ *
+ * @author BetaSteward_at_googlemail.com
+ */
+public class BigCard extends javax.swing.JPanel {
+
+ protected Image bigImage;
+ protected UUID cardId;
+ protected JXPanel panel;
+ protected boolean initState;
+
+ public BigCard() {
+ initComponents();
+ }
+
+ protected void initBounds() {
+ initState = true;
+ scrollPane.setBounds(20, 230, 210, 120);
+ scrollPane.setBounds(new Rectangle(CONTENT_MAX_XOFFSET, TEXT_MAX_YOFFSET, TEXT_MAX_WIDTH, TEXT_MAX_HEIGHT));
+ }
+
+ public void setCard(UUID cardId, Image image, List strings) {
+ if (this.cardId == null || !this.cardId.equals(cardId)) {
+ if (this.panel != null) remove(this.panel);
+ this.cardId = cardId;
+ bigImage = image;
+ this.repaint();
+ drawText(strings);
+ }
+ }
+
+ public UUID getCardId() {
+ return cardId;
+ }
+
+ private void drawText(java.util.List strings) {
+ text.setText("");
+ StyledDocument doc = text.getStyledDocument();
+
+ try {
+ for (String line: strings) {
+ doc.insertString(doc.getLength(), line + "\n", doc.getStyle("regular"));
+ }
+ } catch (BadLocationException ble) { }
+ text.setCaretPosition(0);
+ }
+
+ @Override
+ public void paintComponent(Graphics graphics) {
+ if (bigImage != null)
+ graphics.drawImage(bigImage, 0, 0, this);
+ super.paintComponent(graphics);
+ }
+
+ public void hideTextComponent() {
+ this.scrollPane.setVisible(false);
+ }
+
+ public void showTextComponent() {
+ if (!initState) {initBounds();}
+ this.scrollPane.setVisible(true);
+ }
+
+ public void addJXPanel(UUID cardId, JXPanel jxPanel) {
+ bigImage = null;
+ synchronized (this) {
+ if (this.panel != null) remove(this.panel);
+ this.panel = jxPanel;
+ add(jxPanel);
+ }
+ this.repaint();
+ }
+
+
+ /** This method is called from within the constructor to
+ * initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is
+ * always regenerated by the Form Editor.
+ */
+ @SuppressWarnings("unchecked")
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ scrollPane = new javax.swing.JScrollPane();
+ text = new javax.swing.JTextPane();
+
+ setFocusable(false);
+ setMinimumSize(new Dimension(FRAME_MAX_WIDTH, FRAME_MAX_HEIGHT));
+ setOpaque(false);
+ setPreferredSize(getMinimumSize());
+ setLayout(null);
+
+ scrollPane.setBorder(null);
+ scrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
+ scrollPane.setOpaque(false);
+
+ text.setEditable(false);
+ text.setFocusable(false);
+ text.setOpaque(false);
+ scrollPane.setViewportView(text);
+
+ add(scrollPane);
+ }// //GEN-END:initComponents
+
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JScrollPane scrollPane;
+ private javax.swing.JTextPane text;
+ // End of variables declaration//GEN-END:variables
+
+}
diff --git a/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/ui/GuiDisplayUtil.java b/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/ui/GuiDisplayUtil.java
new file mode 100644
index 0000000000..3850b66858
--- /dev/null
+++ b/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/ui/GuiDisplayUtil.java
@@ -0,0 +1,104 @@
+package org.mage.plugins.rating.ui;
+
+import java.awt.Color;
+import java.awt.Font;
+
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.SwingConstants;
+
+import mage.Constants.CardType;
+import mage.utils.CardUtil;
+import mage.view.CardView;
+
+import org.jdesktop.swingx.JXPanel;
+
+public class GuiDisplayUtil {
+ private static final Font cardNameFont = new Font("Calibri", Font.BOLD, 15);
+
+ public static JXPanel getDescription(CardView card, int width, int height) {
+ JXPanel descriptionPanel = new JXPanel();
+
+ //descriptionPanel.setAlpha(.8f);
+ descriptionPanel.setBounds(0, 0, width, height);
+ descriptionPanel.setVisible(false);
+ descriptionPanel.setLayout(null);
+
+ //descriptionPanel.setBorder(BorderFactory.createLineBorder(Color.green));
+
+ JButton j = new JButton("");
+ j.setBounds(0, 0, width, height);
+ j.setBackground(Color.black);
+ j.setLayout(null);
+
+ JLabel name = new JLabel("Wrath of God");
+ name.setBounds(5, 5, width - 90, 20);
+ name.setForeground(Color.white);
+ name.setFont(cardNameFont);
+ //name.setBorder(BorderFactory.createLineBorder(Color.green));
+ j.add(name);
+
+ JLabel cost = new JLabel("B R G W U");
+ cost.setBounds(width - 85, 5, 77, 20);
+ cost.setForeground(Color.white);
+ cost.setFont(cardNameFont);
+ //cost.setBorder(BorderFactory.createLineBorder(Color.green));
+ cost.setHorizontalAlignment(SwingConstants.RIGHT);
+ j.add(cost);
+
+ JLabel type = new JLabel("Creature - Goblin Shaman");
+ type.setBounds(5, 70, width - 8, 20);
+ type.setForeground(Color.white);
+ type.setFont(cardNameFont);
+ //type.setBorder(BorderFactory.createLineBorder(Color.green));
+ j.add(type);
+
+ JLabel cardText = new JLabel();
+ cardText.setBounds(5, 100, width - 8, 260);
+ cardText.setForeground(Color.white);
+ cardText.setFont(cardNameFont);
+ cardText.setVerticalAlignment(SwingConstants.TOP);
+ //cardText.setBorder(new EtchedBorder());
+ j.add(cardText);
+
+ name.setText(card.getName());
+ cost.setText(card.getManaCost().toString());
+ String typeText = "";
+ String delimiter = card.getCardTypes().size() > 1 ? " - " : "";
+ for (CardType t : card.getCardTypes()) {
+ typeText += t;
+ typeText += delimiter;
+ delimiter = " "; // next delimiters are just spaces
+ }
+ type.setText(typeText);
+ cardText.setText(""+card.getRules()+"");
+
+ if (CardUtil.isCreature(card)) {
+ JLabel pt = new JLabel(card.getPower() + "/" + card.getToughness());
+ pt.setBounds(width - 50, height - 30, 40, 20);
+ pt.setForeground(Color.white);
+ pt.setFont(cardNameFont);
+ pt.setHorizontalAlignment(JLabel.RIGHT);
+ j.add(pt);
+ }
+
+ descriptionPanel.add(j);
+
+ return descriptionPanel;
+ }
+
+ public static String cleanString(String in) {
+ StringBuffer out = new StringBuffer();
+ char c;
+ for (int i = 0; i < in.length(); i++) {
+ c = in.charAt(i);
+ if (c == ' ' || c == '-')
+ out.append('_');
+ else if (Character.isLetterOrDigit(c)) {
+ out.append(c);
+ }
+ }
+ return out.toString().toLowerCase();
+ }
+
+}
diff --git a/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/ui/ImageHelper.java b/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/ui/ImageHelper.java
new file mode 100644
index 0000000000..085078f80f
--- /dev/null
+++ b/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/ui/ImageHelper.java
@@ -0,0 +1,23 @@
+package org.mage.plugins.rating.ui;
+
+import java.awt.image.BufferedImage;
+
+import com.mortennobel.imagescaling.ResampleOp;
+
+/**
+ * Contains utility methods to work with images.
+ *
+ * @author ayrat
+ */
+public class ImageHelper {
+ /**
+ * Returns an image scaled to the size appropriate for the card picture
+ * panel
+ */
+ public static BufferedImage getResizedImage(BufferedImage original, int width, int height) {
+ ResampleOp resampleOp = new ResampleOp(width, height);
+ BufferedImage image = resampleOp.filter(original, null);
+ return image;
+ }
+
+}
diff --git a/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/util/MapSorter.java b/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/util/MapSorter.java
new file mode 100644
index 0000000000..7c902d4a29
--- /dev/null
+++ b/Mage.Plugins/Mage.Rating.Plugin/src/main/java/org/mage/plugins/rating/util/MapSorter.java
@@ -0,0 +1,28 @@
+package org.mage.plugins.rating.util;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+public class MapSorter {
+
+ @SuppressWarnings("unchecked")
+ public static Map sortByValue(Map map) {
+ List list = new LinkedList(map.entrySet());
+ Collections.sort(list, new Comparator() {
+ public int compare(Object o2, Object o1) {
+ return ((Comparable) ((Map.Entry) (o1)).getValue()).compareTo(((Map.Entry) (o2)).getValue());
+ }
+ });
+ Map result = new LinkedHashMap();
+ for (Iterator it = list.iterator(); it.hasNext();) {
+ Map.Entry entry = (Map.Entry) it.next();
+ result.put(entry.getKey(), entry.getValue());
+ }
+ return result;
+ }
+}
diff --git a/Mage.Plugins/Mage.Rating.Plugin/src/main/resources/log4j.properties b/Mage.Plugins/Mage.Rating.Plugin/src/main/resources/log4j.properties
new file mode 100644
index 0000000000..08d7f0e74f
--- /dev/null
+++ b/Mage.Plugins/Mage.Rating.Plugin/src/main/resources/log4j.properties
@@ -0,0 +1,8 @@
+#default levels
+log4j.rootLogger=debug, console
+
+#console log
+log4j.appender.console=org.apache.log4j.ConsoleAppender
+log4j.appender.console.layout=org.apache.log4j.PatternLayout
+log4j.appender.console.layout.ConversionPattern=%-5p [%d{yyyy-MM-dd HH:mm [ss:SSS]}] %C{1}[%t]: %m%n
+log4j.appender.console.Threshold=DEBUG
\ No newline at end of file