/*
 *  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.
 */

package mage.client.deckeditor.table;

import java.awt.Dimension;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumnModel;
import mage.client.MageFrame;
import mage.client.cards.BigCard;
import mage.client.cards.CardEventSource;
import mage.client.cards.ICardGrid;
import mage.client.deckeditor.SortSetting;
import mage.client.plugins.impl.Plugins;
import mage.client.util.Config;
import mage.client.util.Event;
import mage.client.util.ImageHelper;
import mage.client.util.Listener;
import mage.client.util.gui.GuiDisplayUtil;
import mage.constants.CardType;
import mage.constants.EnlargeMode;
import mage.view.CardView;
import mage.view.CardsView;
import org.apache.log4j.Logger;
import org.jdesktop.swingx.JXPanel;
import org.mage.card.arcane.ManaSymbols;
import org.mage.card.arcane.UI;

/**
 * Table Model for card list.
 * 
 * @author nantuko
 */
public class TableModel extends AbstractTableModel implements ICardGrid {

    private static final long serialVersionUID = -528008802935423088L;

    private static final Logger log = Logger.getLogger(TableModel.class);

    protected CardEventSource cardEventSource = new CardEventSource();
    protected BigCard bigCard;
    protected UUID gameId;
    private final Map<UUID, CardView> cards = new LinkedHashMap<>();
    private final Map<String, Integer> cardsNoCopies = new LinkedHashMap<>();
    private final List<CardView> view = new ArrayList<>();
    private Dimension cardDimension;

    private boolean displayNoCopies = false;
    private UpdateCountsCallback updateCountsCallback;

    private final String column[] = { "Qty", "Name", "Cost", "Color", "Type", "Stats", "Rarity", "Set" };

    private SortSetting sortSetting;
    private int recentSortedColumn;
    private boolean recentAscending;

    private boolean numberEditable;
            
    public TableModel() {
        this.numberEditable = false;
    }    
    
    public void clear() {
        this.clearCardEventListeners();
        this.clearCards();
        this.view.clear();
    }

    @Override
    public void loadCards(CardsView showCards, SortSetting sortSetting, BigCard bigCard, UUID gameId) {
        this.loadCards(showCards, sortSetting, bigCard, gameId, true);
    }

    @Override
    public void loadCards(CardsView showCards, SortSetting sortSetting, BigCard bigCard, UUID gameId, boolean merge) {
        if (this.sortSetting == null) {
            this.sortSetting = sortSetting;
        }
        this.bigCard = bigCard;
        this.gameId = gameId;
        int landCount = 0;
        int creatureCount = 0;
        int instantCount = 0;
        int sorceryCount = 0;
        int enchantmentCount = 0;
        if (!merge) {
            this.clearCards();
            for (CardView card : showCards.values()) {
                addCard(card, bigCard, gameId);
            }
        } else {
            for (CardView card : showCards.values()) {
                if (!cards.containsKey(card.getId())) {
                    addCard(card, bigCard, gameId);
                }
                if (updateCountsCallback != null) {
                    if (card.getCardTypes().contains(CardType.LAND)) {
                        landCount++;
                    }
                    if (card.getCardTypes().contains(CardType.CREATURE)) {
                        creatureCount++;
                    }
                    if (card.getCardTypes().contains(CardType.INSTANT)) {
                        instantCount++;
                    }
                    if (card.getCardTypes().contains(CardType.SORCERY)) {
                        sorceryCount++;
                    }
                    if (card.getCardTypes().contains(CardType.ENCHANTMENT)) {
                        enchantmentCount++;
                    }
                }
            }

            // no easy logic for merge :)
            for (Iterator<Entry<UUID, CardView>> i = cards.entrySet().iterator(); i.hasNext();) {
                Entry<UUID, CardView> entry = i.next();
                if (!showCards.containsKey(entry.getKey())) {
                    i.remove();
                    if (displayNoCopies) {
                        String key = entry.getValue().getName() + entry.getValue().getExpansionSetCode() + entry.getValue().getCardNumber();
                        if (cardsNoCopies.containsKey(key)) {
                            Integer count = cardsNoCopies.get(key);
                            count--;
                            if (count > 0) {
                                cardsNoCopies.put(key, count);
                            } else {
                                cardsNoCopies.remove(key);
                            }
                            for (int j = 0; j < view.size(); j++) {
                                CardView cv = view.get(j);
                                if (cv.getId().equals(entry.getValue().getId())) {
                                    if (count > 0) {
                                        // replace by another card with the same name+setCode
                                        String key1 = cv.getName()+cv.getExpansionSetCode()+cv.getCardNumber();
                                        for (CardView cardView : cards.values()) {
                                            String key2 = cardView.getName()+cardView.getExpansionSetCode()+cardView.getCardNumber();
                                            if ((key1).equals(key2)) {
                                                view.set(j, cardView);
                                                break;
                                            }
                                        }
                                    } else {
                                        view.remove(j);
                                    }
                                    break;
                                }
                            }
                        }
                    } else {
                        for (CardView cv : view) {
                            if (cv.getId().equals(entry.getKey())) {
                                view.remove(cv);
                                break;
                            }
                        }
                    }
                }
            }

            if (updateCountsCallback != null) {
                updateCountsCallback.update(cards.size(), creatureCount, landCount, sorceryCount, instantCount, enchantmentCount);
            }
        }

        sort(this.sortSetting.getSortIndex(), this.sortSetting.isAscending());
        drawCards(sortSetting);
    }

    @Override
    public void refresh() {
        fireTableDataChanged();
    }

    public void clearCards() {
        view.clear();
        cards.clear();
    }

    @Override
    public int getRowCount() {
        return view.size();
    }

    @Override
    public int getColumnCount() {
        return column.length;
    }

    @Override
    public String getColumnName(int n) {
        return column[n];
    }

    @Override
    public Object getValueAt(int row, int column) {
        return getColumn(view.get(row), column);
    }

    private Object getColumn(Object obj, int column) {
        CardView c = (CardView) obj;
        switch (column) {
        case 0:
            if (displayNoCopies) {
                String key = c.getName() + c.getExpansionSetCode() + c.getCardNumber();
                Integer count = cardsNoCopies.get(key);
                return count != null ? count : "";
            }
            return "";
        case 1:
            return c.getName();
        case 2:
            String manaCost = "";
            for (String m : c.getManaCost()) {
                manaCost += m;
            }
            String castingCost = UI.getDisplayManaCost(manaCost);
            castingCost = ManaSymbols.replaceSymbolsWithHTML(castingCost, ManaSymbols.Type.PAY);            
            return "<html>" + castingCost + "</html>";
        case 3:
            return CardHelper.getColor(c);
        case 4:
            return CardHelper.getType(c);
        case 5:
            return CardHelper.isCreature(c) ? c.getPower() + "/"
                    + c.getToughness() : "-";
        case 6:
            return c.getRarity().toString();
        case 7:
            return c.getExpansionSetCode();
        default:
            return "error";
        }
    }

    private void addCard(CardView card, BigCard bigCard, UUID gameId) {
        if (cardDimension == null) {
            cardDimension = new Dimension(Config.dimensions.frameWidth,
                    Config.dimensions.frameHeight);
        }
        cards.put(card.getId(), card);

        if (displayNoCopies) {
            String key = card.getName()+card.getExpansionSetCode()+card.getCardNumber();
            Integer count = 1;
            if (cardsNoCopies.containsKey(key)) {
                count = cardsNoCopies.get(key) + 1;
            } else {
                view.add(card);
            }
            cardsNoCopies.put(key, count);
        } else {
               view.add(card);
        }
    }

    @Override
    public void drawCards(SortSetting sortSetting) {
        fireTableDataChanged();
    }

    public void removeCard(UUID cardId) {
        cards.remove(cardId);
        for (CardView cv : view) {
            if (cv.getId().equals(cardId)) {
                view.remove(cv);
                break;
            }
        }
    }

    @Override
    public void addCardEventListener(Listener<Event> listener) {
        cardEventSource.addListener(listener);
    }

    @Override
    public void clearCardEventListeners() {
        cardEventSource.clearListeners();
    }

    public void setNumber(int index, int number) {
        CardView card = view.get(index);
        cardEventSource.setNumber(card, "set-number", number);
    }

    public void doubleClick(int index) {
        CardView card = view.get(index);
        cardEventSource.doubleClick(card, "double-click");
    }

    public void altDoubleClick(int index) {
        CardView card = view.get(index);
        cardEventSource.altDoubleClick(card, "alt-double-click");
    }

    public void removeFromMainEvent(int index) {
        cardEventSource.removeFromMainEvent("remove-main");
    }

    public void removeFromSideEvent(int index) {
        cardEventSource.removeFromSideboardEvent("remove-sideboard");
    }

    public void addListeners(final JTable table) {
        // updates card detail, listens to any key strokes

        table.addKeyListener(new KeyListener() {
            @Override
            public void keyPressed(KeyEvent ev) {
            }

            @Override
            public void keyTyped(KeyEvent ev) {
            }

            @Override
            public void keyReleased(KeyEvent ev) {
                int row = table.getSelectedRow();
                if (row != -1) {
                    showImage(row);
                }
            }
        });

        // updates card detail, listens to any mouse clicks
        table.addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                int row = table.getSelectedRow();
                if (row != -1) {
                    showImage(row);
                }
            }
        });

        // sorts
        MouseListener mouse = new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                TableColumnModel columnModel = table.getColumnModel();
                int viewColumn = columnModel.getColumnIndexAtX(e.getX());
                int column = table.convertColumnIndexToModel(viewColumn);

                if (column != -1) {
                    // sort ascending
                    boolean asc = true;
                    if (recentSortedColumn == column) {
                        asc = !recentAscending;
                    }
                    sortSetting.setSortIndex(column);
                    sortSetting.setAscending(asc);
                    sort(column, asc);
                    fireTableDataChanged();
                }
            }
        };
        table.getTableHeader().addMouseListener(mouse);
    }

    private void showImage(int row) {
        CardView card = view.get(row);
        if (!card.getId().equals(bigCard.getCardId())) {
            if (!MageFrame.isLite()) {
                Image image = Plugins.getInstance().getOriginalImage(card);
                if (image != null && image instanceof BufferedImage) {
                    // XXX: scaled to fit width
                    image = ImageHelper.getResizedImage((BufferedImage) image, bigCard.getWidth());
                    bigCard.setCard(card.getId(), EnlargeMode.NORMAL, image, new ArrayList<String>());
                } else {
                    drawCardText(card);
                }
            } else {
                drawCardText(card);
            }
        }
    }

    private void drawCardText(CardView card) {
        JXPanel panel = GuiDisplayUtil.getDescription(card, bigCard.getWidth(), bigCard.getHeight());
        panel.setVisible(true);
        bigCard.hideTextComponent();
        bigCard.addJXPanel(card.getId(), panel);
    }

    public List<CardView> getCardsView() {
        return view;
    }

    public boolean sort(int column, boolean ascending) {
        // used by addCard() to resort the cards
        recentSortedColumn = column;
        recentAscending = ascending;

        MageCardComparator sorter = new MageCardComparator(column, ascending);
        Collections.sort(view, sorter);

        fireTableDataChanged();

        return true;
    }

    public int getRecentSortedColumn() {
        return recentSortedColumn;
    }

    public boolean isRecentAscending() {
        return recentAscending;
    }

    public void setDisplayNoCopies(boolean value) {
        this.displayNoCopies = value;
    }

    public void setUpdateCountsCallback(UpdateCountsCallback callback) {
        this.updateCountsCallback = callback;
    }

    public void setNumberEditable(boolean numberEditable) {
        this.numberEditable = numberEditable;
    }

    @Override
    public int cardsSize() {
        return cards.size();
    }

    @Override
    public boolean isCellEditable(int row, int col) {
        if (numberEditable && col == 0) {
            return true;
        }
        return super.isCellEditable(row, col); //To change body of generated methods, choose Tools | Templates.
    }
    
    
}
