/*
 * MegaMek - Copyright (C) 2000,2001,2002,2003,2004 Ben Mazur (bmazur@sev.org)
 *
 *  This program is free software; you can redistribute it and/or modify it
 *  under the terms of the GNU General Public License as published by the Free
 *  Software Foundation; either version 2 of the License, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 *  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 *  for more details.
 */

package megamek.client.ui.swing;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;
import java.util.ArrayList;
import java.util.Vector;

import megamek.client.event.BoardViewEvent;
import megamek.client.ui.IDisplayable;
import megamek.client.ui.Messages;
import megamek.client.ui.swing.widget.PMUtil;
import megamek.common.Aero;
import megamek.common.BattleArmor;
import megamek.common.Entity;
import megamek.common.GunEmplacement;
import megamek.common.Infantry;
import megamek.common.Mech;
import megamek.common.Protomech;
import megamek.common.Tank;
import megamek.common.util.StringUtil;

public class UnitOverview implements IDisplayable {

    private static final String IMAGE_DIR = "data/images/widgets";

    private static final int UNKNOWN_UNITS_PER_PAGE = -1;

    /**
     * The maximum length of the icon name.
     */
    public static final int ICON_NAME_MAX_LENGTH = 52;

    private static final Font FONT = new Font("SansSerif", Font.PLAIN, 10); //$NON-NLS-1$
    private static final int DIST_TOP = 5;
    private static final int DIST_SIDE = 5;
    private static final int ICON_WIDTH = 56;
    private static final int ICON_HEIGHT = 48;
    private static final int BUTTON_HEIGHT = 11;
    private static final int BUTTON_PADDING = 2;
    private static final int PADDING = 5;

    private int[] unitIds;
    private boolean isHit = false;
    private boolean visible = true;
    private boolean scroll = false;
    private int unitsPerPage = UNKNOWN_UNITS_PER_PAGE;
    private int actUnitsPerPage = 0;
    private int scrollOffset = 0;

    private ClientGUI clientgui;

    private FontMetrics fm;

    private Image scrollUp;
    private Image scrollDown;
    private Image pageUp;
    private Image pageDown;

    public UnitOverview(ClientGUI clientgui) {
        this.clientgui = clientgui;
        fm = clientgui.getFontMetrics(FONT);

        Toolkit toolkit = clientgui.getToolkit();
        scrollUp = toolkit.getImage(IMAGE_DIR + "/scrollUp.gif"); //$NON-NLS-1$
        PMUtil.setImage(scrollUp, clientgui);
        scrollDown = toolkit.getImage(IMAGE_DIR + "/scrollDown.gif"); //$NON-NLS-1$
        PMUtil.setImage(scrollDown, clientgui);
        pageUp = toolkit.getImage(IMAGE_DIR + "/pageUp.gif"); //$NON-NLS-1$
        PMUtil.setImage(pageUp, clientgui);
        pageDown = toolkit.getImage(IMAGE_DIR + "/pageDown.gif"); //$NON-NLS-1$
        PMUtil.setImage(pageDown, clientgui);
    }

    public void draw(Graphics graph, Point drawRelativeTo, Dimension size) {
        if (!visible) {
            return;
        }

        computeUnitsPerPage(size);

        graph.setFont(FONT);
        ArrayList<Entity> v = clientgui.getClient().game
                .getPlayerEntities(clientgui.getClient().getLocalPlayer(), true);
        unitIds = new int[v.size()];

        scroll = v.size() > unitsPerPage;

        actUnitsPerPage = scroll ? unitsPerPage - 1 : unitsPerPage;

        if (scrollOffset + actUnitsPerPage > unitIds.length) {
            scrollOffset = unitIds.length - actUnitsPerPage;
            if (scrollOffset < 0) {
                scrollOffset = 0;
            }
        }

        int x = drawRelativeTo.x + size.width - DIST_SIDE - ICON_WIDTH;
        int y = drawRelativeTo.y + DIST_TOP;

        if (scroll) {
            graph.drawImage(pageUp, x, y, null);
            graph.drawImage(scrollUp, x, y + BUTTON_HEIGHT + BUTTON_PADDING,
                    null);
            y += BUTTON_HEIGHT + BUTTON_HEIGHT + BUTTON_PADDING
                    + BUTTON_PADDING;
        }

        for (int i = scrollOffset; (i < v.size())
                && (i < actUnitsPerPage + scrollOffset); i++) {
            Entity e = v.get(i);
            unitIds[i] = e.getId();
            String name = getIconName(e, fm);
            Image i1 = clientgui.bv.getTilesetManager().iconFor(e);

            graph.drawImage(i1, x, y, null);
            printLine(graph, x + 3, y + 46, name);
            drawBars(graph, e, x, y);
            drawHeat(graph, e, x, y);
            drawConditionStrings(graph, e, x, y);
            graph.setColor(getFrameColor(e));
            graph.drawRect(x, y, ICON_WIDTH, ICON_HEIGHT);

            Entity se = clientgui == null ? null : clientgui.getClient()
                    .getEntity(clientgui.getSelectedEntityNum());
            if (e == se) {
                graph.drawRect(x - 1, y - 1, ICON_WIDTH + 2, ICON_HEIGHT + 2);
            }

            y += ICON_HEIGHT + PADDING;
        }

        if (scroll) {
            y -= PADDING;
            y += BUTTON_PADDING;
            graph.drawImage(scrollDown, x, y, null);
            graph.drawImage(pageDown, x, y + BUTTON_HEIGHT + BUTTON_PADDING,
                    null);
        }

    }

    public void setIdleTime(long timeIdle, boolean add) {
    }

    public boolean isHit(Point p, Dimension size) {
        if (!visible) {
            return false;
        }

        int actUnits = scroll ? unitsPerPage - 1 : unitsPerPage;

        int x = p.x;
        int y = p.y;
        int xOffset = size.width - DIST_SIDE - ICON_WIDTH;
        int yOffset = DIST_TOP;

        if ((x < xOffset) || (x > xOffset + ICON_WIDTH) || (y < yOffset)
                || (y > yOffset + (unitsPerPage * (ICON_HEIGHT + PADDING)))) {
            return false;
        }

        if (scroll) {
            if ((y > yOffset) && (y < yOffset + BUTTON_HEIGHT)) {
                pageUp();
                return true;
            }
            yOffset += BUTTON_HEIGHT + BUTTON_PADDING;
            if ((y > yOffset) && (y < yOffset + BUTTON_HEIGHT)) {
                scrollUp();
                return true;
            }
            yOffset += BUTTON_HEIGHT + BUTTON_PADDING;
        }

        for (int i = scrollOffset; (i < unitIds.length)
                && (i < actUnits + scrollOffset); i++) {
            if ((y > yOffset) && (y < yOffset + ICON_HEIGHT)) {
                clientgui.bv.processBoardViewEvent(new BoardViewEvent(
                        clientgui.bv, BoardViewEvent.SELECT_UNIT, unitIds[i]));
                isHit = true;
                return true;
            }
            yOffset += ICON_HEIGHT + PADDING;
        }

        if (scroll) {
            yOffset -= PADDING;
            yOffset += BUTTON_PADDING;
            if ((y > yOffset) && (y < yOffset + BUTTON_HEIGHT)) {
                scrollDown();
                return true;
            }
            yOffset += BUTTON_HEIGHT + BUTTON_PADDING;
            if ((y > yOffset) && (y < yOffset + BUTTON_HEIGHT)) {
                pageDown();
                return true;
            }
        }

        return false;
    }

    public boolean isMouseOver(Point p, Dimension size) {
        return false;
    }

    public boolean isSliding() {
        return false;
    }

    public boolean slide() {
        return false;
    }

    public boolean isDragged(Point p, Dimension size) {
        return false;
    }

    public boolean isBeingDragged() {
        return false;
    }

    public boolean isReleased() {
        if (!visible) {
            return false;
        }

        if (isHit) {
            isHit = false;
            return true;
        }
        return false;
    }

    public void setVisible(boolean visible) {
        this.visible = visible;
    }

    public boolean isVisible() {
        return visible;
    }

    private void drawHeat(Graphics graph, Entity entity, int x, int y) {
        if (!((entity instanceof Mech) || (entity instanceof Aero))) {
            return;
        }
        boolean mtHeat = false;
        int mHeat = 30;
        if ((entity.getGame() != null)
                && entity.getGame().getOptions().booleanOption("tacops_heat")) {
            mHeat = 50;
            mtHeat = true;
        }
        int heat = Math.min(mHeat, entity.heat);

        graph.setColor(Color.darkGray);
        graph.fillRect(x + 52, y + 4, 2, 30);
        graph.setColor(Color.lightGray);
        graph.fillRect(x + 51, y + 3, 2, 30);
        graph.setColor(Color.red);
        if (mtHeat) {
            graph.fillRect(x + 51, y + 3 + (30 - (int) (heat * 0.6)), 2,
                    (int) (heat * 0.6));
        } else {
            graph.fillRect(x + 51, y + 3 + (30 - heat), 2, heat);
        }
    }

    private void drawBars(Graphics graph, Entity entity, int x, int y) {
        // Lets draw our armor and internal status bars
        int baseBarLength = 23;
        int barLength = 0;
        double percentRemaining = 0.00;

        percentRemaining = entity.getArmorRemainingPercent();
        barLength = (int) (baseBarLength * percentRemaining);

        graph.setColor(Color.darkGray);
        graph.fillRect(x + 4, y + 4, 23, 2);
        graph.setColor(Color.lightGray);
        graph.fillRect(x + 3, y + 3, 23, 2);
        graph.setColor(getStatusBarColor(percentRemaining));
        graph.fillRect(x + 3, y + 3, barLength, 2);

        percentRemaining = entity.getInternalRemainingPercent();
        barLength = (int) (baseBarLength * percentRemaining);

        graph.setColor(Color.darkGray);
        graph.fillRect(x + 4, y + 7, 23, 2);
        graph.setColor(Color.lightGray);
        graph.fillRect(x + 3, y + 6, 23, 2);
        graph.setColor(getStatusBarColor(percentRemaining));
        graph.fillRect(x + 3, y + 6, barLength, 2);

    }

    private Color getStatusBarColor(double percentRemaining) {
        if (percentRemaining <= .25) {
            return Color.red;
        } else if (percentRemaining <= .75) {
            return Color.yellow;
        } else {
            return new Color(16, 196, 16);
        }
    }

    private Color getFrameColor(Entity entity) {
        if (!clientgui.getClient().isMyTurn() || !entity.isSelectableThisTurn()) {
            return Color.gray;
        }
        return Color.black;
    }

    private void printLine(Graphics g, int x, int y, String s) {
        g.setColor(Color.black);
        g.drawString(s, x + 1, y);
        g.drawString(s, x - 1, y);
        g.drawString(s, x, y + 1);
        g.drawString(s, x, y - 1);
        g.setColor(Color.white);
        g.drawString(s, x, y);
    }

    private void drawConditionStrings(Graphics graph, Entity entity, int x,
            int y) {

//      out of control conditions for ASF
        if(entity instanceof Aero) {
            Aero a = (Aero)entity;


            //draw altitude if Aero in atmosphere
            if(clientgui.getClient().game.getBoard().inAtmosphere()) {
                graph.setColor(Color.darkGray);
                graph.drawString(Integer.toString(a.getElevation()), x + 36, y + 15);
                graph.setColor(Color.PINK);
                graph.drawString(Integer.toString(a.getElevation()), x + 35, y + 14);
            }

            if(a.isRolled()) {
                // draw "rolled"
                graph.setColor(Color.darkGray);
                graph.drawString(Messages.getString("BoardView1.ROLLED"), x + 11, y+29); //$NON-NLS-1$
                graph.setColor(Color.red);
                graph.drawString(Messages.getString("BoardView1.ROLLED"), x + 10, y+28); //$NON-NLS-1$
            }

            if(a.isOutControlTotal() && a.isRandomMove()) {
                graph.setColor(Color.darkGray);
                graph.drawString(Messages.getString("UnitOverview.RANDOM"), x + 11, y + 24); //$NON-NLS-1$
                graph.setColor(Color.red);
                graph.drawString(Messages.getString("UnitOverview.RANDOM"), x + 10, y + 23); //$NON-NLS-1$
            } else if(a.isOutControlTotal()) {
                // draw "CONTROL"
                graph.setColor(Color.darkGray);
                graph.drawString(Messages.getString("UnitOverview.CONTROL"), x + 11, y + 24); //$NON-NLS-1$
                graph.setColor(Color.red);
                graph.drawString(Messages.getString("UnitOverview.CONTROL"), x + 10, y + 23); //$NON-NLS-1$
            }

            //is the unit evading? - can't evade and be out of control so just draw on top
            if(a.isEvading()) {
                //draw evasion
                graph.setColor(Color.darkGray);
                graph.drawString(Messages.getString("UnitOverview.EVADE"), x +11, y + 24); //$NON-NLS-1$
                graph.setColor(Color.red);
                graph.drawString(Messages.getString("UnitOverview.EVADE"), x + 10, y + 23); //$NON-NLS-1$
            }

        }

        // draw condition strings
        if (entity.isImmobile() && !entity.isProne()) {
            // draw "IMMOB"
            graph.setColor(Color.darkGray);
            graph.drawString(
                    Messages.getString("UnitOverview.IMMOB"), x + 11, y + 29); //$NON-NLS-1$
            graph.setColor(Color.red);
            graph.drawString(
                    Messages.getString("UnitOverview.IMMOB"), x + 10, y + 28); //$NON-NLS-1$
        } else if (!entity.isImmobile() && entity.isProne()) {
            // draw "PRONE"
            graph.setColor(Color.darkGray);
            graph.drawString(
                    Messages.getString("UnitOverview.PRONE"), x + 11, y + 29); //$NON-NLS-1$
            graph.setColor(Color.yellow);
            graph.drawString(
                    Messages.getString("UnitOverview.PRONE"), x + 10, y + 28); //$NON-NLS-1$
        } else if (entity.isImmobile() && entity.isProne()) {
            // draw "IMMOB" and "PRONE"
            graph.setColor(Color.darkGray);
            graph.drawString(
                    Messages.getString("UnitOverview.IMMOB"), x + 11, y + 24); //$NON-NLS-1$
            graph.drawString(
                    Messages.getString("UnitOverview.PRONE"), x + 11, y + 34); //$NON-NLS-1$
            graph.setColor(Color.red);
            graph.drawString(
                    Messages.getString("UnitOverview.IMMOB"), x + 10, y + 23); //$NON-NLS-1$
            graph.setColor(Color.yellow);
            graph.drawString(
                    Messages.getString("UnitOverview.PRONE"), x + 10, y + 33); //$NON-NLS-1$
        } else if (!entity.isImmobile() && entity.isHullDown()) {
            // draw "PRONE"
            graph.setColor(Color.darkGray);
            graph.drawString(
                    Messages.getString("UnitOverview.HULLDOWN"), x - 1, y + 29); //$NON-NLS-1$
            graph.setColor(Color.yellow);
            graph.drawString(
                    Messages.getString("UnitOverview.HULLDOWN"), x - 2, y + 28); //$NON-NLS-1$
        } else if (entity.isImmobile() && entity.isHullDown()) {
            // draw "IMMOB" and "PRONE"
            graph.setColor(Color.darkGray);
            graph.drawString(
                    Messages.getString("UnitOverview.IMMOB"), x + 11, y + 24); //$NON-NLS-1$
            graph.drawString(
                    Messages.getString("UnitOverview.HULLDOWN"), x - 1, y + 34); //$NON-NLS-1$
            graph.setColor(Color.red);
            graph.drawString(
                    Messages.getString("UnitOverview.IMMOB"), x + 10, y + 23); //$NON-NLS-1$
            graph.setColor(Color.yellow);
            graph.drawString(
                    Messages.getString("UnitOverview.HULLDOWN"), x - 2, y + 33); //$NON-NLS-1$
        } else if (!entity.isDeployed()) {
            int roundsLeft = entity.getDeployRound()
                    - clientgui.getClient().game.getRoundCount();
            if (roundsLeft > 0) {
                printLine(graph, x + 25, y + 28, Integer.toString(roundsLeft));
            }
        }
    }

    private void computeUnitsPerPage(Dimension size) {
        unitsPerPage = (size.height - DIST_TOP) / (ICON_HEIGHT + PADDING);
    }

    private void pageUp() {
        if (scrollOffset > 0) {
            scrollOffset -= actUnitsPerPage;
            if (scrollOffset < 0) {
                scrollOffset = 0;
            }
            clientgui.bv.refreshDisplayables();
        }
    }

    private void pageDown() {
        if (scrollOffset < unitIds.length - actUnitsPerPage) {
            scrollOffset += actUnitsPerPage;
            if (scrollOffset > unitIds.length - actUnitsPerPage) {
                scrollOffset = unitIds.length - actUnitsPerPage;
            }
            clientgui.bv.refreshDisplayables();
        }
    }

    private void scrollUp() {
        if (scrollOffset > 0) {
            scrollOffset--;
            clientgui.bv.refreshDisplayables();
        }
    }

    private void scrollDown() {
        if (scrollOffset < unitIds.length - actUnitsPerPage) {
            scrollOffset++;
            clientgui.bv.refreshDisplayables();
        }
    }

    protected String getIconName(Entity e, FontMetrics metrics) {

        if (e instanceof BattleArmor) {
            String iconName = e.getShortName();
            if (metrics.stringWidth(iconName) > ICON_NAME_MAX_LENGTH) {
                Vector<String> v = StringUtil.splitString(iconName, " "); //$NON-NLS-1$
                iconName = v.elementAt(0);
                if (iconName.equals("Clan")) {
                    iconName = v.elementAt(1);
                }
            }
            return adjustString(iconName, metrics);
        } else if (e instanceof Protomech) {
            String iconName = e.getChassis() + " " + e.getModel(); //$NON-NLS-1$
            return adjustString(iconName, metrics);
        } else if (e instanceof Tank) {
            String iconName = e.getShortName();

            if (metrics.stringWidth(iconName) > ICON_NAME_MAX_LENGTH) {
                Vector<String> v = StringUtil.splitString(iconName, " "); //$NON-NLS-1$
                iconName = v.elementAt(0);
            }
            return adjustString(iconName, metrics);
        } else if ((e instanceof Infantry) || (e instanceof Mech)
                || (e instanceof GunEmplacement) ||
                (e instanceof Aero)) {
            String iconName = e.getModel();
            return adjustString(iconName, metrics);
        }
        return "!!Unknown!!";
    }

    protected String adjustString(String s, FontMetrics metrics) {
        while (metrics.stringWidth(s) > ICON_NAME_MAX_LENGTH) {
            s = s.substring(0, s.length() - 1);
        }
        return s;
    }

}
