package de.hagenah.diplomacy.addins;

import de.hagenah.helper.*;
import de.hagenah.util.IntegerMap;
import de.hagenah.diplomacy.diptool.*;
import de.hagenah.diplomacy.game.*;
import de.hagenah.diplomacy.map.*;
import java.util.*;
import javax.swing.*;

/**
 * <p>
 * This add-in tests some game groups, a game group, or a game.
 * If all necessary information is present then the turns
 * are adjudicated and the result is compared with the
 * original adjudication. If some information is missing
 * it is determined which information is missing.
 * </p>
 */
public class TestAddIn
{
    /** true if the summary information must be available */
    private boolean Summary;
    
    /**
     * Each time the add-in is called a new object is created. This constructor
     * should not return before the add-in has finished. It is executed by the
     * event dispatching thread.
     * 
     * @param parent
     *   A parent frame for dialog boxes
     * @param preferences
     *   The preferences of the user
     * @param obj
     *   The selected object in the tree:
     *   <ul>
     *   <li>a DipTurn, or</li>
     *   <li>a DipGame, or</li>
     *   <li>a DipGameGroup, or</li>
     *   <li>a Map (String &rarr; DipGameGroup)</li>
     *   </ul>
     */
    public TestAddIn(JFrame parent,Preferences preferences,Object obj)
    {
        List errors;
        StringBuffer text;
        Iterator i;

        Summary=(JOptionPane.showConfirmDialog(parent,"Has the summary information to be available?","DipTool Test Add-In",JOptionPane.YES_NO_OPTION,JOptionPane.QUESTION_MESSAGE)==JOptionPane.YES_OPTION);
        // Find errors
        errors=new LinkedList();
        if(obj instanceof DipGame)
            testGame((DipGame)obj,"",errors);
        else if(obj instanceof DipGameGroup)
            testGameGroup((DipGameGroup)obj,"",errors);
        else if(obj instanceof Map)
            testRoot((Map)obj,"",errors);
        else
            throw new IllegalArgumentException("Type must not be 'Turn'");
        // Show errors
        if(!errors.isEmpty())
        {
            text=new StringBuffer();
            i=errors.iterator();
            while(i.hasNext())
            {
                text.append((String)i.next());
                if(i.hasNext())
                    text.append("\n");
            }
            SwingHelper.showMessageDialog(parent,"The following errors were found:",text.toString(),"DipTool Test Add-In",JOptionPane.INFORMATION_MESSAGE);
        }
        else
            SwingHelper.showMessageDialog(parent,"No errors were found.",null,"DipTool Test Add-In",JOptionPane.INFORMATION_MESSAGE);
    }

    /**
     * Tests all game groups for errors.
     *
     * @param gamegroups
     *   A Map (String &rarr; GameGroup) with the game groups
     * @param location
     *   The location of these game groups
     * @param errors
     *   A List (String objects) to which the found errors are added
     */
    private void testRoot(Map gamegroups,String location,List errors)
    {
        Map.Entry gamegroup;
        Iterator i;

        i=gamegroups.entrySet().iterator();
        while(i.hasNext())
        {
            gamegroup=(Map.Entry)i.next();
            testGameGroup((GameGroup)gamegroup.getValue(),location+(String)gamegroup.getKey()+"/",errors);
        }
    }

    /**
     * Tests all games for errors.
     *
     * @param gamegroup
     *   A game group with the games
     * @param location
     *   The location of these games
     * @param errors
     *   A List (String objecs) to which the found errors are added
     */
    private void testGameGroup(GameGroup gamegroup,String location,List errors)
    {
        Game game;
        Iterator i;

        i=gamegroup.getGames().iterator();
        while(i.hasNext())
        {
            game=(Game)i.next();
            testGame(game,location+game+"/",errors);
        }
    }

    /**
     * Tests a game for errors.
     *
     * @param game
     *   The game
     * @param location
     *   The location of this game
     * @param errors
     *   A List (String objects) to which the found errors are added
     */
    private void testGame(Game game,String location,List errors)
    {
        Turn previous,current,next,last;
        Collection turns;
        Iterator i;
        String gamelocation;
        Set victors,survivors;
        MapData map;
        Map specialphasecenters;
        int winningcenters;
        int j;
        boolean dias,winningcentersdefault;

        map=game.getMap();
        turns=game.getTurns();
        if(location.endsWith("/"))
            gamelocation=location.substring(0,location.length()-1);
        else
            gamelocation=location;
        if(gamelocation.length()==0)
            gamelocation="Game";
        // Determine winningcenters
        try
        {
            // Ken Lowe judge: Winning Centers
            // DPJudge: Victory
            if(game.getParameter("Winning Centers")!=null)
                winningcenters=Integer.parseInt(game.getParameter("Winning Centers"));
            else
                winningcenters=Integer.parseInt(game.getParameter("Victory"));
            winningcentersdefault=false;
        }
        catch(RuntimeException e)
        {
            // Default: More than half of the centers
            winningcenters=map.getCenters().size()/2+1;
            winningcentersdefault=true;
        }
        // Get last turn
        if(turns.isEmpty())
            last=null;
        else
            last=(Turn)CollectionHelper.getLast(turns);
        // Test PersonInfo
        if(Summary)
        {
            Set persons;
            PersonInfo personinfo;
            
            persons=new TreeSet(map.getCountries());
            persons.add(Person.MASTER);
            i=game.getPersonInfo().iterator();
            while(i.hasNext())
            {
                personinfo=(PersonInfo)i.next();
                if(personinfo.getFirstPhase().equals(map.getStartPhase()))
                    persons.remove(personinfo.getPerson());
            }
            if(!persons.isEmpty())
            {
                if(persons.size()==map.getCountries().size()+1)
                    errors.add(gamelocation+": The information about all persons is missing");
                else if(persons.size()==map.getCountries().size() && !persons.contains(Person.MASTER))
                    errors.add(gamelocation+": The information about all players is missing");
                else
                    errors.add(gamelocation+": The information about "+Person.toString(persons,0)+" is missing");
            }
        }
        // Test parameters
        if(Summary && game.getParameters()==null)
            errors.add(gamelocation+": The parameters are missing");
        // Test comment
        if(Summary && game.getComment()==null)
            errors.add(gamelocation+": The comment is missing");
        // Test victors
        victors=game.getVictors();
        if(victors!=null)
        {
            dias=game.hasParameterFlag("Flags","DIAS") || // Ken Lowe judge
                 (game.getParameter("Victory")!=null && game.getParameter("Timing")!=null && !game.hasParameterFlag("Flags","NO_DIAS")); // DPJudge
            if(turns.isEmpty())
            {
                if(dias && game.getLastPhase()==null && !victors.equals(map.getCountries()))
                    errors.add(gamelocation+": Draw doesn't include all survivors");
            }
            else
            {
                if(last.areCentersKnown())
                {
                    survivors=new TreeSet(last.getCenters().values());
                    if(dias)
                    {
                        if(victors.size()==1)
                        {
                            if(game.wasConceded())
                            {
                                IntegerMap centers;
                                int max,count;
                                
                                centers=new IntegerMap();
                                i=last.getCenters().values().iterator();
                                while(i.hasNext())
                                    centers.increment(i.next());
                                max=centers.get(CollectionHelper.getFirst(victors));
                                count=0;
                                i=centers.iterator();
                                while(i.hasNext())
                                    if(centers.get(i.next())>=max)
                                        count++;
                                if(count!=1)
                                    errors.add(gamelocation+": The victor has not enough centers for a concession");
                            }
                            else
                            {
                                if(last.getCenters((Country)CollectionHelper.getFirst(victors)).size()<winningcenters)
                                {
                                    if(winningcentersdefault)
                                        errors.add(gamelocation+": The victor has not enough centers (default needed: "+winningcenters+")");
                                    else
                                        errors.add(gamelocation+": The victor has not enough centers (needed: "+winningcenters+")");
                                }
                            }
                        }
                        else
                        {
                            if(!victors.containsAll(survivors))
                                errors.add(gamelocation+": The draw doesn't include all survivors");
                            else if(!survivors.containsAll(victors))
                                errors.add(gamelocation+": The draw contains an already eliminated player");
                        }
                    }
                    else
                    {
                        if(!survivors.containsAll(victors))
                            errors.add(gamelocation+": The draw contains an already eliminated player");
                    }
                }
            }
        }
        else if(Summary)
            errors.add(gamelocation+": The victor resp. the draw participants are missing");
        // Test last phase
        if(game.getLastPhase()!=null)
        {
            if(!turns.isEmpty())
            {
                previous=game.getTurn(last.getNumber()-1);
                if(previous==null)
                {
                    if(last.getNumber()==Turn.NUMBER_FIRST)
                        errors.add(gamelocation+": The adjudication of the last turn ("+game.getLastPhase()+") is missing");
                }
                else
                {
                    j=previous.getPhase().compareTo(game.getLastPhase());
                    if(j<0)
                        errors.add(gamelocation+": The adjudication of the last turn ("+game.getLastPhase()+") is missing");
                    else if(j>0)
                        errors.add(gamelocation+": A turn after the last turn ("+game.getLastPhase()+") appeared");
                    else if(last.areOrdersKnown()) // j==0
                        errors.add(gamelocation+": Orders after the last turn ("+game.getLastPhase()+") appeared");
                }
            }
            else
                errors.add(gamelocation+": The adjudication of the last turn ("+game.getLastPhase()+") is missing");
        }
        else if(Summary)
            errors.add(gamelocation+": The information about the last turn is missing");
        // Test turns
        specialphasecenters=null;
        previous=null;
        i=turns.iterator();
        if(i.hasNext())
            current=(Turn)i.next();
        else
            current=null;
        while(current!=null)
        {
            if(i.hasNext())
                next=(Turn)i.next();
            else
                next=null;
            // Set specialphasecenters
            if(map.getHomeCentersType()==MapData.HOME_CENTERS_PHASE)
            {
                // The special phase could be missing because there were
                // no builds
                // Note: getCenters returns null if the centers are not known
                j=current.getPhase().compareTo(map.getHomeCentersPhase());
                if(j==0 ||
                   (j>0 && previous!=null && 
                    previous.getPhase().compareTo(map.getHomeCentersPhase())<0 &&
                    previous.getNumber()==current.getNumber()-1))
                     specialphasecenters=current.getCenters();
            }
            // Turn missing?
            if((previous==null && current.getNumber()!=Turn.NUMBER_FIRST) ||
               (previous!=null && current.getNumber()!=previous.getNumber()+1))
                errors.add(location+current+": A turn is missing before this turn");
            // Should the game have ended in this turn?
            if(current.areCentersKnown() && current.getPhase().isAdjustment() && current!=last)
            {
                Iterator k;
                Country country;
                Map centers;
                int[] count;
                Map.Entry entry;
                
                centers=new TreeMap();
                k=current.getCenters().values().iterator();
                while(k.hasNext())
                {
                    country=(Country)k.next();
                    count=(int[])centers.get(country);
                    if(count==null)
                    {
                        count=new int[1];
                        centers.put(country,count);
                    }
                    count[0]++;
                }
                k=centers.entrySet().iterator();
                while(k.hasNext())
                {
                    entry=(Map.Entry)k.next();
                    count=(int[])entry.getValue();
                    if(count[0]>=winningcenters)
                    {
                        if(winningcentersdefault)
                            errors.add(location+current+": "+entry.getKey()+" has enough centers to win (default needed: "+winningcenters+")");
                        else
                            errors.add(location+current+": "+entry.getKey()+" has enough centers to win (needed: "+winningcenters+")");
                    }
                }
            }
            // Starting position correct?
            if(current.getNumber()==Turn.NUMBER_FIRST)
                testStartPosition(current,map,location+current,errors);
            // Test turn
            if(current.isComplete(false) && next!=null)
            {
                // Adjudication ok?
                if(next.getNumber()==current.getNumber()+1 && next.isComplete(true))
                    testTurn(current,next,map,specialphasecenters,location,errors);
                else
                    testTurn(current,null,map,specialphasecenters,location,errors);
            }
            else
            {
                String missing;
                
                // Information missing?
                missing=current.getMissingInformation(next==null);
                if(missing!=null)
                    errors.add(location+current+": The information about the "+missing+" is missing");
            }
            previous=current;
            current=next;
        }
    }

    /**
     * Compares the turn with the start position of the game.
     *
     * @param current
     *   The turn
     * @param map
     *   The map of the game
     * @param location
     *   The location of this turn
     * @param errors
     *   A List (String objects) to which the found errors are added
     */
    private void testStartPosition(Turn current,MapData map,String location,List errors)
    {
        TreeMap units,centers;
        Iterator i,j;
        Country owner;
        Unit unit;
        SubProvince subprovince;
        Province center;
        Map.Entry entry;

        // Did units vanish?
        // units: Province -> Unit
        units=new TreeMap(current.getUnits());
        i=map.getCountries().iterator();
        while(i.hasNext())
        {
            owner=(Country)i.next();
            j=owner.getStartUnits().iterator();
            while(j.hasNext())
            {
                subprovince=(SubProvince)j.next();
                unit=(Unit)units.get(subprovince.getProvince());
                if(unit==null || unit.getSubProvince()!=subprovince || unit.getCountry()!=owner)
                    addUnitError(owner,subprovince,false,location,errors);
                else
                    units.remove(subprovince.getProvince());
            }
        }
        // Did units appear?
        i=units.values().iterator();
        while(i.hasNext())
        {
            unit=(Unit)i.next();
            addUnitError(unit.getCountry(),unit.getSubProvince(),true,location,errors);
        }
        // Did centers vanish?
        // centers: Province -> Country
        centers=new TreeMap(current.getCenters());
        i=map.getCountries().iterator();
        while(i.hasNext())
        {
            owner=(Country)i.next();
            j=owner.getStartCenters().iterator();
            while(j.hasNext())
            {
                center=(Province)j.next();
                if(centers.get(center)!=owner)
                    errors.add(location+": "+owner.getOwnerName()+" center vanished in "+center);
                else
                    centers.remove(center);
            }
        }
        // Did centers appear?
        i=centers.entrySet().iterator();
        while(i.hasNext())
        {
            entry=(Map.Entry)i.next();
            errors.add(location+": "+((Country)entry.getValue()).getOwnerName()+" center appeared in "+(Province)entry.getKey());
        }
    }

    /**
     * Checks the consistency of the turn and with the next turn.
     *
     * @param current
     *   The current turn (complete)
     * @param next
     *   The next turn (complete up to orders)
     * @param map
     *   The map of the game
     * @param specialphasecenters
     *   If the home centers type is HOME_CENTERS_PHASE and the special home 
     *   centers phase is before the phase of the turn the centers for the 
     *   special home centers phase, null otherwise
     * @param location
     *   The location of the game
     * @param errors
     *   A List (String objects) to which the found errors are added.
     */
    private void testTurn(Turn current,Turn next,MapData map,Map specialphasecenters,String location,List errors)
    {
        Iterator i,j;
        Order order;
        Unit unit,correctunit;
        SubProvince subprovince;
        Country country;
        Map.Entry entry;
        Integer build;
        Phase nextphase;
        // Province -> Unit
        Map units;
        // Province -> Unit
        Map dislodgedunits;
        // Province -> Country
        Map centers;
        // Country -> Integer
        Map builds;
        // Order -> Integer
        SortedMap results;
        // Order
        SortedSet nmrorders,noorderorders,onlynmrorders,onlynoorderorders;
        // String
        ArrayList adjudicatorerrors;
        DecisionAdjudicator adjudicator;

        units=new TreeMap();
        dislodgedunits=new TreeMap();
        centers=new TreeMap();
        builds=new TreeMap();
        results=new TreeMap(Order.UNIQUE_ORDER);
        nmrorders=new TreeSet();
        adjudicatorerrors=new ArrayList();
        adjudicator=new DecisionAdjudicator(
            DecisionAdjudicator.OPTION_NMR_YES|
            DecisionAdjudicator.OPTION_PROXY_YES|
            DecisionAdjudicator.OPTION_NOORDER_IGNORE|
            DecisionAdjudicator.OPTION_RULE_IX7_RELAXED|
            DecisionAdjudicator.OPTION_RULE_XII3_ONE_SUCCESSFUL|
            DecisionAdjudicator.OPTION_RULE_XII5_ARMYNOTCONVOYED);
        nextphase=adjudicator.adjudicate(current,map,specialphasecenters,units,dislodgedunits,centers,builds,nmrorders,results,adjudicatorerrors);
        if(nextphase==null)
        {
            i=adjudicatorerrors.iterator();
            while(i.hasNext())
                errors.add(location+current+": "+i.next());
            return;
        }
        // Check nmrorders
        noorderorders=new TreeSet();
        i=current.getOrders().iterator();
        while(i.hasNext())
        {
            order=(Order)i.next();
            if((order.getResult()&Order.RESULT_NOORDER)!=0)
                noorderorders.add(order);
        }
        onlynmrorders=new TreeSet(Order.UNIQUE_ORDER);
        onlynoorderorders=new TreeSet(Order.UNIQUE_ORDER);
        CollectionHelper.compare(nmrorders,noorderorders,onlynmrorders,onlynoorderorders, new Comparator()
        {
            public int compare(Object o1,Object o2)
            {
                Order order1,order2;
                
                // WAIVE orders are equal if the country is the same
                order1=(Order)o1;
                order2=(Order)o2;
                if(order1.getType()==Order.TYPE_WAIVE && 
                   order2.getType()==Order.TYPE_WAIVE && 
                   order1.getCountry()==order2.getCountry())
                    return 0;
                return order1.compareTo(order2);
            }
        });
        i=onlynmrorders.iterator();
        while(i.hasNext())
        {
            boolean wrong;
            Order wrongorder;
            
            order=(Order)i.next();
            // Wrong remove?
            wrong=false;
            wrongorder=null;
            if(order.getType()==Order.TYPE_REMOVE)
            {
                j=onlynoorderorders.iterator();
                while(j.hasNext())
                {
                    wrongorder=(Order)j.next();
                    if(wrongorder.getType()==Order.TYPE_REMOVE)
                    {
                        wrong=true;
                        // Suppress "Superfluous order ..." error
                        j.remove();
                        break;
                    }
                }
            }
            if(wrong)
                // Wrong remove order
                errors.add(location+current+": Expected "+order.toString(0,null)+" instead of "+wrongorder.toString(0,null));
            else
            {
                // Missing order
                switch(order.getType())
                {
                case Order.TYPE_WAIVE:
                    errors.add(location+current+": Missing "+order.getCountry().getOwnerName()+" build");
                    break;
                case Order.TYPE_REMOVE:
                    errors.add(location+current+": Missing "+order.getCountry().getOwnerName()+" remove");
                    break;
                default: // TYPE_HOLD, TYPE_DISBAND
                    errors.add(location+current+": The order for "+order.getFirst().getUnitText()+" is missing");
                    break;
                }
            }
        }
        i=onlynoorderorders.iterator();
        while(i.hasNext())
        {
            order=(Order)i.next();
            errors.add(location+current+": Superfluous order "+order.toString(Order.TOSTRING_COUNTRY+Order.TOSTRING_OWNER,current.getUnits()));
        }
        if(!onlynmrorders.isEmpty() || !onlynoorderorders.isEmpty())
            return;
        // nmr=noorder: replace nmr results by noorder results
        i=nmrorders.iterator();
        j=noorderorders.iterator();
        while(i.hasNext())
            results.put(j.next(),results.remove(i.next()));
        // Check result flags
        i=current.getOrders().iterator();
        while(i.hasNext())
        {
            int k,result,correctresult;

            order=(Order)i.next();
            // NOORDER already checked
            result=order.getResult()&~Order.RESULT_NOORDER;
            correctresult=((Integer)results.get(order)).intValue()&~Order.RESULT_NOORDER;
            for(k=1;result!=correctresult;k<<=1)
            {
                if((result&k)!=(correctresult&k))
                {
                    addResultError(current,location,errors,order,k);
                    result|=k;
                    correctresult|=k;
                }
            }
        }
        if(next==null)
            return;
        // Did units appear?
        i=next.getUnits().values().iterator();
        while(i.hasNext())
        {
            unit=(Unit)i.next();
            correctunit=(Unit)units.get(unit.getSubProvince().getProvince());
            if(correctunit==null || correctunit.getSubProvince()!=unit.getSubProvince() || correctunit.getCountry()!=unit.getCountry())
                addUnitError(unit.getCountry(),unit.getSubProvince(),true,location+next,errors);
            else
                units.remove(unit.getSubProvince().getProvince());
        }
        // Did units vanish?
        i=units.values().iterator();
        while(i.hasNext())
        {
            unit=(Unit)i.next();
            addUnitError(unit.getCountry(),unit.getSubProvince(),false,location+next,errors);
        }
        // Are the retreats correct?
        if(dislodgedunits.isEmpty())
        {
            if(next.areDislodgedUnitsKnown())
                errors.add(location+next+": There must be no retreat phase");
        }
        else if(!next.areDislodgedUnitsKnown())
            errors.add(location+next+": A retreat phase is missing before this phase");
        else
        {
            // Did dislodged units appear?
            i=next.getDislodgedUnits().values().iterator();
            while(i.hasNext())
            {
                unit=(Unit)i.next();
                correctunit=(Unit)dislodgedunits.get(unit.getSubProvince().getProvince());
                if(correctunit==null || correctunit.getSubProvince()!=unit.getSubProvince() || correctunit.getCountry()!=unit.getCountry())
                    addUnitError(unit.getCountry(),unit.getSubProvince(),true,location+next,errors);
                else
                {
                    SortedSet retreats;

                    // Did retreats appear?
                    retreats=new TreeSet(correctunit.getRetreats());
                    j=unit.getRetreats().iterator();
                    while(j.hasNext())
                    {
                        subprovince=(SubProvince)j.next();
                        if(!retreats.remove(subprovince))
                            errors.add(location+next+": The retreat "+subprovince+" appeared for "+unit.toString(Unit.TOSTRING_COUNTRY));
                    }
                    // Did retreats vanish?
                    j=retreats.iterator();
                    while(j.hasNext())
                    {
                        subprovince=(SubProvince)j.next();
                        errors.add(location+next+": The retreat "+subprovince+" vanished for "+unit.toString(Unit.TOSTRING_COUNTRY));
                    }
                    dislodgedunits.remove(unit.getSubProvince().getProvince());
                }
            }
            // Did dislodged units vanish?
            i=dislodgedunits.values().iterator();
            while(i.hasNext())
            {
                unit=(Unit)i.next();
                addUnitError(unit.getCountry(),unit.getSubProvince(),false,location+next,errors);
            }
        }
        // Did centers appear?
        i=next.getCenters().entrySet().iterator();
        while(i.hasNext())
        {
            entry=(Map.Entry)i.next();
            if(centers.get((Province)entry.getKey())!=(Country)entry.getValue())
                errors.add(location+next+": "+((Country)entry.getValue()).getOwnerName()+" center appeared in "+(Province)entry.getKey());
            else
                centers.remove((Province)entry.getKey());
        }
        // Did centers vanish?
        i=centers.entrySet().iterator();
        while(i.hasNext())
        {
            entry=(Map.Entry)i.next();
            errors.add(location+next+": "+((Country)entry.getValue()).getOwnerName()+" center vanished in "+(Province)entry.getKey());
        }
        // Did builds appear or vanish?
        if(builds.isEmpty())
        {
            if(next.areBuildsKnown())
                errors.add(location+next+": There must be no adjustment phase");
        }
        else if(!next.areBuildsKnown())
        {
            // If no build is possible the judge is allowed jump directly
            // to the next movement phase
            if(nextphase.isAdjustment())
                errors.add(location+next+": An adjustment phase is missing before this phase");
        }
        else
        {
            i=map.getCountries().iterator();
            while(i.hasNext())
            {
                int difference;
                String type;

                country=(Country)i.next();
                difference=next.getBuilds(country);
                build=(Integer)builds.get(country);
                if(build!=null)
                {
                    difference-=build.intValue();
                    if(build.intValue()>0)
                        type="build";
                    else
                    {
                        type="remove";
                        difference=-difference;
                    }
                }
                else
                {
                    if(difference>0)
                        type="build";
                    else
                    {
                        type="remove";
                        difference=-difference;
                    }
                }
                if(difference>1)
                    errors.add(location+next+": "+difference+" "+country.getOwnerName()+" "+type+"s appeared");
                else if(difference==1)
                    errors.add(location+next+": One "+country.getOwnerName()+" "+type+" appeared");
                else if(difference<-1)
                    errors.add(location+next+": "+(-difference)+" "+country.getOwnerName()+" "+type+"s vanished");
                else if(difference==-1)
                    errors.add(location+next+": One "+country.getOwnerName()+" "+type+" vanished");
            }
        }
    }

    /**
     * Adds an error for a result flag of an order.
     *
     * @param current
     *   The current turn.
     * @param location
     *   The location of the game
     * @param errors
     *   A List (String objects) to which the error is added
     * @param order
     *   The order with the wrong result flag
     * @param resultflag
     *   The wrong result flag
     */
    private void addResultError(Turn current,String location,List errors,Order order,int resultflag)
    {
        String name;
        Unit unit;

        switch(resultflag)
        {
        case Order.RESULT_NOORDER:
            name="no order";
            break;
        case Order.RESULT_BOUNCE:
            name="bounce";
            break;
        case Order.RESULT_CUT:
            name="cut";
            break;
        case Order.RESULT_VOID:
            name="void";
            break;
        case Order.RESULT_NOCONVOY:
            name="no convoy";
            break;
        case Order.RESULT_BLOCKED:
            name="blocked";
            break;
        case Order.RESULT_DESTROYED:
            name="destroyed";
            break;
        case Order.RESULT_DISLODGED:
            name="dislodged";
            break;
        default:
            throw new IllegalArgumentException("Illegal value for ResultFlag");
        }
        unit=current.getUnit(order.getFirst().getProvince());
        if(unit==null)
            unit=current.getDislodgedUnit(order.getFirst().getProvince());
        if((order.getResult()&resultflag)==0)
            errors.add(location+current+": The "+name+" flag vanished for "+unit.toString(Unit.TOSTRING_COUNTRY));
        else
            errors.add(location+current+": The "+name+" flag appeared for "+unit.toString(Unit.TOSTRING_COUNTRY));
    }

    /**
     * Adds an unit error to errors.
     *
     * @param owner
     *   The owner of the vanished or appeared unit
     * @param subprovince
     *   The subprovince where the unit vanished or appeared
     * @param appeared
     *   true if a unit appeared, false if a unit vanished
     * @param location
     *   Where it happened
     * @param errors
     *   A List (String objects) to which the found errors are added.
     */
    private void addUnitError(Country owner,SubProvince subprovince,boolean appeared,String location,List errors)
    {
        String verb;

        if(appeared)
            verb="appeared";
        else
            verb="vanished";
        if(subprovince.isSea())
        {
            if(subprovince.getProvince().isSea())
                errors.add(location+": "+owner.getOwnerName()+" fleet "+verb+" in the "+subprovince);
            else
                errors.add(location+": "+owner.getOwnerName()+" fleet "+verb+" in "+subprovince);
        }
        else
            errors.add(location+": "+owner.getOwnerName()+" army "+verb+" in "+subprovince);
    }
}
