/**
 *  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.server.model;

import java.util.Random;
import java.util.logging.Logger;

import net.sf.freecol.common.model.Building;
import net.sf.freecol.common.model.BuildingType;
import net.sf.freecol.common.model.Colony;
import net.sf.freecol.common.model.Game;
import net.sf.freecol.common.model.ModelMessage;
import net.sf.freecol.common.model.StringTemplate;
import net.sf.freecol.common.model.Unit;
import net.sf.freecol.common.model.UnitType;
import net.sf.freecol.server.control.ChangeSet;
import net.sf.freecol.server.control.ChangeSet.See;


/**
 * The server version of a building.
 */
public class ServerBuilding extends Building implements ServerModelObject {

    private static final Logger logger = Logger.getLogger(ServerBuilding.class.getName());


    /**
     * Trivial constructor required for all ServerModelObjects.
     */
    public ServerBuilding(Game game, String id) {
        super(game, id);
    }

    /**
     * Creates a new ServerBuilding.
     *
     * @param game The <code>Game</code> this object belongs to.
     * @param colony The <code>Colony</code> in which this building is located.
     * @param type The <code>BuildingType</code> of building.
     */
    public ServerBuilding(Game game, Colony colony, BuildingType type) {
        super(game, colony, type);
    }


    /**
     * New turn for this building.
     *
     * @param random A <code>Random</code> number source.
     * @param cs A <code>ChangeSet</code> to update.
     */
    public void csNewTurn(Random random, ChangeSet cs) {
        BuildingType type = getType();
        ServerPlayer owner = (ServerPlayer) getColony().getOwner();

        if (canTeach()) {
            teach(cs, owner);
        }

        if (type.hasAbility("model.ability.repairUnits")) {
            repairUnits(cs);
        }

    }

    private void teach(ChangeSet cs, ServerPlayer owner) {
        for (Unit teacher : getUnitList()) {
            boolean teacherDirty = false;
            boolean studentDirty = false;
            Unit student = teacher.getStudent();

            if (student != null && student.getTeacher() != teacher) {
                // Sanitation, make sure we have the proper
                // teacher/student relation.
                logger.warning("Bogus teacher/student assignment.");
                teacher.setStudent(null);
                student = null;
                teacherDirty = true;
            }

            // Student may have changed
            if (student == null && csAssignStudent(teacher, cs)) {
                teacherDirty = true;
                studentDirty = true;
                student = teacher.getStudent();
            }

            // Ready to train?
            if (student != null) {
                final int training = teacher.getTurnsOfTraining() + 1;
                if (training < teacher.getNeededTurnsOfTraining()) {
                    teacher.setTurnsOfTraining(training);
                    if (!teacherDirty) {
                        cs.addPartial(See.only(owner), teacher,
                                      "turnsOfTraining");
                    }
                } else {
                    StringTemplate oldName = student.getLabel();
                    UnitType teach = teacher.getType().getSkillTaught();
                    UnitType skill = Unit
                        .getUnitTypeTeaching(teach, student.getType());
                    if (skill == null) {
                        logger.warning("Student " + student.getId()
                                       + " can not learn from "
                                       + teacher.getId());
                    } else {
                        student.setType(skill);
                        StringTemplate newName = student.getLabel();
                        cs.addMessage(See.only(owner),
                                      new ModelMessage(ModelMessage.MessageType.UNIT_IMPROVED,
                                                       "model.unit.unitEducated",
                                                       getColony(), this)
                                      .addStringTemplate("%oldName%", oldName)
                                      .addStringTemplate("%unit%", newName)
                                      .addName("%colony%", getColony().getName()));
                    }
                    student.setTurnsOfTraining(0);
                    student.setMovesLeft(0);
                    cs.add(See.only(owner), student);
                    studentDirty = false;

                    teacher.setTurnsOfTraining(0);
                    if (student.canBeStudent(teacher)) { // Keep teaching
                        cs.addPartial(See.only(owner), teacher,
                                      "turnsOfTraining");
                    } else {
                        student.setTeacher(null);
                        teacher.setStudent(null);
                        teacherDirty = true;
                        if (csAssignStudent(teacher, cs)) {
                            student = teacher.getStudent();
                            studentDirty = true;
                        }
                    }
                }
            }

            if (teacherDirty) cs.add(See.only(owner), teacher);
            if (studentDirty) cs.add(See.only(owner), student);
        }
    }


    /**
     * Assigns a student to a teacher within a building.
     *
     * @param teacher The <code>Unit</code> that is teaching.
     * @param cs A <code>ChangeSet</code> to update.
     */
    private boolean csAssignStudent(Unit teacher, ChangeSet cs) {
        Colony colony = getColony();
        final Unit student = colony.findStudent(teacher);
        if (student == null) {
            cs.addMessage(See.only((ServerPlayer) colony.getOwner()),
                new ModelMessage(ModelMessage.MessageType.WARNING,
                                 "model.building.noStudent",
                                 colony, teacher)
                          .addStringTemplate("%teacher%", teacher.getLabel())
                          .addName("%colony%", colony.getName()));
            return false;
        }
        teacher.setStudent(student);
        teacher.setWorkType(null);
        student.setTeacher(teacher);
        return true;
    }

    private void repairUnits(ChangeSet cs) {
        for (Unit unit : getTile().getUnitList()) {
            if (unit.isUnderRepair()
                && getType().hasAbility("model.ability.repairUnits",
                                        unit.getType())) {
                ((ServerUnit) unit).csRepairUnit(cs);
            }
        }
    }

    /**
     * Returns the tag name of the root element representing this object.
     *
     * @return "serverBuilding"
     */
    public String getServerXMLElementTagName() {
        return "serverBuilding";
    }
}
