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

import java.util.List;

import net.sf.freecol.server.model.ServerBuilding;
import net.sf.freecol.server.model.ServerColony;
import net.sf.freecol.server.model.ServerUnit;
import net.sf.freecol.util.test.FreeColTestCase;


public class ColonyProductionTest extends FreeColTestCase {

    private static final BuildingType countryType
        = spec().getBuildingType("model.building.country");
    private static final BuildingType depotType
        = spec().getBuildingType("model.building.depot");
    private static final BuildingType townHallType
        = spec().getBuildingType("model.building.townHall");

    private static final GoodsType bellsType
        = spec().getGoodsType("model.goods.bells");
    private static final GoodsType clothType
        = spec().getGoodsType("model.goods.cloth");
    private static final GoodsType cottonType
        = spec().getGoodsType("model.goods.cotton");
    private static final GoodsType crossesType
        = spec().getGoodsType("model.goods.crosses");
    private static final GoodsType foodType
        = spec().getGoodsType("model.goods.food");
    private static final GoodsType grainType
        = spec().getGoodsType("model.goods.grain");
    private static final GoodsType horsesType
        = spec().getGoodsType("model.goods.horses");

    private static final TileType plainsType
        = spec().getTileType("model.tile.plains");


    public void testProductionSoldier() {

        Game game = getStandardGame();
        Map map = getTestMap();
        game.setMap(map);
        Player dutch = game.getPlayer("model.nation.dutch");

        Tile tile = map.getTile(5, 8);
        Resource grain = new Resource(game, tile, spec().getResourceType("model.resource.grain"));
        tile.addResource(grain);

        UnitType veteran = spec().getUnitType("model.unit.veteranSoldier");
        Unit soldier = new ServerUnit(game, map.getTile(6, 8), dutch, veteran);

        Colony colony = new ServerColony(game, dutch, "New Amsterdam", soldier.getTile());
        dutch.addSettlement(colony);
        GoodsType foodType = grainType;
        soldier.setWorkType(foodType);
        nonServerBuildColony(soldier, colony);

        // Test the colony
        assertEquals(map.getTile(6, 8), colony.getTile());

        assertEquals("New Amsterdam", colony.getName());

        assertEquals(colony, colony.getTile().getSettlement());

        assertEquals(dutch, colony.getTile().getOwner());

        // Disabled.  Removal of equipment has moved to the server, so
        // nonServerBuildColony is not going to work.
        //// Should have 50 Muskets and nothing else
        //GoodsType muskets = spec().getGoodsType("model.goods.muskets");
        //assertNotNull(muskets);
        //
        //for (GoodsType type : spec().getGoodsTypeList()){
        //    if (type == muskets)
        //        assertEquals(50, colony.getGoodsCount(type));
        //    else
        //        assertEquals(type.toString(), 0, colony.getGoodsCount(type));
        //}

        // Test the state of the soldier
        // Soldier should be working on the field with the bonus

        assertEquals(foodType, soldier.getWorkType());

        assertEquals(colony.getColonyTile(tile).getTile(), soldier.getLocation().getTile());

        assertEquals(0, soldier.getMovesLeft());

        //assertEquals(false, soldier.isArmed());
    }

    public void testProductionPioneer() {

        Game game = getStandardGame();
        Map map = getTestMap();
        game.setMap(map);
        Player dutch = game.getPlayer("model.nation.dutch");

        Tile tile = map.getTile(5, 8);
        Resource grain = new Resource(game, tile, spec().getResourceType("model.resource.grain"));
        tile.addResource(grain);

        UnitType pioneerType = spec().getUnitType("model.unit.hardyPioneer");
        GoodsType foodType = grainType;
        Unit pioneer = new ServerUnit(game, map.getTile(6, 8), dutch, pioneerType);

        Colony colony = new ServerColony(game, dutch, "New Amsterdam", pioneer.getTile());
        dutch.addSettlement(colony);
        pioneer.setWorkType(foodType);
        nonServerBuildColony(pioneer, colony);

        // Test the colony
        assertEquals(map.getTile(6, 8), colony.getTile());

        assertEquals("New Amsterdam", colony.getName());

        assertEquals(colony, colony.getTile().getSettlement());

        assertEquals(dutch, colony.getTile().getOwner());

        // Disabled.  Removal of equipment has moved to the server, so
        // nonServerBuildColony is not going to work.
        //// Should have 100 tools and nothing else
        //GoodsType tools = spec().getGoodsType("model.goods.tools");
        //assertNotNull(tools);
        //
        //for (GoodsType type : spec().getGoodsTypeList()){
        //    if (type == tools)
        //        assertEquals(100, colony.getGoodsCount(type));
        //    else
        //        assertEquals(type.toString(), 0, colony.getGoodsCount(type));
        //}

        // Test the state of the pioneer
        // Pioneer should be working on the field with the bonus
        assertEquals(foodType, pioneer.getWorkType());

        assertEquals(colony.getColonyTile(tile).getTile(), pioneer.getLocation().getTile());

        assertEquals(0, pioneer.getMovesLeft());

        //assertEquals(false, pioneer.isArmed());
    }

    public void testBellNetProduction(){
    	Game game = getStandardGame();

    	game.setMap(getTestMap());

    	Colony colony = getStandardColony(7);

    	int initialBellCount = colony.getGoodsCount(bellsType);
    	int expectedBellCount = 0;
    	int bellsProdPerTurn = colony.getProductionOf(bellsType);
    	int expectedBellProd = 1;
    	int bellsUpkeep = colony.getConsumptionOf(bellsType);
    	int expectedBellUpkeep =  colony.getUnitCount() - 2;
    	int bellsNetProdPerTurn = colony.getNetProductionOf(bellsType);
    	int expectedBellNetProd = expectedBellProd - expectedBellUpkeep;

    	assertEquals("Wrong bell count", expectedBellCount, initialBellCount);
    	assertEquals("Wrong bell production",expectedBellProd,bellsProdPerTurn);
    	assertEquals("Wrong bell upkeep",expectedBellUpkeep,bellsUpkeep);
    	assertEquals("Wrong bell net production",expectedBellNetProd,bellsNetProdPerTurn);
    }
    /**
     * Tests that there is no over production of horses, to avoid being thrown out
     * A test of the proper production of horses is in <code>BuildingTest</code>
     */
    public void testNoHorsesOverProduction() {
        Game game = getGame();
        game.setMap(getTestMap());

        Colony colony = getStandardColony(1);

        Building pasture = colony.getBuilding(countryType);
        assertEquals(horsesType, pasture.getGoodsOutputType());
        assertEquals("Wrong warehouse capacity in colony",100,colony.getWarehouseCapacity());

        // Still room for more
        colony.addGoods(horsesType, 99);
        assertTrue(colony.getNetProductionOf(foodType) > 0);

        assertEquals("Wrong horse production",1, pasture.getProductionOf(horsesType));
        assertEquals("Wrong maximum horse production", 1, pasture.getMaximumProduction());
        assertEquals("Wrong net horse production",1, colony.getNetProductionOf(horsesType));

        // No more room available
        colony.addGoods(horsesType, 1);
        assertEquals("Wrong number of horses in colony",colony.getWarehouseCapacity(), colony.getGoodsCount(horsesType));
        assertEquals("Wrong horse production",0, pasture.getProductionOf(horsesType));
        assertEquals("Wrong maximum horse production", 0, pasture.getMaximumProduction());
        assertEquals("Wrong net horse production",0, colony.getNetProductionOf(horsesType));
    }


    public void testConsumers() {

        Game game = getGame();
        game.setMap(getTestMap());


        Colony colony = getStandardColony(3);
        int units = colony.getUnitCount();
        int buildings = colony.getBuildings().size();

        List<Consumer> consumers = colony.getConsumers();

        // units come first
        for (int index = 0; index < units; index++) {
            assertTrue(consumers.get(index).toString(),
                       consumers.get(index) instanceof Unit);
        }
        // buildings come next
        for (int index = units; index < units + buildings; index++) {
            assertTrue(consumers.get(index).toString(),
                       consumers.get(index) instanceof Building);
        }
        // build and population queues come last
        for (int index = units + buildings; index < units + buildings + 2; index++) {
            assertTrue(consumers.get(index).toString(),
                       consumers.get(index) instanceof BuildQueue);
        }

        Building country = colony.getBuilding(countryType);
        assertTrue(consumers.contains(country));

        Building depot = colony.getBuilding(depotType);
        assertTrue(consumers.contains(depot));

        int countryIndex = consumers.indexOf(country);
        int depotIndex = consumers.indexOf(depot);
        assertTrue(countryIndex >= 0);
        assertTrue(depotIndex >= 0);
        assertTrue("Priority of depot should be higher than that of country",
                   depotIndex < countryIndex);

        BuildingType armoryType = spec().getBuildingType("model.building.armory");
        Building armory = new ServerBuilding(getGame(), colony, armoryType);
        colony.addBuilding(armory);
        consumers = colony.getConsumers();

        // units come first
        for (int index = 0; index < units; index++) {
            assertTrue(consumers.get(index).toString(),
                       consumers.get(index) instanceof Unit);
        }
        int offset = units + buildings;
        // buildings come next
        for (int index = units; index < offset; index++) {
            assertTrue(consumers.get(index).toString(),
                       consumers.get(index) instanceof Building);
        }
        // build queue comes last
        assertTrue(consumers.get(offset).toString(),
                   consumers.get(offset) instanceof BuildQueue);
        // armory has a lower priority than the build queue
        assertTrue(consumers.get(offset + 1).toString(),
                   consumers.get(offset + 1) instanceof Building);
        assertEquals(armoryType, ((Building) consumers.get(offset + 1)).getType());
        // population queue comes last
        assertTrue(consumers.get(offset + 2).toString(),
                   consumers.get(offset + 2) instanceof BuildQueue);


    }


    public void testProductionMap() {
        ProductionMap pm = new ProductionMap();

        pm.add(new AbstractGoods(cottonType, 33));
        assertEquals(33, pm.get(cottonType).getAmount());

        pm.add(new AbstractGoods(grainType, 44));
        assertEquals(44, pm.get(grainType).getAmount());
        assertEquals(44, pm.get(foodType).getAmount());

        pm.remove(new AbstractGoods(grainType, 22));
        assertEquals(22, pm.get(grainType).getAmount());
        assertEquals(22, pm.get(foodType).getAmount());

        pm.remove(new AbstractGoods(foodType, 11));
        assertEquals(11, pm.get(grainType).getAmount());
        assertEquals(11, pm.get(foodType).getAmount());
    }


    public void testProduction() {
        Game game = getGame();
        game.setMap(getTestMap());

        Colony colony = getStandardColony(3);
        ColonyTile tile = colony.getColonyTile(colony.getTile());

        assertEquals(0, colony.getGoodsCount(foodType));

        assertEquals(grainType, tile.getProduction().get(0).getType());
        assertEquals(5, tile.getProduction().get(0).getAmount());
        assertEquals(cottonType, tile.getProduction().get(1).getType());
        assertEquals(2, tile.getProduction().get(1).getAmount());

        for (Unit unit : colony.getUnitList()) {
            ProductionInfo unitInfo = colony.getProductionInfo(unit);
            assertNotNull(unitInfo);
            assertEquals(2, unitInfo.getConsumption().size());
            assertEquals(2, unitInfo.getMaximumConsumption().size());
            ProductionInfo tileInfo = colony.getProductionInfo(unit.getLocation());
            assertEquals(1, tileInfo.getProduction().size());
            assertEquals(grainType, tileInfo.getProduction().get(0).getType());
            assertEquals(5, tileInfo.getProduction().get(0).getAmount());
        }

        /*
        TypeCountMap<GoodsType> grossProduction = new TypeCountMap<GoodsType>();
        TypeCountMap<GoodsType> netProduction = new TypeCountMap<GoodsType>();
        for (ProductionInfo productionInfo : info.values()) {
            for (AbstractGoods goods : productionInfo.getProduction()) {
                grossProduction.incrementCount(goods.getType(), goods.getAmount());
                netProduction.incrementCount(goods.getType().getStoredAs(), goods.getAmount());
            }
            for (AbstractGoods goods : productionInfo.getStorage()) {
                grossProduction.incrementCount(goods.getType(), goods.getAmount());
                netProduction.incrementCount(goods.getType().getStoredAs(), goods.getAmount());
            }
            for (AbstractGoods goods : productionInfo.getConsumption()) {
                netProduction.incrementCount(goods.getType().getStoredAs(), -goods.getAmount());
            }
        }

        assertEquals(2, grossProduction.getCount(cottonType));
        assertEquals(2, colony.getNetProductionOf(cottonType));

        assertEquals(20, grossProduction.getCount(grainType));
        assertEquals(0, colony.getNetProductionOf(grainType));

        assertEquals(3, grossProduction.getCount(bellsType));
        assertEquals(0, colony.getNetProductionOf(bellsType));

        assertEquals(1, grossProduction.getCount(crossesType));
        assertEquals(1, colony.getNetProductionOf(crossesType));

        // this is storage only
        assertEquals(7, grossProduction.getCount(foodType));
        // this includes implicit type change and consumption
        assertEquals(14, colony.getNetProductionOf(foodType));

        colony.addGoods(horsesType, 50);
        colony.getUnitList().get(0).setWorkType(cottonType);
        Building weaverHouse = colony.getBuilding(spec().getBuildingType("model.building.weaverHouse"));
        colony.getUnitList().get(1).setLocation(weaverHouse);

        info = colony.getProductionAndConsumption();

        assertEquals(grainType, tile.getProduction().get(0).getType());
        assertEquals(5, tile.getProduction().get(0).getAmount());
        assertEquals(cottonType, tile.getProduction().get(1).getType());
        assertEquals(2, tile.getProduction().get(1).getAmount());

        grossProduction = new TypeCountMap<GoodsType>();
        netProduction = new TypeCountMap<GoodsType>();
        for (ProductionInfo productionInfo : info.values()) {
            for (AbstractGoods goods : productionInfo.getProduction()) {
                grossProduction.incrementCount(goods.getType(), goods.getAmount());
                netProduction.incrementCount(goods.getType().getStoredAs(), goods.getAmount());
            }
            for (AbstractGoods goods : productionInfo.getStorage()) {
                grossProduction.incrementCount(goods.getType(), goods.getAmount());
                netProduction.incrementCount(goods.getType().getStoredAs(), goods.getAmount());
            }
            for (AbstractGoods goods : productionInfo.getConsumption()) {
                netProduction.incrementCount(goods.getType().getStoredAs(), -goods.getAmount());
            }
        }

        assertEquals(4, grossProduction.getCount(cottonType));
        assertEquals(1, colony.getNetProductionOf(cottonType));

        assertEquals(3, grossProduction.getCount(clothType));
        assertEquals(3, colony.getNetProductionOf(clothType));

        assertEquals(10, grossProduction.getCount(grainType));
        assertEquals(0, colony.getNetProductionOf(grainType));

        assertEquals(2, grossProduction.getCount(horsesType));
        assertEquals(2, colony.getNetProductionOf(horsesType));

        assertEquals(3, grossProduction.getCount(bellsType));
        assertEquals(0, colony.getNetProductionOf(bellsType));

        assertEquals(1, grossProduction.getCount(crossesType));
        assertEquals(1, colony.getNetProductionOf(crossesType));

        // this is storage only
        assertEquals(2, grossProduction.getCount(foodType));
        // this includes implicit type change and consumption
        assertEquals(2, colony.getNetProductionOf(foodType));

        */
    }

    public void testGetPotentialProduction() {
        Game game = getGame();
        game.setMap(getTestMap());

        Colony colony = getStandardColony(1);
        ColonyTile colonyTile = colony.getColonyTile(colony.getTile());
        assertNotNull(colonyTile);
        assertEquals(plainsType, colony.getTile().getType());
        Building townHall = colony.getBuilding(townHallType);
        assertNotNull(townHall);
        UnitType colonistType = spec().getDefaultUnitType();
        assertNotNull(colonistType);

        assertEquals("Zero potential production of cotton in town hall", 0,
            townHall.getPotentialProduction(colonistType, cottonType));
        assertEquals("Basic potential production of bells in town hall", 
            (int) FeatureContainer.applyModifiers(townHallType.getBasicProduction(),
                game.getTurn(), townHall.getProductionModifiers()),
            townHall.getPotentialProduction(colonistType, bellsType));

        assertEquals("Basic potential production of cotton in town hall",
            plainsType.getProductionOf(cottonType, null),
            colonyTile.getPotentialProduction(colonistType, cottonType));
        assertEquals("Zero potential production of cotton in town hall", 0,
            townHall.getPotentialProduction(colonistType, cottonType));
    }
}
