//==============================================================================
// The Society of Actuaries (SOA) makes this computer model and its computer
// code available to its members for educational purposes. The model and its
// code may be modified and redistributed without prior authorization from the
// SOA. The SOA distributes this computer model and its computer code 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.
//==============================================================================

package agents;

//==============================================================================
// Import statements
//------------------------------------------------------------------------------
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.ListIterator;
import java.util.Random;
import implementation.SimulationUserPanel;
import repast.simphony.context.Context;
import repast.simphony.engine.environment.RunEnvironment;
import repast.simphony.parameter.Parameter;
import repast.simphony.random.RandomHelper;
import repast.simphony.space.graph.Network;
import repast.simphony.space.graph.RepastEdge;
import repast.simphony.ui.probe.ProbeID;
import resources.AgentID;
import resources.Constants;
import resources.Constants.AgentType;
import resources.Constants.GoalDescription;
import resources.Constants.PlanTag;
import resources.FavoredPlan;
import resources.Goal;
import resources.InsurancePlan;

@SuppressWarnings("unused")
public class Person extends Agent
{

//==============================================================================
// Attribute variables
// These variables do not change during the agent's life (unless the user
// changes an attribute parameter during a simulation).
//------------------------------------------------------------------------------
    private double grossIncome;
    private ArrayList<Goal> goalList = new ArrayList<Goal>();
    private String goal_First;
    private String goal_Second;
    private String goal_Third;
    private String goal_Fourth;
    private String goal_Fifth;

//==============================================================================


//==============================================================================
// Memory variables
// These variables can change during an agent's life.
// There are two types of memory variables:
// 1. Currently updated variables:  Variables that are updated during the period 
//    to which they apply.
// 2. Previously updated variables:  Variables that are updated during the period 
//    prior to the period in which they apply.
//------------------------------------------------------------------------------

    // 1. Currently updated memory variables
    // Variables from p1_PurchasePlan()
    private double currentLowestPremium;
    private double currentPenaltyTaxAmount;
    private PlanTag currentFirstOfferedPlan;
    private PlanTag currentLowestCostPlan;
    private PlanTag currentRichestPlan;
    private PlanTag currentFavoredPlan;
    private boolean currentAdvertisingImpact;
    private boolean currentPlanPurchaseDecision;
    private PlanTag currentPlanChosen;
    private PlanTag priorPlanChosen;
    private PlanTag penultimatePlanChosen;
    private boolean currentExchangeParticipationStatus;
    private boolean priorExchangeParticipationStatus;
    private boolean currentInertiaEffect;
    private double currentDiseaseStatus;
    private boolean currentHealthGravity;
    private boolean currentAdverseSelection;
    private double currentAdverseSelectionValue;
    private double currentPremiumAmountPaid;
    private double currentNetIncome;

    // Variables from p2_PayPenaltyTax()
    private double currentPenaltyTaxAmountPaid;

    // Variables from p3_RequestTreatment()
    private boolean currentTreatmentRequest;

    // Variables from p4_PayCopayment()
    private double currentCopaymentAmountPaid;

    // Variables from p5_UpdateDiseaseStatus()
    // None


    // 2. Previously updated memory variables
    private double nextCurrentDiseaseStatus;
    private double nextCurrentDiseaseStatusRandomIncrement;

//==============================================================================


//==============================================================================
// Other variables
// These are other variables used in the class, or required by other classes.
//------------------------------------------------------------------------------
    private Context<Object> simulationContext;
    private double tickCount;

    private double priorDiseaseStatus;

    private String currentPlanChosenString;
    private String priorPlanChosenString;
    private String currentFirstOfferedPlanString;
    private String currentLowestCostPlanString;
    private String currentRichestPlanString;
    private String currentFavoredPlanString;

//==============================================================================
// Method:  Person() constructor
//------------------------------------------------------------------------------
    public Person(Context<Object> pContext, AgentID pPersonID)
    {

        setAgentID(pPersonID);
        simulationContext = pContext;

        init_InitializeVariables_Constructor();

    }//end Person()
//==============================================================================


//==============================================================================
// Methods: Initialization methods for the agent. These are used in
//          the agent's constructor method or at the beginning of each
//          simulation period.
//==============================================================================

//==============================================================================
// Method:  init_InitializeVariables_Constructor()
// The constructor does the following:
// 1. Initializes variables that remain constant throughout
//    an agent's life (unless the user changes parameters).
//    These are the agent's "attribute" variables.
// 2. Initializes the value of all other variables.
// This method gives a valid (non-null) value to all the agent's variables.
//------------------------------------------------------------------------------
    private void init_InitializeVariables_Constructor()
    {

        // 1. Initialize "attribute" variables.
        // This initializes currentDiseaseStatus, currentNetIncome,
        // grossIncome, and goals.
        init_InitializeGoals();
        init_InitializeIncome();
        init_InitializeDiseaseStatus();

        // 2. Initialize the value of all other variables.
        currentLowestPremium = 0.0;
        currentPenaltyTaxAmount = 0.0;
        currentFirstOfferedPlan = PlanTag.None;
        currentLowestCostPlan = PlanTag.None;
        currentRichestPlan = PlanTag.None;
        currentFavoredPlan = PlanTag.None;
        currentAdvertisingImpact = false;
        currentPlanPurchaseDecision = false;
        currentPlanChosen = PlanTag.None;
        priorPlanChosen = PlanTag.None;
        penultimatePlanChosen = PlanTag.None;
        currentExchangeParticipationStatus = false;
        priorExchangeParticipationStatus = false;
        currentInertiaEffect = false;
        currentHealthGravity = false;
        currentAdverseSelection = false;
        currentAdverseSelectionValue = 0.0;
        currentPremiumAmountPaid = 0.0;
        currentPenaltyTaxAmountPaid = 0.0;
        currentTreatmentRequest = false;
        currentCopaymentAmountPaid = 0.0;
        nextCurrentDiseaseStatus = 0.0;
        nextCurrentDiseaseStatusRandomIncrement = 0.0;
        priorDiseaseStatus = 0.0;
        currentPlanChosenString = PlanTag.None.toString();
        priorPlanChosenString = PlanTag.None.toString();
        currentFirstOfferedPlanString = PlanTag.None.toString();
        currentLowestCostPlanString = PlanTag.None.toString();
        currentRichestPlanString = PlanTag.None.toString();
        currentFavoredPlanString = PlanTag.None.toString();

    }//end init_InitializeVariables_Constructor()

//==============================================================================
// Method:  init_InitializeVariables_BOP() (beginning of period)
// This method does the following:
// 1. Updates attribute variables. If the user changes an attribute parameter
//    during a simulation, these may change.
// 2. Updates the prior period values of all variables (after the first period).
// 3. Provides "current" values for certain variables that the agent
//    does not set at the beginning of the period with a separate behavior.
//    This is done for those variables will be needed by behaviors of this agent
//    or by other agents during the period.
// 4. Re-initialize all CURRENT (NOT PRIOR) variables (except those that would cause
//    display or chart problems (flickering, etc.).
// 5. removes agents that have died.
// 6. initialize edges (remove them).
//------------------------------------------------------------------------------
    public void init_InitializeVariables_BOP()
    {

        // 1. Update attribute variables.
        // Note that income, disease status, and goals are initialized only once
        // (because they are determined randomly).

        // 2. Update prior period values of all variables (after the first period).
        tickCount = RunEnvironment.getInstance().getCurrentSchedule().getTickCount();
        if (tickCount >= 1.0)
        {
            penultimatePlanChosen = priorPlanChosen;
            priorPlanChosen = currentPlanChosen;
            priorExchangeParticipationStatus = currentExchangeParticipationStatus;
            priorDiseaseStatus = currentDiseaseStatus;
            priorPlanChosenString = currentPlanChosenString;
        }//end if

        // 3. Provide "current" values for certain variables that the agent does not set at the
        //    beginning of the period with a separate behavior.
        tickCount = RunEnvironment.getInstance().getCurrentSchedule().getTickCount();
        if (tickCount < 1.0)
        {
            // None
            // Disease status is only initialized once, in the constructor, so that the agent
            // will display correctly.
        }//end if
        else
        {
            currentDiseaseStatus = nextCurrentDiseaseStatus;
        }//end if

        // 4. Re-initialize all CURRENT (NOT PRIOR) variables (except those that would cause
        //    display or chart problems (flickering, etc.).
        currentLowestPremium = 0.0;
        currentPenaltyTaxAmount = 0.0;
        currentFirstOfferedPlan = PlanTag.None;
        currentLowestCostPlan = PlanTag.None;
        currentRichestPlan = PlanTag.None;
        currentFavoredPlan = PlanTag.None;
        currentAdvertisingImpact = false;
        currentPlanPurchaseDecision = false;
        currentPlanChosen = PlanTag.None;
//        currentExchangeParticipationStatus = false; // Would cause display flickering
        currentInertiaEffect = false;
        currentHealthGravity = false;
        currentAdverseSelection = false;
        currentAdverseSelectionValue = 0.0;
        currentPremiumAmountPaid = 0.0;
        currentPenaltyTaxAmountPaid = 0.0;
        currentTreatmentRequest = false;
        currentCopaymentAmountPaid = 0.0;
//        nextCurrentDiseaseStatus = 0.0; // Would cause display flickering
        nextCurrentDiseaseStatusRandomIncrement = 0.0;
        currentPlanChosenString = PlanTag.None.toString();
        currentFirstOfferedPlanString = PlanTag.None.toString();
        currentLowestCostPlanString = PlanTag.None.toString();
        currentRichestPlanString = PlanTag.None.toString();
        currentFavoredPlanString = PlanTag.None.toString();

        // 5. Remove agents that have died
        if (currentDiseaseStatus >= 10.00)
        {
            simulationContext.remove(this);
            Environment.getPersonList().remove(this);
        }//end if

        // 6. Initialize edges (remove them)
//        @SuppressWarnings("unchecked")
//        Network<Object> network = (Network<Object>)simulationContext.getProjection("Person-Insurer network");
//        network.removeEdges();

    }//end init_InitializeVariables_BOP()
//------------------------------------------------------------------------------

//==============================================================================
// Method:  init_InitializeGoals()
//------------------------------------------------------------------------------
    private void init_InitializeGoals()
    {
        //------------------------------------------------------------------
        // Determine the goals for the Person.
        // Goal is a class that has two variables:  goalDescription
        // and goalPriority. This method generates five of these, in an
        // array list, goalList, and in five separate variables:  goal_First
        // through goal_Fifth.
        // To do this, it uses an array list of column indices for the goal priority
        // distribution matrix. It proceeds row by row through the matrix,
        // identifying the priority order column for that row (through a draw from
        // a uniform random variable), adding the priority to the proper goal,
        // and then deleting this column index from the list of indices,
        // until the list is empty.
        // For this scheme to work properly, the rows have to be stepped
        // through in a random order.
        //------------------------------------------------------------------
        ArrayList<Integer> remainingIndicesList = new ArrayList<Integer>();
        double[] probabilities = {0.0, 0.0, 0.0, 0.0, 0.0};
        int rowIndex;
        int rowsProcessed = 0;
        int goalIndex = 0;
        int goalPriority = 0;
        int indexToRemove = 0;

        int randomStartRow = RandomHelper.nextIntFromTo(0, 4);
        rowIndex = randomStartRow;

        for (int i = 0; i < 5; i++)
        {
            remainingIndicesList.add(i);
        }//end for
        
        while (remainingIndicesList.size() != 0)
        {
            Goal goal = new Goal();
            
            // Set the goal description
            switch (rowIndex)
            {
                case 0:
                {
                    goal.setGoalDescription(GoalDescription.MaximizeIncome);
                    break;
                }//end case

                case 1:
                {
                    goal.setGoalDescription(GoalDescription.IncreaseHealth);
                    break;
                }//end case

                 case 2:
                {
                    goal.setGoalDescription(GoalDescription.Conform);
                    break;
                }//end case

                case 3:
                {
                    goal.setGoalDescription(GoalDescription.FollowPolicy);
                    break;
                }//end case

                case 4:
                {
                    goal.setGoalDescription(GoalDescription.FollowAdvertising);
                    break;
                }//end case

                default:
                {
                // There should be no default
                    System.out.println("Error:  Unexpected switch default in Person init_InitializeGoals()");
                }//end case

            }//end switch

            // Fill the probabilities array
            for (int j = 0; j < 5; j++)
            {
                probabilities[j] = SimulationUserPanel.getPersonGoalPriorities()[rowIndex][j];
            }//end for

            // Get a random number in the right range
            double rangeLimit = 0.0;
            for (int k = 0; k < remainingIndicesList.size(); k++)
            {
                rangeLimit = rangeLimit + probabilities[remainingIndicesList.get(k)];
            }//end for

            double randomNumber = RandomHelper.nextDoubleFromTo(0, rangeLimit);

            // Determine the index to use
            double probabilitySum = 0.0;
            boolean indexFound = false;

//            System.out.println("======================="); //Test statement

            for (int m = 0; m < remainingIndicesList.size(); m++)
            {
                probabilitySum = probabilitySum + probabilities[remainingIndicesList.get(m)];
                if ((probabilitySum >= randomNumber) && !indexFound)
                {
                    goalIndex = remainingIndicesList.get(m);
                    goalPriority = goalIndex;
                    indexToRemove = m;
                    indexFound = true;
                }//end if

                //More tests (this method is particularly tricky)
//                    System.out.println("rowIndex:  " + rowIndex);
//                    System.out.println("Inside for loop");
//                    System.out.println("m:  " + m);
//                    System.out.println("remainingIndicesList.size():  " + remainingIndicesList.size());
//                    System.out.println("probabilitySum:  " + probabilitySum);
//                    System.out.println("rangeLimit:  " + rangeLimit);
//                    System.out.println("randomNumber:  " + randomNumber);
//                    System.out.println("goalIndex:  " + goalIndex);
//                    System.out.println("indexToRemove:  " + indexToRemove);
//                    System.out.println("indexFound:  " + indexFound);

            }//end for

//            System.out.println("=======================");  // Test statement

            // Set the goal priority
            switch (goalPriority)
            {
                case 0:
                {
                    goal.setGoalPriority(Constants.FIRST);
                    break;
                }//end case

                case 1:
                {
                    goal.setGoalPriority(Constants.SECOND);;
                    break;
                }//end case

                 case 2:
                {
                    goal.setGoalPriority(Constants.THIRD);;
                    break;
                }//end case

                case 3:
                {
                    goal.setGoalPriority(Constants.FOURTH);;
                    break;
                }//end case

                case 4:
                {
                    goal.setGoalPriority(Constants.FIFTH);;
                    break;
                }//end case

                default:
                {
                // There should be no default
                    System.out.println("Error:  Unexpected switch default in Person init_InitializeGoals()");
                }//end case

            }//end switch

            // Add the goal to the goal list
            goalList.add(goal);

            // Remove the used index from the remaining indices list
            remainingIndicesList.remove(indexToRemove);

            // Advance the row
            if ((rowIndex == 4))
            {
                if (rowsProcessed < 4)
                {
                    rowIndex = 0;
                    rowsProcessed = rowsProcessed + 1;
                }//end if
            }//end if
            else
            {
                rowIndex = rowIndex + 1;
                rowsProcessed = rowsProcessed + 1;
            }//end else


//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Test 
// This block tests this method.
// To run the test, remove comments from the code below.
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//    System.out.println("Person init_InitializeGoals() in while loop at " +
//            RunEnvironment.getInstance().getCurrentSchedule().getTickCount());
//
//    System.out.println("Goal probabilities:");
//    for (int i = 0; i < 5; i++)
//    {
//        for (int j = 0; j < 5; j++)
//        {
//            System.out.print(SimulationUserPanel.getPersonGoalPriorities()[i][j] + " "); //Person Goals:  A1
//        }//end for
//        System.out.println();
//    }//end for
//
//    System.out.println("Goal description:  " + goal.getGoalDescription());
//
//    for (int j = 0; j < 5; j++)
//    {
//        System.out.println("probabilities[" + j + "]:  " + probabilities[j]);
//    }//end for
//
//    System.out.println("randomNumber:  " + randomNumber);
//    System.out.println("goalPriority:  " + goalPriority);
//
//    for (int i = 0; i < goalList.size(); i++)
//    {
//        System.out.println("Goal " + i + " description:  " + goalList.get(i).getGoalDescription());
//        System.out.println("Goal " + i + " priority:  " + goalList.get(i).getGoalPriority());
//    }//end for
//
//    for (int i = 0; i < remainingIndicesList.size(); i++)
//    {
//        System.out.println("remainingIndicesList(" + i + "):  " + remainingIndicesList.get(i));
//    }//end for
//
//    System.out.println("rowIndex:  " + rowIndex);
//
//    System.out.println("-----------------");
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

        }//end while

        // Set the individual goal variables
        for (int i = 0; i < goalList.size(); i++)
        {
            switch (goalList.get(i).getGoalPriority())
            {
                case Constants.FIRST:
                {
                    goal_First = goalList.get(i).getGoalDescription().toString();
                    break;
                }//end case
                case Constants.SECOND:
                {
                    goal_Second = goalList.get(i).getGoalDescription().toString();
                    break;
                }//end case
                case Constants.THIRD:
                {
                    goal_Third = goalList.get(i).getGoalDescription().toString();
                    break;
                }//end case
                case Constants.FOURTH:
                {
                    goal_Fourth = goalList.get(i).getGoalDescription().toString();
                    break;
                }//end case
                case Constants.FIFTH:
                {
                    goal_Fifth = goalList.get(i).getGoalDescription().toString();
                    break;
                }//end case
                default:
                {
                // There should be no default
                    System.out.println("Error:  Unexpected switch default in Person init_InitializeGoals()");
                }//end case
            }//end switch
        }//end for


//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Test 
// This block tests this method.
// To run the test, remove comments from the code below.
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//    for (int i = 0; i < goalList.size(); i++)
//    {
//        System.out.println("Goal " + i + " description:  " + goalList.get(i).getGoalDescription());
//        System.out.println("Goal " + i + " priority:  " + goalList.get(i).getGoalPriority());
//    }//end for
//
//    System.out.println("goal_First:  " + goal_First);
//    System.out.println("goal_Second:  " + goal_Second);
//    System.out.println("goal_Third:  " + goal_Third);
//    System.out.println("goal_Fourth:  " + goal_Fourth);
//    System.out.println("goal_Fifth:  " + goal_Fifth);
//
//    System.out.println("--------------------------------------");
//    System.out.println("--------------------------------------");
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    }//end init_InitializeGoals()
//------------------------------------------------------------------------------

//==============================================================================
// Method:  init_InitializeIncome()
//------------------------------------------------------------------------------
    private void init_InitializeIncome()
    {

        //------------------------------------------------------------------
        // If the Person income distribution type is "Log normal", distribute
        // the income according to a log normal distribution.
        // The distribution is limited to the maximum income amount.
        //------------------------------------------------------------------

        //------------------------------------------------------------------
        // Note that Repast Simphony does not currently have a lognormal
        // random number generator. Therefore, this process employs the 
        // normal distribution random number generator to generate 
        // lognormal random numbers.
        // The normal and lognormal distributions are closely related. If X
        // is distributed lognormally with parameters mu and sigma, then log(X)
        // is distributed normally with mean mu and standard deviation sigma.
        // For a lognormal distribution with mean m and standard deviation s,
        // the corresponding normal distribution has:
        // mu = log(m**2/(s**2 + m**2)**(1/2))
        // sigma = (log(s**2/(m**2 + 1)))(1/2)
        // So, in this process, we will generate random numbers with these
        // mu and sigma, and then take the natural exponent of the resulting
        // random numbers.
        // Reference:  www.mathworks.com/help/stats/lognrnd.html
        //------------------------------------------------------------------


        if (SimulationUserPanel.getIncomeDistributionType().equals(Constants.LOG_NORMAL_DISTRIBUTION))
        {
            double lognormalMean = SimulationUserPanel.getIncomeLogNormalDistributionMean();
            double lognormalSD = SimulationUserPanel.getIncomeLogNormalDistributionSD();
            double normalMean, normalSD;
            normalMean = Math.log(Math.pow(lognormalMean,2)/Math.sqrt((Math.pow(lognormalSD,2) + Math.pow(lognormalMean,2))));
            normalSD = Math.sqrt(Math.log(1 + (Math.pow(lognormalSD,2)/Math.pow(lognormalMean,2))));

            RandomHelper.createNormal(normalMean,normalSD);
            grossIncome = Math.max(Math.min(Math.exp(RandomHelper.getNormal().nextDouble()), SimulationUserPanel.getMaximumIncome()), 0.0);
            // Set the net income for the initial period.
            currentNetIncome = grossIncome;
         }//end if

        // If the Person income distribution type is "Random Uniform Distribution",
        // distribute the income randomly.
        else
        {
            grossIncome = RandomHelper.nextDoubleFromTo(0, SimulationUserPanel.getMaximumIncome());
            // Set the net income for the initial period.
            currentNetIncome = grossIncome;
         }//end else

    }//end init_InitializeIncome()
//------------------------------------------------------------------------------

//==============================================================================
// Method:  init_InitializeDiseaseStatus()
//------------------------------------------------------------------------------
    private void init_InitializeDiseaseStatus()
    {

        //------------------------------------------------------------------
        // If the Person initial disease status distribution type is "Log normal",
        // distribute the initial disease status according to a log normal distribution.
        // The distribution is limited to the maximum disease status of 10.0.
        //------------------------------------------------------------------
        
        //------------------------------------------------------------------
        // Note that Repast Simphony does not currently have a lognormal
        // random number generator. Therefore, this process employs the 
        // normal distribution random number generator to generate 
        // lognormal random numbers.
        // The normal and lognormal distributions are closely related. If X
        // is distributed lognormally with parameters mu and sigma, then log(X)
        // is distributed normally with mean mu and standard deviation sigma.
        // For a lognormal distribution with mean m and standard deviation s,
        // the corresponding normal distribution has:
        // mu = log(m**2/(s**2 + m**2)**(1/2))
        // sigma = (log(s**2/(m**2 + 1)))(1/2)
        // So, in this process, we will generate random numbers with these
        // mu and sigma, and then take the natural exponent of the resulting
        // random numbers.
        // Reference:  www.mathworks.com/help/stats/lognrnd.html
        //------------------------------------------------------------------


        if (SimulationUserPanel.getInitialDiseaseDistributionType().equals(Constants.LOG_NORMAL_DISTRIBUTION))
        {

            double lognormalMean = SimulationUserPanel.getInitialDiseaseLogNormalDistributionMean();
            double lognormalSD = SimulationUserPanel.getInitialDiseaseLogNormalDistributionSD();
            double normalMean, normalSD;
            normalMean = Math.log(Math.pow(lognormalMean,2)/Math.sqrt((Math.pow(lognormalSD,2) + Math.pow(lognormalMean,2))));
            normalSD = Math.sqrt(Math.log(1 + (Math.pow(lognormalSD,2)/Math.pow(lognormalMean,2))));

            RandomHelper.createNormal(normalMean, normalSD);
            // Set the disease status for the first period
            currentDiseaseStatus = Math.max(Math.min(Math.exp(RandomHelper.getNormal().nextDouble()), 9.99),0.01);
         }//end if

        // If the Person initial disease status distribution type is "Random Uniform Distribution", distribute the 
        // initial disease status randomly.
        else
        {
            currentDiseaseStatus = RandomHelper.nextDoubleFromTo(0.01, 9.99);
         }//end else

    }//end init_InitializeDiseaseStatus()
//------------------------------------------------------------------------------

//==============================================================================


//==============================================================================
// Methods:  "Input" methods for the agent. These are described in the
// detailed description of the model. Each method is preceded by "i#".
//==============================================================================

// None

//==============================================================================


//==============================================================================
// Methods:  "Process" methods for the agent. These are described in the
// detailed description of the model. Each method is preceded by "p#"
// to indicate its number according to the numbering scheme used in the
// detailed description.
//==============================================================================

//==============================================================================
// Method:  p1_PurchasePlan()
// For simulation periods after the first period, if the rule
// “R1. Determine inertia effect” is TRUE, the Person selects the same
// health plan that the Person selected in the prior period.
// Otherwise: The Person employs two sub-processes to determine the plan
// to purchase. The first sub-process (p1a_DecideToPurchasePlan())is to
// determine whether to purchase a plan at all. 
// The second sub-process is to actually purchase a plan.
// For a detailed description of this process, see the detailed design document.
//------------------------------------------------------------------------------
    public void p1_PurchasePlan()
    {

        if (p1A_DecideToPurchaseAPlan() == true)
        {
            currentPlanPurchaseDecision = true;

            tickCount = RunEnvironment.getInstance().getCurrentSchedule().getTickCount();
            
            if ((tickCount >= 1.0) && (priorExchangeParticipationStatus == true) && (r1_DetermineInertiaEffect() == true))
            {
                currentPlanChosen = priorPlanChosen;
            }//end if

            else
            {
                switch(r2_GetFirstGoal())
                {
                    case MaximizeIncome:
                    {
                        r11_SelectLowestCostPlan();
                        currentPlanChosen = currentLowestCostPlan;
                        break;
                    }//end case
                    case IncreaseHealth:
                    {
                        if (r9_DetermineHealthGravity() == true)
                        {
                            r12_SelectRichestPlan();
                            currentAdverseSelection = true;
                            currentPlanChosen = currentRichestPlan;
                        }//end if
                        else
                        {
                            switch(r3_GetSecondGoal())
                            {
                                case MaximizeIncome:
                                {
                                    r11_SelectLowestCostPlan();
                                    currentPlanChosen = currentLowestCostPlan;
                                    break;
                                }//end case
                                case Conform:
                                {
                                    r7_GetFavoredPlan();
                                    if (currentFavoredPlan == PlanTag.None)
                                    {
                                        r10_SelectFirstOfferedPlan();
                                        currentPlanChosen = currentFirstOfferedPlan;
                                        break;
                                    }//end if
                                    else
                                    {
                                        for (int i = 0; i < Environment.getInsurancePlanList().size(); i++)
                                        {
                                            if (Environment.getInsurancePlanList().get(i).getPlanTag() == currentFavoredPlan)
                                            {
                                                currentPlanChosen = Environment.getInsurancePlanList().get(i).getPlanTag();
                                            }//end if
                                        }//end for
                                        break;
                                    }//end else
                                }//end case
                                default:
                                {
                                    r10_SelectFirstOfferedPlan();
                                    currentPlanChosen = currentFirstOfferedPlan;
                                    break;
                                }//end case
    
                            }//end switch
                        }//end else
    
                        break;
                    }//end case
                    case Conform:
                    {
                        r7_GetFavoredPlan();
                        for (int i = 0; i < Environment.getInsurancePlanList().size(); i++)
                        {
                            if (Environment.getInsurancePlanList().get(i).getPlanTag() == currentFavoredPlan)
                            {
                                currentPlanChosen = Environment.getInsurancePlanList().get(i).getPlanTag();
                                break;
                            }//end if
                        }//end for
                        break;
                    }//end case
                    case FollowPolicy:
                    {
                        r10_SelectFirstOfferedPlan();
                        currentPlanChosen = currentFirstOfferedPlan;
                        break;
                    }//end case
                    case FollowAdvertising:
                    {
                        if(r8_DetermineAdImpact() == true)
                        {
                            r10_SelectFirstOfferedPlan();
                            currentPlanChosen = currentFirstOfferedPlan;
                            break;
                        }//end if
                        else
                        {
                            switch(r3_GetSecondGoal())
                            {
                                case MaximizeIncome:
                                {
                                    r11_SelectLowestCostPlan();
                                    currentPlanChosen = currentLowestCostPlan;
                                    break;
                                }//end case
                                case IncreaseHealth:
                                {
                                    if (r9_DetermineHealthGravity() == true)
                                    {
                                        r12_SelectRichestPlan();
                                        currentPlanChosen = currentRichestPlan;
                                        break;
                                    }//end if
                                    else
                                    {
                                        r10_SelectFirstOfferedPlan();
                                        currentPlanChosen = currentFirstOfferedPlan;
                                        break;
                                    }//end else
                                }//end case
                                case Conform:
                                {
                                    r7_GetFavoredPlan();
                                    for (int i = 0; i < Environment.getInsurancePlanList().size(); i++)
                                    {
                                        if (Environment.getInsurancePlanList().get(i).getPlanTag() == currentFavoredPlan)
                                        {
                                            currentPlanChosen = Environment.getInsurancePlanList().get(i).getPlanTag();
                                            break;
                                        }//end if
                                    }//end for
                                    break;
                                }//end case
                                case FollowPolicy:
                                {
                                    r10_SelectFirstOfferedPlan();
                                    currentPlanChosen = currentFirstOfferedPlan;
                                    break;
                                }//end case
                                default:
                                {
                                // There should be no default
                                    System.out.println("Error:  Unexpected switch default in Person p1_PurchasePlan()");
                                }//end case

                            }//end switch
                        }//end else
                        break;
                    }//end case

                    default:
                    {
                    // There should be no default
                        System.out.println("Error:  Unexpected switch default in Person p1_PurchasePlan()");
                    }//end case
                }//end switch
            }//end else

            currentPlanChosenString = currentPlanChosen.toString();
            currentExchangeParticipationStatus = true;
            currentPremiumAmountPaid = getPlan(currentPlanChosen).getCurrentPremiumAmount();
            currentNetIncome = Math.max(grossIncome - currentPremiumAmountPaid, 0.0);

            // Add an edge between the Person and the Health Insurance Company that
            // provides the chosen plan.
//            @SuppressWarnings("unchecked")
//            Network<Object> network = (Network<Object>)simulationContext.getProjection("Person-Insurer network");
//
//            if ((currentPlanChosen == PlanTag.PlanA1) || (currentPlanChosen == PlanTag.PlanA2) && currentAdverseSelection == true)
//            {
//                network.addEdge(this, Environment.getHealthInsuranceCompanyList().get(Constants.HEALTH_INSURANCE_COMPANY_A));
//            }//end if
//
//            if ((currentPlanChosen == PlanTag.PlanB1) || (currentPlanChosen == PlanTag.PlanB2) && currentAdverseSelection == true)
//            {
//                network.addEdge(this, Environment.getHealthInsuranceCompanyList().get(Constants.HEALTH_INSURANCE_COMPANY_B));
//            }//end if


//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Test 
// This block tests this method.
// To run the test, remove comments from the code below.
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//    System.out.println("Person p1_PurchasePlan() at " +
//            RunEnvironment.getInstance().getCurrentSchedule().getTickCount());
//
//    System.out.println("Person ID:  " + getAgentID().getAgentIDString());
//    System.out.println("currentPlanPurchaseDecision:  " + currentPlanPurchaseDecision);
//    System.out.println("currentPlanChosen:  " + currentPlanChosen.toString());
//    System.out.println("Plan ID:  " + getPlan(currentPlanChosen).getAgentName());
//    System.out.println("currentPremiumAmountPaid:  " + currentPremiumAmountPaid);
//    System.out.println("--------------------------------------");
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

        }//end if

    }//end p1_PurchasePlan()
//------------------------------------------------------------------------------

//==============================================================================
// Method:  p1A_DecideToPurchaseAPlan()
//------------------------------------------------------------------------------
    public boolean p1A_DecideToPurchaseAPlan()
    {
        boolean purchaseResult = false;

        switch(r2_GetFirstGoal())
        {
            case MaximizeIncome:
            {
                if(r6_DetermineIfTaxOrPremiumIsGreater().matches("Tax"))
                {
                    purchaseResult = true;
                }//end if
                break;
            }//end case
            case IncreaseHealth:
            {
                purchaseResult = true;
                break;
            }//end case
            case Conform:
            {
                r7_GetFavoredPlan();
                if(currentFavoredPlan != PlanTag.None)
                {
                    purchaseResult = true;
                }//end if
                break;
            }//end case
            case FollowPolicy:
            {
                purchaseResult = true;
                break;
            }//end case
            case FollowAdvertising:
            {
                if(r8_DetermineAdImpact() == true)
                {
                    purchaseResult = true;
                }//end if
                else
                {
                    switch(r3_GetSecondGoal())
                    {
                        case MaximizeIncome:
                        {
                            if(r6_DetermineIfTaxOrPremiumIsGreater().matches("Tax"))
                            {
                                purchaseResult = true;
                            }//end if
                            break;
                        }//end case
                        case IncreaseHealth:
                        {
                            purchaseResult = true;
                            break;
                        }//end case
                        case Conform:
                        {
                            r7_GetFavoredPlan();
                            if(currentFavoredPlan != PlanTag.None)
                            {
                                purchaseResult = true;
                            }//end if
                            break;
                        }//end case
                        case FollowPolicy:
                        {
                            purchaseResult = true;
                            break;
                        }//end case
                        default:
                        {
                        // There should be no default
                            purchaseResult = true;
                            System.out.println("Error:  Unexpected switch default in Person p1A_DecideToPurchaseAPlan()");
                        }//end case
                    }//end switch
                }//end else
                break;
            }//end case
            default:
            {
            // There should be no default
                System.out.println("Error:  Unexpected switch default in Person p1A_DecideToPurchaseAPlan()");
            }//end case
        }//end switch


//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Test 
// This block tests this method.
// To run the test, remove comments from the code below.
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//    System.out.println("Person p1A_DecideToPurchaseAPlan() at " +
//            RunEnvironment.getInstance().getCurrentSchedule().getTickCount());
//
//    System.out.println("Person ID:  " + getAgentID().getAgentIDString());
//    System.out.println("First goal:  " + r2_GetFirstGoal().toString());
//    System.out.println("Penalty tax rate:  " + Environment.getPenaltyTaxAgencyList()
//                .get(0).getCurrentPenaltyTaxRate());
//    System.out.println("grossIncome:  " + grossIncome);
//    System.out.println("currentPenaltyTaxAmount:  " + currentPenaltyTaxAmount);
//    System.out.println("currentLowestPremium:  " + currentLowestPremium);
//    System.out.println("Greater of tax or premium:  " + r6_DetermineIfTaxOrPremiumIsGreater());
//    System.out.println("Favored plan:  " + currentFavoredPlan.toString());
//    System.out.println("Exchange advertising intensity:  " 
//            + Environment.getExchangeList().get(0).getCurrentAdvertisingIntensity());
//    System.out.println("Advertising impact threshold:  " 
//            + SimulationUserPanel.getAdvertisingImpactThreshold());
//    System.out.println("Advertising impact:  " + r8_DetermineAdImpact());
//    System.out.println("--------------------------------------");
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

        if (purchaseResult == true) currentExchangeParticipationStatus = true;
        else currentExchangeParticipationStatus = false;

        return purchaseResult;

    }//end p1A_DecideToPurchaseAPlan()
//------------------------------------------------------------------------------


//==============================================================================
// Method:  p2_PayPenaltyTax()
// If a Person does not purchase health insurance, the Person pays a penalty
// tax amount obtained by rule “R5. Calculate penalty tax”
//------------------------------------------------------------------------------
    public void p2_PayPenaltyTax()
    {
        if (currentExchangeParticipationStatus == false)
        {
            r5_CalculatePenaltyTaxAmount();
            currentPenaltyTaxAmountPaid = currentPenaltyTaxAmount;
            currentNetIncome = Math.max(currentNetIncome - currentPenaltyTaxAmountPaid, 0.0);
        }//end if

    }//end p2_PayPenaltyTax()
//------------------------------------------------------------------------------

//==============================================================================
// Method:  p3_RequestTreatment()
// If the Person has purchased health insurance, and the Person’s disease status
// is grave (as determined by the rule “R9. Determine health gravity”) the person
// requests (and receives) treatment from the Provider Network.
// If the Person requests treatment, set “Current treatment request” to “TRUE”.
// Otherwise the result is “FALSE”.
//------------------------------------------------------------------------------
    public void p3_RequestTreatment()
    {

        if (currentExchangeParticipationStatus == true && 
                r9_DetermineHealthGravity() == true)
        {
            currentTreatmentRequest = true;
        }//end if
        else currentTreatmentRequest = false;

    }//end p3_RequestTreatment()
//------------------------------------------------------------------------------

//==============================================================================
// Method:  p4_PayCopayment()
// If the Person requests treatment and has a health insurance plan that
// requires co-payment, the person pays the co-payment amount.
// (determined by rule “R13. Calculate co-payment”).
//------------------------------------------------------------------------------
    public void p4_PayCopayment()
    {

        if (currentTreatmentRequest == true)
        {
            r13_CalculateCopayment();
            currentNetIncome = Math.max(currentNetIncome - currentCopaymentAmountPaid, 0.0);
        }

    }//end p4_PayCopayment()
//------------------------------------------------------------------------------

//==============================================================================
// Method:  p5_UpdateDiseaseStatus()
// If the Person was not treated in the current period, increase the Person’s
// disease status by the “Health deterioration percentage”.
// If the Person was treated in the current period, decrease the Person’s disease
// status by the “Health improvement percentage”.
// The Person’s disease status is also increased by the “Disease status random
// increment”, which is a random increment recalculated each period. 
//------------------------------------------------------------------------------
    public void p5_UpdateDiseaseStatus()
    {

        //------------------------------------------------------------------
        // If the distribution type of the Person's initial disease status
        // random increment is "Log normal", distribute it according to a
        // log normal distribution.
        // The distribution is limited to the maximum increment.
        //------------------------------------------------------------------
        
        //------------------------------------------------------------------
        // Note that Repast Simphony does not currently have a lognormal
        // random number generator. Therefore, this process employs the 
        // normal distribution random number generator to generate 
        // lognormal random numbers.
        // The normal and lognormal distributions are closely related. If X
        // is distributed lognormally with parameters mu and sigma, then log(X)
        // is distributed normally with mean mu and standard deviation sigma.
        // For a lognormal distribution with mean m and standard deviation s,
        // the corresponding normal distribution has:
        // mu = log(m**2/(s**2 + m**2)**(1/2)
        // sigma = (log(s**2/(m**2 + 1)))(1/2)
        // So, in this process, we will generate random numbers with these
        // mu and sigma, and then take the natural exponent of the resulting
        // random numbers.
        // Reference:  www.mathworks.com/help/stats/lognrnd.html
        //------------------------------------------------------------------

        double lognormalMean = 0.0;
        double lognormalSD = 0.0;

        double maximumIncrement = SimulationUserPanel.getIncrementalDiseaseMaximum();
        if (SimulationUserPanel.getIncrementalDiseaseDistributionType().equals(Constants.LOG_NORMAL_DISTRIBUTION))
        {
            lognormalMean = SimulationUserPanel.getIncrementalDiseaseLogNormalDistributionMean();
            lognormalSD = SimulationUserPanel.getIncrementalDiseaseLogNormalDistributionSD();
            double normalMean, normalSD;
            normalMean = Math.log(Math.pow(lognormalMean,2)/Math.sqrt((Math.pow(lognormalSD,2) + Math.pow(lognormalMean,2))));
            normalSD = Math.sqrt(Math.log(1 + (Math.pow(lognormalSD,2)/Math.pow(lognormalMean,2))));

            RandomHelper.createNormal(normalMean, normalSD);
            nextCurrentDiseaseStatusRandomIncrement = Math.max(Math.min(Math.exp(RandomHelper.getNormal().nextDouble()),
                    maximumIncrement),0.00);
         }//end if

        // If the Person initial disease status distribution type is "Random Uniform Distribution", distribute the 
        // initial disease status randomly.
        else
        {
            nextCurrentDiseaseStatusRandomIncrement = RandomHelper.nextDoubleFromTo(0.00, maximumIncrement);
         }//end else

        double healthDeteriorationPercentage = SimulationUserPanel.getHealthDeteriorationPercentage();
        double healthImprovementPercentage = SimulationUserPanel.getHealthImprovementPercentage();

        if (currentTreatmentRequest == true)
        {
            nextCurrentDiseaseStatus = currentDiseaseStatus/(1 + healthImprovementPercentage) + nextCurrentDiseaseStatusRandomIncrement;
        }//end if
        else nextCurrentDiseaseStatus = currentDiseaseStatus * (1 + healthDeteriorationPercentage) + nextCurrentDiseaseStatusRandomIncrement;

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Test 
// This block tests this method.
// To run the test, remove comments from the code below.
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//    System.out.println("Person p5_UpdateDiseaseStatus() at " +
//            RunEnvironment.getInstance().getCurrentSchedule().getTickCount());
//
//    System.out.println("Person ID:  " + getAgentID().getAgentIDString());
//
//    System.out.println("lognormalMean:  " + lognormalMean);
//    System.out.println("lognormalSD:  " + lognormalSD);
//    System.out.println("nextCurrentDiseaseStatusRandomIncrement:  " + nextCurrentDiseaseStatusRandomIncrement);
//
//    System.out.println("currentTreatmentRequest:  " + currentTreatmentRequest);
//    System.out.println("currentDiseaseStatus:  " + currentDiseaseStatus);
//    System.out.println("healthImprovementPercentage:  " + healthImprovementPercentage);
//    System.out.println("healthDeteriorationPercentage:  " + healthDeteriorationPercentage);
//    System.out.println("nextCurrentDiseaseStatus:  " + nextCurrentDiseaseStatus);
//
//    System.out.println("--------------------------------------");
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


    }//end p5_UpdateDiseaseStatus()
//------------------------------------------------------------------------------


//==============================================================================


//==============================================================================
// Methods:  "Rule" methods for the agent. These are described in the
// detailed description of the model. Each method is preceded by "r#"
// to indicate its number according to the numbering scheme used in the
// detailed description.
//==============================================================================

//==============================================================================
// Method:  r1_DetermineInertiaEffect()
// If a random number between 0 and 1 is less than the “Decision inertia percentage”
// (a parameter), return “TRUE”. Otherwise return “FALSE”. A result of “TRUE”
// means that the Person is under the influence of inertia, and will select the
// same health plan that the Person selected in the prior period.
//------------------------------------------------------------------------------
    private boolean r1_DetermineInertiaEffect()
    {

        if (RandomHelper.nextDoubleFromTo(0, 1.00) < SimulationUserPanel.getDecisionInertiaPercentage())
        {
            currentInertiaEffect = true;
            return true;
        }//end if

        else
        {
            currentInertiaEffect = false;
            return false;
        }//end else

    }//end r1_DetermineInertiaEffect()
//------------------------------------------------------------------------------

//==============================================================================
// Method:  r2_GetFirstGoal()
// Determine the Person’s highest-priority goal.
//------------------------------------------------------------------------------
    private GoalDescription r2_GetFirstGoal()
    {
        GoalDescription firstGoal = GoalDescription.None;
        for (int i = 0; i < goalList.size(); i++)
        {
            if(goalList.get(i).getGoalPriority() == Constants.FIRST) firstGoal = goalList.get(i).getGoalDescription();
        }//end for

        return firstGoal;

    }//end r2_GetFirstGoal()
//------------------------------------------------------------------------------

//==============================================================================
// Method:  r3_GetSecondGoal()
// Determine the Person’s second-priority goal.
//------------------------------------------------------------------------------
    private GoalDescription r3_GetSecondGoal()
    {
        GoalDescription secondGoal = GoalDescription.None;
        for (int i = 0; i < goalList.size(); i++)
        {
            if(goalList.get(i).getGoalPriority() == Constants.SECOND)
                secondGoal = goalList.get(i).getGoalDescription();
        }//end for

        return secondGoal;

    }//end r3_GetSecondGoal()
//------------------------------------------------------------------------------

//==============================================================================
// Method:  r4_GetLastGoal()
// Determine the Person’s last-priority goal.
//------------------------------------------------------------------------------
    private GoalDescription r4_GetLastGoal()
    {
        GoalDescription lastGoal = GoalDescription.None;
        for (int i = 0; i < goalList.size(); i++)
        {
            if(goalList.get(i).getGoalPriority() == Constants.FIFTH)
                lastGoal = goalList.get(i).getGoalDescription();
        }//end for

        return lastGoal;

    }//end r4_GetLastGoal()
//------------------------------------------------------------------------------

//==============================================================================
// Method:  r5_CalculatePenaltyTaxAmount()
// Calculate the potential current penalty tax amount by multiplying the
// “Penalty tax level” (obtained from the Penalty Tax Agency agent) times the
// Person’s “Current income” (an attribute).
//------------------------------------------------------------------------------
    private void r5_CalculatePenaltyTaxAmount()
    {

        currentPenaltyTaxAmount = Environment.getPenaltyTaxAgencyList().get(0).getCurrentPenaltyTaxRate() * grossIncome;

    }//end r5_CalculatePenaltyTaxAmount()
//------------------------------------------------------------------------------

//==============================================================================
// Method:  r6_DetermineIfTaxOrPremiumIsGreater()
// Determine which is greater, the penalty tax amount or the premium for the
// lowest-cost health plan. If the tax is greater, the result is “Tax”.
// If the premium is greater, the result is “Premium”.
//------------------------------------------------------------------------------
    private String r6_DetermineIfTaxOrPremiumIsGreater()
    {

        currentLowestPremium = Environment.getInsurancePlanList().get(0).getCurrentPremiumAmount();

        for (int i = 1; i < Environment.getInsurancePlanList().size(); i++)
        {
            if (currentLowestPremium > Environment.getInsurancePlanList().get(i).getCurrentPremiumAmount())
            {
                currentLowestPremium = Environment.getInsurancePlanList().get(i).getCurrentPremiumAmount();
            }//end if 
        }//end for

        r5_CalculatePenaltyTaxAmount();
        if (currentPenaltyTaxAmount < currentLowestPremium)
        return "Premium";

        else return "Tax";

    }//end r6_DetermineIfTaxOrPremiumIsGreater()
//------------------------------------------------------------------------------

//==============================================================================
// Method:  r7_GetFavoredPlan()
// Get the most frequent plan choice among all Person agents within a certain
// geographic radius of the Person making the request. The “neighborhood radius”
// is a user-entered parameter. If most Person agent neighbors choose to remain
// uninsured, the result is “None”. Otherwise, return the plan's ID in string
// format.
//------------------------------------------------------------------------------
    private void r7_GetFavoredPlan()
    {
        PlanTag result = PlanTag.None;
        int priorNumberOfUninsuredNeighbors = 0;
        ArrayList<FavoredPlan> favoredPlanList = new ArrayList<FavoredPlan>();

        // Locate Person agents within the neighborhood radius, put
        // the plan they decided to purchase in the prior period (if any) in the
        // favored plan list, provide the appropriate counts, and return
        // the most favored decision.
        ListIterator<Person> iterator1 = Environment.getPersonList().listIterator();
         
        while (iterator1.hasNext())
        {
            Person person = iterator1.next();
            double distance = distance(getAgentXLocation(), getAgentYLocation(),
                person.getAgentXLocation(), person.getAgentYLocation());
            
            // Only consider people within the neighborhood radius.
            if (distance <= SimulationUserPanel.getNeighborhoodRadius())
            {
                // If person was uninsured in the prior period,
                // add one to the count of uninsured neighbors.
                if (person.isPriorExchangeParticipationStatus() == false)
                {
                    priorNumberOfUninsuredNeighbors++;
                }//end if
                
                // Otherwise, determine the most common plan chosen.
                // Note that this person's own previous plan choice may be considered.
                else
                {
                    PlanTag previousPeriodPlanChosen = person.getPriorPlanChosen();
                    
                    // Determine if the chosen plan is already in the favored plan list.
                    // If so, increment the count.
                    FavoredPlan favoredPlan1 = new FavoredPlan(previousPeriodPlanChosen, 1);
                    
                    boolean inFavoredPlanList = false;
                    for (int i = 0; i < favoredPlanList.size(); i++)
                    {
                        FavoredPlan favoredPlan2 = favoredPlanList.get(i);
                        
                        if(favoredPlan1.getFavoredPlanTag() == favoredPlan2.getFavoredPlanTag())
                        {
                            inFavoredPlanList = true;
                            int count = favoredPlan2.getFavoredPlanCount();
                            favoredPlanList.get(i).setFavoredPlanCount(count + 1);
                        }//end if
                    }//end for
                    
                    // If the Plan is not in the list, add it.
                    if (!inFavoredPlanList) favoredPlanList.add(favoredPlan1);

                    PlanTag favoredPlanTag = PlanTag.None;
                    int highestPlanCount = 0;
                    
                    // Determine the PCP with the highest count, and return it.
                    ListIterator<FavoredPlan> iterator3 = favoredPlanList.listIterator();

                    while (iterator3.hasNext())
                    {
                        FavoredPlan favoredPlan3 = iterator3.next();
                        
                        if(favoredPlan3.getFavoredPlanCount() > highestPlanCount)
                        {
                            highestPlanCount = favoredPlan3.getFavoredPlanCount();
                            favoredPlanTag = favoredPlan3.getFavoredPlanTag();
                        }//end if
                    }//end while

                    // Return the favored plan
                    // If there is no favored plan, or if the favored choice is
                    // to be uninsured, return "None"
                    if (favoredPlanList.size() == 0 ||
                            priorNumberOfUninsuredNeighbors > highestPlanCount)
                    {
                        result = PlanTag.None;
                    }//end if
                    else result = favoredPlanTag;
                    
                }//end else
            }//end if
        }//end while
        
        currentFavoredPlan = result;
        currentFavoredPlanString = currentFavoredPlan.toString();

    }//end r7_GetFavoredPlan()
//------------------------------------------------------------------------------

//==============================================================================
// Method:  r8_DetermineAdImpact()
// Determine if the Exchange advertising intensity is greater than the
// “advertising impact threshold”. If so, then the advertising has an impact
// (“TRUE”) Otherwise: “FALSE”.
//------------------------------------------------------------------------------
    private boolean r8_DetermineAdImpact()
    {
        if (Environment.getExchangeList().get(0).getCurrentAdvertisingIntensity() > SimulationUserPanel.getAdvertisingImpactThreshold())
        {
            currentAdvertisingImpact = true;
            return true;
        }//end if 
        else
        {
            currentAdvertisingImpact = false;
            return false;
        }//end else

    }//end r8_DetermineAdImpact()
//------------------------------------------------------------------------------

//==============================================================================
// Method:  r9_DetermineHealthGravity()
// If the Person’s disease status is more than the “health gravity threshold”,
// it is grave (“TRUE”). Otherwise the result is “FALSE”.
//------------------------------------------------------------------------------
    private boolean r9_DetermineHealthGravity()
    {

        if (currentDiseaseStatus > SimulationUserPanel.getHealthGravityThreshold())
        {
            currentHealthGravity = true;
            return true;
        }//end if

        else
        {
            currentHealthGravity = false;
            return false;
        }//end else 

    }//end r9_DetermineHealthGravity()
//------------------------------------------------------------------------------

//==============================================================================
// Method:  r10_SelectFirstOfferedPlan()
// Select the first plan offered on the Exchange's list of plans.
//------------------------------------------------------------------------------
    private void r10_SelectFirstOfferedPlan()
    {

        for (int i = 0; i < Environment.getInsurancePlanList().size(); i++)
        {
            InsurancePlan insurancePlan = Environment.getInsurancePlanList().get(i);
            if (insurancePlan.getCurrentExchangePresentationOrder() == Constants.FIRST)
            {
                currentFirstOfferedPlan = insurancePlan.getPlanTag();
                currentFirstOfferedPlanString = currentFirstOfferedPlan.toString();
            }
        }//end for

    }//end r10_SelectFirstOfferedPlan()
//------------------------------------------------------------------------------

//==============================================================================
// Method:  r11_SelectLowestCostPlan()
// Select the plan on the Exchange with the lowest premium.
//------------------------------------------------------------------------------
    private void r11_SelectLowestCostPlan()
    {
        InsurancePlan insurancePlan = Environment.getInsurancePlanList().get(Constants.PLAN_A1);
        double lowestPremium = insurancePlan.getCurrentPremiumAmount();
        currentLowestCostPlan = insurancePlan.getPlanTag();

        // Compare the other plans to Plan A1, to find the one with the lowest premium.
        for (int i = 0; i < Environment.getInsurancePlanList().size(); i++)
        {
            InsurancePlan insurancePlan1= Environment.getInsurancePlanList().get(i);
            if (insurancePlan1.getCurrentPremiumAmount() < lowestPremium)
            {
                currentLowestCostPlan = insurancePlan1.getPlanTag();
                currentLowestCostPlanString = currentLowestCostPlan.toString();
                lowestPremium = insurancePlan1.getCurrentPremiumAmount();
            }//end if
        }//end for

    }//end r11_SelectLowestCostPlan()
//------------------------------------------------------------------------------

//==============================================================================
// Method:  r12_SelectRichestPlan()
// Select the lowest cost plan on the Exchange that has no co-payment.
//------------------------------------------------------------------------------
    private void r12_SelectRichestPlan()
    {
        // Only plans A1 and B1 have no co-payment.
        InsurancePlan insurancePlanA1 = Environment.getInsurancePlanList().get(Constants.PLAN_A1);
        InsurancePlan insurancePlanB1 = Environment.getInsurancePlanList().get(Constants.PLAN_B1);

        if (insurancePlanA1.getCurrentPremiumAmount() < insurancePlanB1.getCurrentPremiumAmount())
        {
            currentRichestPlan = insurancePlanA1.getPlanTag();
            currentRichestPlanString = currentRichestPlan.toString();
        }//end if   
        else
        {
            currentRichestPlan = insurancePlanB1.getPlanTag();
            currentRichestPlanString = currentRichestPlan.toString();
        }//end else

    }//end r12_SelectRichestPlan()
//------------------------------------------------------------------------------

//==============================================================================
// Method:  r13_CalculateCopayment()
// For plans that require a co-payment, calculate the amount of a Person’s
// co-payment, equal to the plan’s co-payment percentage times alpha*(DS^2), where DS
// is a Person’s disease status and  alpha is the “treatment cost alpha” that for
// the simulation’s first period is entered by the user, and that thereafter
// is negotiated between each Health Insurance Company and its Provider Network.
//------------------------------------------------------------------------------
    private void r13_CalculateCopayment()
    {
        double copaymentPercentage = getPlan(currentPlanChosen).getCopaymentPercentage();
        double alpha = 0.0;

        switch (currentPlanChosen)
        {
            case PlanA1: case PlanA2:
            {
                alpha = Environment.getHealthInsuranceCompanyList().get(Constants.HEALTH_INSURANCE_COMPANY_A).getCurrentTreatmentCostAlpha();
                break;
            }//end case

            case PlanB1: case PlanB2:
            {
                alpha = Environment.getHealthInsuranceCompanyList().get(Constants.HEALTH_INSURANCE_COMPANY_B).getCurrentTreatmentCostAlpha();
                break;
            }//end case

            default:
            {
            // There should be no default
                System.out.println("Error:  Unexpected switch default in Person r13_CalculateCopayment()");

            }//end case

        }//end switch

        currentCopaymentAmountPaid = copaymentPercentage * alpha * Math.pow(currentDiseaseStatus, 2);

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Test 
// This block tests this method.
// To run the test, remove comments from the code below.
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//    System.out.println("Person r13_CalculateCopayment() at " +
//            RunEnvironment.getInstance().getCurrentSchedule().getTickCount());
//
//    System.out.println("Person ID:  " + getAgentID().getAgentIDString());
//    System.out.println("currentPlanChosen:  " + currentPlanChosen.toString());
//    System.out.println("copaymentPercentage:  " + copaymentPercentage);
//    System.out.println("--------------------------------------");
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    }//end r13_CalculateCopayment()
//------------------------------------------------------------------------------


//==============================================================================


//==============================================================================
// Methods:  General methods for the agent.
//==============================================================================

    private InsurancePlan getPlan(PlanTag pPlanTag)
    {
        InsurancePlan insurancePlanResult = null;
        for (int i = 0; i < Environment.getInsurancePlanList().size(); i++)
        {
            if (Environment.getInsurancePlanList().get(i).getPlanTag() == pPlanTag)
            {
                insurancePlanResult = Environment.getInsurancePlanList().get(i);
            }//end if
        }//end for

        return insurancePlanResult;

    }//end getPlan()

    private double truncate(double d, int decimalPlace)
    {
        BigDecimal bd = new BigDecimal(d);
        bd = bd.setScale(decimalPlace, BigDecimal.ROUND_FLOOR);
        return bd.doubleValue();
    }//end truncate()


//==============================================================================


//==============================================================================
// Methods: "Probe" methods for the model user to probe the agent's data.
// Note:    As a simulation runs, the model user can probe an agent by
//          double-clicking on its icon in the display. For more information
//          about probes, see the Repast Simphony reference documentation.
//==============================================================================        
     // The "ProbeID" annotation provides the probe ID
     @ProbeID
     public String getPersonIDString()
     {
         return (getAgentID().getAgentIDString());
     }//end getPersonIDString()


    @Parameter (usageName="currentDiseaseStatus", displayName="A1. Current disease status")
     public double getCurrentDiseaseStatus()
     {
         return currentDiseaseStatus;
     }

    @Parameter (usageName="grossIncome", displayName="B1. Current gross income")
     public double getGrossIncome()
     {
         return grossIncome;
     }

    @Parameter (usageName="currentNetIncome", displayName="B2. Current net income")
    public double getCurrentNetIncome()
    {
        return currentNetIncome;
    }

    @Parameter (usageName="goal_First", displayName="C1. First goal")
    public String getGoal_First()
    {
        return goal_First;
    }

    @Parameter (usageName="goal_Second", displayName="C2. Second goal")
    public String getGoal_Second()
    {
        return goal_Second;
    }

    @Parameter (usageName="goal_Third", displayName="C3. Third goal")
    public String getGoal_Third()
    {
        return goal_Third;
    }

    @Parameter (usageName="goal_Fourth", displayName="C4. Fourth goal")
    public String getGoal_Fourth()
    {
        return goal_Fourth;
    }

    @Parameter (usageName="goal_Fifth", displayName="C5. Fifth goal")
    public String getGoal_Fifth()
    {
        return goal_Fifth;
    }

    @Parameter (usageName="currentPenaltyTaxAmount", displayName="D1. Current penalty tax amount")
    public double getCurrentPenaltyTaxAmount()
    {
        return currentPenaltyTaxAmount;
    }

    @Parameter (usageName="currentLowestPremium", displayName="D2. Current lowest premium")
    public double getCurrentLowestPremium()
    {
        return currentLowestPremium;
    }

    @Parameter (usageName="currentAdvertisingImpact", displayName="D3. Current advertising impact")
    public boolean isCurrentAdvertisingImpact()
    {
        return currentAdvertisingImpact;
    }

    @Parameter (usageName="currentPlanPurchaseDecision", displayName="D4. Current plan purchase decision")
    public boolean isCurrentPlanPurchaseDecision()
    {
        return currentPlanPurchaseDecision;
    }

    @Parameter (usageName="currentInertiaEffect", displayName="E1. Current inertia effect")
    public boolean isCurrentInertiaEffect()
    {
        return currentInertiaEffect;
    }

    @Parameter (usageName="currentHealthGravity", displayName="E2. Current health gravity")
    public boolean isCurrentHealthGravity()
    {
        return currentHealthGravity;
    }

    @Parameter (usageName="currentAdverseSelection", displayName="E3. Current adverse selection")
    public boolean isCurrentAdverseSelection()
    {
        return currentAdverseSelection;
    }

    @Parameter (usageName="currentExchangeParticipationStatus", displayName="E4. Current Exchange participation status")
     public boolean isCurrentExchangeParticipationStatus()
     {
         return currentExchangeParticipationStatus;
     }

    @Parameter (usageName="currentPlanChosenString", displayName="E5. Current plan chosen")
    public String getCurrentPlanChosenString()
    {
        return currentPlanChosenString;
    }

    @Parameter (usageName="priorPlanChosenString", displayName="E6. Prior plan chosen")
    public String getPriorPlanChosenString()
    {
        return priorPlanChosenString;
    }

    @Parameter (usageName="currentPremiumAmountPaid", displayName="E7. Current premium amount paid")
    public double getCurrentPremiumAmountPaid()
    {
        return currentPremiumAmountPaid;
    }

    @Parameter (usageName="currentCopaymentAmountPaid", displayName="E7. Current copayment amount paid")
    public double getCurrentCopaymentAmountPaid()
    {
        return currentCopaymentAmountPaid;
    }

    @Parameter (usageName="currentFirstOfferedPlanString", displayName="F1. Current first offered plan")
    public String getCurrentFirstOfferedPlanString()
    {
        return currentFirstOfferedPlanString;
    }

    @Parameter (usageName="currentLowestCostPlanString", displayName="F2. Current lowest cost plan")
    public String getCurrentLowestCostPlanString()
    {
        return currentLowestCostPlanString;
    }

    @Parameter (usageName="currentRichestPlanString", displayName="F3. Current richest plan")
    public String getCurrentRichestPlanString()
    {
        return currentRichestPlanString;
    }

    @Parameter (usageName="currentFavoredPlanString", displayName="F4. Current favored plan")
    public String getCurrentFavoredPlanString()
    {
        return currentFavoredPlanString;
    }

    @Parameter (usageName="currentTreatmentRequest", displayName="G1. Current treatment request")
    public boolean isCurrentTreatmentRequest()
    {
        return currentTreatmentRequest;
    }

    @Parameter (usageName="nextCurrentDiseaseStatusRandomIncrement", displayName="G2. Next current disease status random increment")
    public double getCurrentDiseaseStatusRandomIncrement()
    {
        return nextCurrentDiseaseStatusRandomIncrement;
    }

    @Parameter (usageName="currentPenaltyTaxAmountPaid", displayName="G3. Current penalty tax amount paid")
    public double getCurrentPenaltyTaxAmountPaid()
    {
        return currentPenaltyTaxAmountPaid;
    }

//==============================================================================


//==============================================================================
// Methods:  "Analysis" methods to support simulation charts and graphs.
//==============================================================================


     public boolean analysisLapseStatus_PlanA1()
     {
         if ((getPenultimatePlanChosen() == PlanTag.PlanA1) && (getPriorPlanChosen() != getPenultimatePlanChosen())) return true;
         else return false;
     }

     public boolean analysisLapseStatus_PlanA2()
     {
         if ((getPenultimatePlanChosen() == PlanTag.PlanA2) && (getPriorPlanChosen() != getPenultimatePlanChosen())) return true;
         else return false;
     }

     public boolean analysisLapseStatus_PlanB1()
     {
         if ((getPenultimatePlanChosen() == PlanTag.PlanB1) && (getPriorPlanChosen() != getPenultimatePlanChosen())) return true;
         else return false;
     }

     public boolean analysisLapseStatus_PlanB2()
     {
         if ((getPenultimatePlanChosen() == PlanTag.PlanB2) && (getPriorPlanChosen() != getPenultimatePlanChosen())) return true;
         else return false;
     }


     public boolean analysisIsFirstGoal_MaximizeIncome()
     {

         boolean result = false;

          for (int i = 0; i < goalList.size(); i++)
         {
             if (goalList.get(i).getGoalPriority() == Constants.FIRST)
             {
                 if(goalList.get(i).getGoalDescription() == GoalDescription.MaximizeIncome) result = true;
             }//end if
         }//end for
         
         return result;
         
     }

     public boolean analysisIsFirstGoal_IncreaseHealth()
     {

         boolean result = false;

          for (int i = 0; i < goalList.size(); i++)
         {
             if (goalList.get(i).getGoalPriority() == Constants.FIRST)
             {
                 if(goalList.get(i).getGoalDescription() == GoalDescription.IncreaseHealth) result = true;
             }//end if
         }//end for
         
         return result;
         
     }

     public boolean analysisIsFirstGoal_Conform()
     {

         boolean result = false;

          for (int i = 0; i < goalList.size(); i++)
         {
             if (goalList.get(i).getGoalPriority() == Constants.FIRST)
             {
                 if(goalList.get(i).getGoalDescription() == GoalDescription.Conform) result = true;
             }//end if
         }//end for
         
         return result;
         
     }

     public boolean analysisIsFirstGoal_FollowPolicy()
     {

         boolean result = false;

          for (int i = 0; i < goalList.size(); i++)
         {
             if (goalList.get(i).getGoalPriority() == Constants.FIRST)
             {
                 if(goalList.get(i).getGoalDescription() == GoalDescription.FollowPolicy) result = true;
             }//end if
         }//end for
         
         return result;
         
     }

     public boolean analysisIsFirstGoal_FollowAdvertising()
     {

         boolean result = false;

          for (int i = 0; i < goalList.size(); i++)
         {
             if (goalList.get(i).getGoalPriority() == Constants.FIRST)
             {
                 if(goalList.get(i).getGoalDescription() == GoalDescription.FollowAdvertising) result = true;
             }//end if
         }//end for
         
         return result;
         
     }

     public boolean analysisIsSecondGoal_MaximizeIncome()
     {

         boolean result = false;

          for (int i = 0; i < goalList.size(); i++)
         {
             if (goalList.get(i).getGoalPriority() == Constants.SECOND)
             {
                 if(goalList.get(i).getGoalDescription() == GoalDescription.MaximizeIncome) result = true;
             }//end if
         }//end for
         
         return result;
         
     }

     public boolean analysisIsSecondGoal_IncreaseHealth()
     {

         boolean result = false;

          for (int i = 0; i < goalList.size(); i++)
         {
             if (goalList.get(i).getGoalPriority() == Constants.SECOND)
             {
                 if(goalList.get(i).getGoalDescription() == GoalDescription.IncreaseHealth) result = true;
             }//end if
         }//end for
         
         return result;
         
     }

     public boolean analysisIsSecondGoal_Conform()
     {

         boolean result = false;

          for (int i = 0; i < goalList.size(); i++)
         {
             if (goalList.get(i).getGoalPriority() == Constants.SECOND)
             {
                 if(goalList.get(i).getGoalDescription() == GoalDescription.Conform) result = true;
             }//end if
         }//end for
         
         return result;
         
     }

     public boolean analysisIsSecondGoal_FollowPolicy()
     {

         boolean result = false;

          for (int i = 0; i < goalList.size(); i++)
         {
             if (goalList.get(i).getGoalPriority() == Constants.SECOND)
             {
                 if(goalList.get(i).getGoalDescription() == GoalDescription.FollowPolicy) result = true;
             }//end if
         }//end for
         
         return result;
         
     }

     public boolean analysisIsSecondGoal_FollowAdvertising()
     {

         boolean result = false;

          for (int i = 0; i < goalList.size(); i++)
         {
             if (goalList.get(i).getGoalPriority() == Constants.SECOND)
             {
                 if(goalList.get(i).getGoalDescription() == GoalDescription.FollowAdvertising) result = true;
             }//end if
         }//end for
         
         return result;
         
     }

     public boolean analysisIsLastGoal_MaximizeIncome()
     {

         boolean result = false;

          for (int i = 0; i < goalList.size(); i++)
         {
             if (goalList.get(i).getGoalPriority() == Constants.FIFTH)
             {
                 if(goalList.get(i).getGoalDescription() == GoalDescription.MaximizeIncome) result = true;
             }//end if
         }//end for
         
         return result;
         
     }

     public boolean analysisIsLastGoal_IncreaseHealth()
     {

         boolean result = false;

          for (int i = 0; i < goalList.size(); i++)
         {
             if (goalList.get(i).getGoalPriority() == Constants.FIFTH)
             {
                 if(goalList.get(i).getGoalDescription() == GoalDescription.IncreaseHealth) result = true;
             }//end if
         }//end for
         
         return result;
         
     }

     public boolean analysisIsLastGoal_Conform()
     {

         boolean result = false;

          for (int i = 0; i < goalList.size(); i++)
         {
             if (goalList.get(i).getGoalPriority() == Constants.FIFTH)
             {
                 if(goalList.get(i).getGoalDescription() == GoalDescription.Conform) result = true;
             }//end if
         }//end for
         
         return result;
         
     }

     public boolean analysisIsLastGoal_FollowPolicy()
     {

         boolean result = false;

          for (int i = 0; i < goalList.size(); i++)
         {
             if (goalList.get(i).getGoalPriority() == Constants.FIFTH)
             {
                 if(goalList.get(i).getGoalDescription() == GoalDescription.FollowPolicy) result = true;
             }//end if
         }//end for
         
         return result;
         
     }

     public boolean analysisIsLastGoal_FollowAdvertising()
     {

         boolean result = false;

          for (int i = 0; i < goalList.size(); i++)
         {
             if (goalList.get(i).getGoalPriority() == Constants.FIFTH)
             {
                 if(goalList.get(i).getGoalDescription() == GoalDescription.FollowAdvertising) result = true;
             }//end if
         }//end for
         
         return result;
     }

     public boolean analysisIsPlan_None()
     {
         if (getCurrentPlanChosen() == PlanTag.None) return true;
         else return false;
     }

     public boolean analysisIsPlan_PlanA1()
     {
         if (getCurrentPlanChosen() == PlanTag.PlanA1) return true;
         else return false;
     }

     public boolean analysisIsPlan_PlanA2()
     {
         if (getCurrentPlanChosen() == PlanTag.PlanA2) return true;
         else return false;
     }

     public boolean analysisIsPlan_PlanB1()
     {
         if (getCurrentPlanChosen() == PlanTag.PlanB1) return true;
         else return false;
     }

     public boolean analysisIsPlan_PlanB2()
     {
         if (getCurrentPlanChosen() == PlanTag.PlanB2) return true;
         else return false;
     }

    public int analysisGetPersonCount_PlanA1()
    {
        int personCount = 0;
        
        ListIterator<Person> iterator = Environment.getPersonList().listIterator();
         
        while (iterator.hasNext())
        {
            Person person = iterator.next();
            if (person.getCurrentPlanChosen() == PlanTag.PlanA1)
                personCount ++;
        }//end while
                
        return personCount;

    }//end analysisGetPersonCount_PlanA1()

     public double analysisGetDiseaseStatus_Average_PlanA1()
     {

         if (analysisGetPersonCount_PlanA1() ==0)
         {
             return 0.0;
         }//end if
         else
         {
             if (getCurrentPlanChosen() == PlanTag.PlanA1)
             {
                 return(getCurrentDiseaseStatus()/analysisGetPersonCount_PlanA1());
             }//end if
             else return 0.0;
         }//end else

     }//end analysisGetDiseaseStatus_Average_PlanA1()


    public int analysisGetPersonCount_PlanA2()
    {
        int personCount = 0;
        
        ListIterator<Person> iterator = Environment.getPersonList().listIterator();
         
        while (iterator.hasNext())
        {
            Person person = iterator.next();
            if (person.getCurrentPlanChosen() == PlanTag.PlanA2)
                personCount ++;
        }//end while
                
        return personCount;

    }//end analysisGetPersonCount_PlanA2()

     public double analysisGetDiseaseStatus_Average_PlanA2()
     {

         if (analysisGetPersonCount_PlanA2() ==0)
         {
             return 0.0;
         }//end if
         else
         {
             if (getCurrentPlanChosen() == PlanTag.PlanA2)
             {
                 return(getCurrentDiseaseStatus()/analysisGetPersonCount_PlanA2());
             }//end if
             else return 0.0;
         }//end else

     }//end analysisGetDiseaseStatus_Average_PlanA2()


    public int analysisGetPersonCount_PlanB1()
    {
        int personCount = 0;
        
        ListIterator<Person> iterator = Environment.getPersonList().listIterator();
         
        while (iterator.hasNext())
        {
            Person person = iterator.next();
            if (person.getCurrentPlanChosen() == PlanTag.PlanB1)
                personCount ++;
        }//end while
                
        return personCount;

    }//end analysisGetPersonCount_PlanB1()

     public double analysisGetDiseaseStatus_Average_PlanB1()
     {

         if (analysisGetPersonCount_PlanB1() ==0)
         {
             return 0.0;
         }//end if
         else
         {
             if (getCurrentPlanChosen() == PlanTag.PlanB1)
             {
                 return(getCurrentDiseaseStatus()/analysisGetPersonCount_PlanB1());
             }//end if
             else return 0.0;
         }//end else

     }//end analysisGetDiseaseStatus_Average_PlanB1()


    public int analysisGetPersonCount_PlanB2()
    {
        int personCount = 0;
        
        ListIterator<Person> iterator = Environment.getPersonList().listIterator();
         
        while (iterator.hasNext())
        {
            Person person = iterator.next();
            if (person.getCurrentPlanChosen() == PlanTag.PlanB2)
                personCount ++;
        }//end while
                
        return personCount;

    }//end analysisGetPersonCount_PlanB2()

     public double analysisGetDiseaseStatus_Average_PlanB2()
     {

         if (analysisGetPersonCount_PlanB2() ==0)
         {
             return 0.0;
         }//end if
         else
         {
             if (getCurrentPlanChosen() == PlanTag.PlanB2)
             {
                 return(getCurrentDiseaseStatus()/analysisGetPersonCount_PlanB2());
             }//end if
             else return 0.0;
         }//end else

     }//end analysisGetDiseaseStatus_Average_PlanB2()


    public int analysisGetPersonCount_NonCovered()
    {
        int personCount = 0;
        
        ListIterator<Person> iterator = Environment.getPersonList().listIterator();
         
        while (iterator.hasNext())
        {
            Person person = iterator.next();
            if (person.getCurrentPlanChosen() == PlanTag.None)
                personCount ++;
        }//end while
                
        return personCount;

    }//end analysisGetPersonCount_PlanB2()


     public double analysisGetDiseaseStatus_Average_Covered()
     {

         if (analysisGetPersonCount_Covered() == 0)
         {
             return 0.0;
         }//end if
         else
         {
             if (getCurrentPlanChosen() != PlanTag.None)
             {
                 return(getCurrentDiseaseStatus()/analysisGetPersonCount_Covered());
             }//end if
             else return 0.0;
         }//end else

     }//end analysisGetDiseaseStatus_Average_Covered()


     public double analysisGetDiseaseStatus_Average_NonCovered()
     {

         if (analysisGetPersonCount_NonCovered() == 0)
         {
             return 0.0;
         }//end if
         else
         {
             if (getCurrentPlanChosen() == PlanTag.None)
             {
                 return(getCurrentDiseaseStatus()/analysisGetPersonCount_NonCovered());
             }//end if
             else return 0.0;
         }//end else

     }//end analysisGetDiseaseStatus_Average_NonCovered()


     public double analysisGetDiseaseStatus_Average_Total()
     {

         if (Environment.getPersonList().size() == 0)
         {
             return 0.0;
         }//end if
         else
         {
                 return(getCurrentDiseaseStatus()/Environment.getPersonList().size());

         }//end else

     }//end analysisGetDiseaseStatus_Average_Total()


     public double analysisGetNetIncome_Average_Total()
     {

         if (Environment.getPersonList().size() == 0)
         {
             return 0.0;
         }//end if
         else
         {
                 return(getCurrentNetIncome()/Environment.getPersonList().size());

         }//end else

     }//end analysisGetNetIncome_Average_Total()


    public int analysisGetPersonCount_PenaltyTaxAmountPaid()
    {
        int personCount = 0;
        
        ListIterator<Person> iterator = Environment.getPersonList().listIterator();
         
        while (iterator.hasNext())
        {
            Person person = iterator.next();
            if (person.currentPenaltyTaxAmountPaid != 0.0)
                personCount ++;
        }//end while
                
        return personCount;
    }

    public int analysisGetPersonCount_PremiumAmountPaid()
    {
        int personCount = 0;
        
        ListIterator<Person> iterator = Environment.getPersonList().listIterator();
         
        while (iterator.hasNext())
        {
            Person person = iterator.next();
            if (person.currentPremiumAmountPaid != 0.0)
                personCount ++;
        }//end while
                
        return personCount;
    }

    public int analysisGetPersonCount_CopaymentAmountPaid()
    {
        int personCount = 0;
        
        ListIterator<Person> iterator = Environment.getPersonList().listIterator();
         
        while (iterator.hasNext())
        {
            Person person = iterator.next();
            if (person.currentCopaymentAmountPaid != 0.0)
                personCount ++;
        }//end while
                
        return personCount;
    }

    public double analysisGetCurrentCopaymentAmountPaid_Average()
    {
        return currentCopaymentAmountPaid/analysisGetPersonCount_CopaymentAmountPaid();
    }


    public double analysisGetCurrentPenaltyTaxAmountPaid_Average()
    {
        return currentPenaltyTaxAmountPaid/analysisGetPersonCount_PenaltyTaxAmountPaid();
    }


    public double analysisGetCurrentPremiumAmountPaid_Average()
    {
        return currentPremiumAmountPaid/analysisGetPersonCount_PremiumAmountPaid();
    }



    public int analysisGetPersonCount_Covered()
    {
        int personCount = 0;
        
        ListIterator<Person> iterator = Environment.getPersonList().listIterator();
         
        while (iterator.hasNext())
        {
            Person person = iterator.next();
            if (person.isCurrentExchangeParticipationStatus() == true)
                personCount ++;
        }//end while
                
        return personCount;

    }//end analysisGetPersonCount_Covered

     public double analysisGetNetIncome_Average_Covered()
     {

         if (analysisGetPersonCount_Covered() ==0)
         {
             return 0.0;
         }//end if
         else
         {
             if (isCurrentExchangeParticipationStatus() == true)
             {
                 return(getCurrentNetIncome()/analysisGetPersonCount_Covered());
             }//end if
             else return 0.0;
         }//end else

     }//end analysisGetNetIncome_Average_Covered()


     public double analysisGetNetIncome_Average_NonCovered()
     {

         if (analysisGetPersonCount_NonCovered() ==0)
         {
             return 0.0;
         }//end if
         else
         {
             if (isCurrentExchangeParticipationStatus() == false)
             {
                 return(getCurrentNetIncome()/analysisGetPersonCount_NonCovered());
             }//end if
             else return 0.0;
         }//end else

     }//end analysisGetNetIncome_Average_NonCovered()


     public int analysisTreatmentRequests_NetworkA()
     {

         if ((isCurrentTreatmentRequest() == true) && ((getCurrentPlanChosen() == PlanTag.PlanA1) || (getCurrentPlanChosen() == PlanTag.PlanA2)))
         {
             return 1;
         }//end if
         else return 0;

     }//end analysisTreatmentRequests_NetworkA()

     public int analysisTreatmentRequests_NetworkB()
     {

         if ((isCurrentTreatmentRequest() == true) && ((getCurrentPlanChosen() == PlanTag.PlanB1) || (getCurrentPlanChosen() == PlanTag.PlanB2)))
         {
             return 1;
         }//end if
         else return 0;

     }//end analysisTreatmentRequests_NetworkB()


    public int analysisGetAdverseSelectionCount_CompanyA()
    {
        int adverseSelectionCount = 0;
        
        if (((getCurrentPlanChosen() == PlanTag.PlanA1) || (getCurrentPlanChosen() == PlanTag.PlanA2)) && isCurrentAdverseSelection())
        {
                adverseSelectionCount = 1;
        }//end if
                
        return adverseSelectionCount;
    }

    public int analysisGetAdverseSelectionCount_CompanyB()
    {
        int adverseSelectionCount = 0;
        
        if (((getCurrentPlanChosen() == PlanTag.PlanB1) || (getCurrentPlanChosen() == PlanTag.PlanB2)) && isCurrentAdverseSelection())
        {
                adverseSelectionCount = 1;
        }//end if
                
        return adverseSelectionCount;
    }

    public int analysisGetAdverseSelectionCount_PlanA1()
    {
        int adverseSelectionCount = 0;
        
        if ((getCurrentPlanChosen() == PlanTag.PlanA1) && isCurrentAdverseSelection())
        {
                adverseSelectionCount = 1;
        }//end if
                
        return adverseSelectionCount;
    }

    public int analysisGetAdverseSelectionCount_PlanA2()
    {
        int adverseSelectionCount = 0;
        
        if ((getCurrentPlanChosen() == PlanTag.PlanA2) && isCurrentAdverseSelection())
        {
                adverseSelectionCount = 1;
        }//end if
                
        return adverseSelectionCount;
    }

    public int analysisGetAdverseSelectionCount_PlanB1()
    {
        int adverseSelectionCount = 0;
        
        if ((getCurrentPlanChosen() == PlanTag.PlanB1) && isCurrentAdverseSelection())
        {
                adverseSelectionCount = 1;
        }//end if
                
        return adverseSelectionCount;
    }

    public int analysisGetAdverseSelectionCount_PlanB2()
    {
        int adverseSelectionCount = 0;
        
        if ((getCurrentPlanChosen() == PlanTag.PlanB2) && isCurrentAdverseSelection())
        {
                adverseSelectionCount = 1;
        }//end if
                
        return adverseSelectionCount;
    }



//==============================================================================


//==============================================================================
// Methods:  General methods for the agent.
//==============================================================================

// None

//==============================================================================


//==============================================================================
// Methods:  getter and setter methods
//------------------------------------------------------------------------------       
    public PlanTag getPriorPlanChosen()
    {
        return priorPlanChosen;
    }

    public PlanTag getCurrentPlanChosen()
    {
        return currentPlanChosen;
    }

    public boolean isPriorExchangeParticipationStatus()
    {
        return priorExchangeParticipationStatus;
    }

    public double getCurrentAdverseSelectionValue()
    {
        if (currentAdverseSelection && ((getCurrentPlanChosen() == PlanTag.PlanA1) || (getCurrentPlanChosen() == PlanTag.PlanA2))) return 0.5;
        else if(currentAdverseSelection && ((getCurrentPlanChosen() == PlanTag.PlanB1) || (getCurrentPlanChosen() == PlanTag.PlanB2))) return 1.5;
        else return 2.5;
    }

    public PlanTag getPenultimatePlanChosen()
    {
        return penultimatePlanChosen;
    }

    public double getNextCurrentDiseaseStatusRandomIncrement()
    {
        return nextCurrentDiseaseStatusRandomIncrement;
    }


//==============================================================================   


}//end Person class
