/**
 *  Copyright (C) 2002-2012   The FreeCol Team
 *
 *  This file is part of FreeCol.
 *
 *  FreeCol 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.
 *
 *  FreeCol 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.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with FreeCol.  If not, see <http://www.gnu.org/licenses/>.
 */

package net.sf.freecol.common.networking;

import net.sf.freecol.common.model.Colony;
import net.sf.freecol.common.model.DiplomaticTrade;
import net.sf.freecol.common.model.FreeColGameObject;
import net.sf.freecol.common.model.FreeColObject;
import net.sf.freecol.common.model.Game;
import net.sf.freecol.common.model.Player;
import net.sf.freecol.common.model.Settlement;
import net.sf.freecol.common.model.Unit;
import net.sf.freecol.server.FreeColServer;
import net.sf.freecol.server.model.ServerPlayer;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;


/**
 * The message sent when executing a diplomatic trade.
 */
public class DiplomacyMessage extends DOMMessage {

    /**
     * The unit doing the trading.  Can not use just an id as the unit
     * might be invisible to the settlement due to being aboard a carrier.
     */
    private Unit unit;

    /**
     * The settlement to negotiate with.
     */
    private Settlement settlement;

    /**
     * The trade to make.
     */
    private DiplomaticTrade agreement;


    /**
     * Create a new <code>DiplomacyMessage</code>.
     *
     * @param unit The <code>Unit</code> that is negotiating.
     * @param settlement The <code>Settlement</code> to negotiate with.
     * @param agreement The <code>DiplomaticTrade</code> to make.
     */
    public DiplomacyMessage(Unit unit, Settlement settlement,
                            DiplomaticTrade agreement) {
        this.unit = unit;
        this.settlement = settlement;
        this.agreement = agreement;
    }

    /**
     * Create a new <code>DiplomacyMessage</code> from a
     * supplied element.  The unit is supplied in case it was hidden in
     * some way, such as aboard a ship.
     *
     * @param game The <code>Game</code> this message belongs to.
     * @param element The <code>Element</code> to use to create the message.
     */
    public DiplomacyMessage(Game game, Element element) {
        FreeColGameObject fcgo = game.getFreeColGameObject(element.getAttribute("settlement"));
        this.settlement = (fcgo instanceof Settlement) ? (Settlement) fcgo
            : null;
        NodeList nodes = element.getChildNodes();
        this.agreement = (nodes.getLength() < 1) ? null
            : new DiplomaticTrade(game, (Element) nodes.item(0));
        if (nodes.getLength() < 2) {
            this.unit = null;
        } else {
            Element unitElement = (Element) nodes.item(1);
            String unitId = unitElement.getAttribute(FreeColObject.ID_ATTRIBUTE);
            fcgo = game.getFreeColGameObject(unitId);
            if (fcgo instanceof Unit) {
                this.unit = (Unit) fcgo;
            } else {
                this.unit = new Unit(game, unitElement);
            }
        }
    }

    /**
     * Get the <code>Unit</code> which began this diplomatic exchange.
     *
     * @return The unit, or null if none.
     */
    public Unit getUnit() {
        return unit;
    }

    /**
     * Get the <code>Settlement</code> at which a diplomatic exchange
     * happens.
     *
     * @return The settlement, or null if none.
     */
    public Settlement getSettlement() {
        return settlement;
    }

    /**
     * Get the agreement (a <code>DiplomaticTrade</code>) in this message.
     *
     * @return The agreement in this message.
     */
    public DiplomaticTrade getAgreement() {
        return agreement;
    }

    /**
     * Set the agreement (a <code>DiplomaticTrade</code>) in this message.
     *
     * @param agreement The <code>DiplomaticTrade</code> to set.
     */
    public void setAgreement(DiplomaticTrade agreement) {
        this.agreement = agreement;
    }

    /**
     * Handle a "diplomacy"-message.
     *
     * @param server The <code>FreeColServer</code> that handles the message.
     * @param connection The <code>Connection</code> the message is from.
     * @return An <code>Element</code> describing the trade with either
     *         "accept" or "reject" status, null on trade failure,
     *         or an error <code>Element</code> on outright error.
     */
    public Element handle(FreeColServer server, Connection connection) {
        ServerPlayer serverPlayer = server.getPlayer(connection);

        Unit unit = getUnit();
        if (unit == null) {
            return DOMMessage.clientError("Missing unit in diplomacy.");
        } else if (unit.getTile() == null) {
            return DOMMessage.clientError("Unit is not on the map: "
                + unit.getId());
        } else if (unit.getOwner() != serverPlayer) {
            return DOMMessage.clientError("Player does not own unit: "
                + unit.getId());
        }

        Settlement settlement = getSettlement();
        if (settlement == null) {
            return DOMMessage.clientError("Missing settlement in diplomacy.");
        } else if (!(settlement instanceof Colony)) {
            return DOMMessage.clientError("Settlement is not a colony: "
                + settlement.getId());
        } else if (!unit.getTile().isAdjacent(settlement.getTile())) {
            return DOMMessage.clientError("Unit " + unit.getId()
                + " is not adjacent to settlement " + settlement.getId());
        }
        Player otherPlayer = settlement.getOwner();

        if (agreement == null) {
            return DOMMessage.clientError("Null diplomatic agreement.");
        }
        Player senderPlayer = agreement.getSender();
        Player recipientPlayer = agreement.getRecipient();
        Player refPlayer = serverPlayer.getREFPlayer();
        if (senderPlayer == null) {
            return DOMMessage.clientError("Null sender in agreement.");
        } else if (recipientPlayer == null) {
            return DOMMessage.clientError("Null recipient in agreement.");
        } else if (senderPlayer != (Player) serverPlayer) {
            return DOMMessage.clientError("Sender is not the unit owner: "
                + senderPlayer.getId());
        } else if (recipientPlayer != otherPlayer) {
            return DOMMessage.clientError("Recipient is not the settlement owner: "
                + recipientPlayer.getId());
        } else if (senderPlayer == refPlayer || recipientPlayer == refPlayer) {
            return DOMMessage.clientError("The REF does not negotiate: "
                + refPlayer.getId());
        }

        // Valid, try to trade.
        return server.getInGameController()
            .diplomaticTrade(serverPlayer, unit, settlement, agreement);
    }

    /**
     * Convert this DiplomacyMessage to XML.
     *
     * @return The XML representation of this message.
     */
    public Element toXMLElement() {
        Element result = createNewRootElement(getXMLElementTagName());
        Document doc = result.getOwnerDocument();
        result.setAttribute("settlement", settlement.getId());
        result.appendChild(agreement.toXMLElement(null, doc));
        result.appendChild(unit.toXMLElement(null, doc));
        return result;
    }

    /**
     * The tag name of the root element representing this object.
     *
     * @return "diplomacy".
     */
    public static String getXMLElementTagName() {
        return "diplomacy";
    }
}
