//==============================================================================
// 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 implementation;

//==============================================================================
//Import statements
//------------------------------------------------------------------------------
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import agents.*;
import repast.simphony.context.Context;
import repast.simphony.engine.environment.RunEnvironment;
import repast.simphony.engine.schedule.ISchedule;
import repast.simphony.engine.schedule.ScheduleParameters;

public class Schedule
{
//==============================================================================
// Variables used in this class
//------------------------------------------------------------------------------    
    private static int period;
    public static FileWriter outputFile = null;
    public static BufferedWriter  outputBuffer = null;
    private Context<Object> simulationContext;
//==============================================================================

//==============================================================================
// Method:  Schedule() constructor
// Note:  System events (updating data stores, charts, displays, etc.) occur on
// integral clock ticks (1.0, 2.0, 3.0, etc.). The initialization of variables
// happens immediately after system events (at time n + 0.001). This is when “prior”
// variables are updated from “current” variables, and when “current” variables
// are initialized. Thus, for example, when system events happen at time 3.0,
// “current” refers to the period from 2.001 to 3.0, inclusive. At time 3.001,
// “current” refers to the period from 3.001 to 4.0, inclusive.
//------------------------------------------------------------------------------        

    public Schedule(Context<Object> pContext)
    {

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//    System.out.println("Schedule constructor at " +
//        RunEnvironment.getInstance().getCurrentSchedule().getTickCount());
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

        simulationContext = pContext;

        ISchedule schedule = RunEnvironment.getInstance().getCurrentSchedule();

        ScheduleParameters scheduleTime000 = ScheduleParameters.createRepeating(0.00, 1);
        ScheduleParameters scheduleTime0001 = ScheduleParameters.createRepeating(.0001, 1);
        ScheduleParameters scheduleTime999 = ScheduleParameters.createRepeating(.999, 1);
        ScheduleParameters scheduleTimeStop = ScheduleParameters.createAtEnd(ScheduleParameters.LAST_PRIORITY);

        schedule.schedule(scheduleTime000, this, "schedule_EnvironmentBehaviors");
        schedule.schedule(scheduleTime000, this, "schedule_PersonBehaviors");
        schedule.schedule(scheduleTime000, this, "schedule_HealthInsuranceCompanyBehaviors");
        schedule.schedule(scheduleTime000, this, "schedule_ProviderNetworkBehaviors");
        schedule.schedule(scheduleTime000, this, "schedule_ExchangeBehaviors");
        schedule.schedule(scheduleTime000, this, "schedule_RiskAdjustmentAgencyBehaviors");
        schedule.schedule(scheduleTime000, this, "schedule_PremiumRateLimitAgencyBehaviors");
        schedule.schedule(scheduleTime000, this, "schedule_PenaltyTaxAgencyBehaviors");


        // Initialize the static variable "period"
        period = 0;

        schedule.schedule(scheduleTime0001, this, "init_IncrementPeriod");
        schedule.schedule(scheduleTimeStop, this, "cleanupAtEnd");  

        if (SimulationUserPanel.isFileOutput() == true)
        {
            //----------------------------------------------------------------------
            // If the input parameter "Simulation tab:  C1. Output file" is "Yes",
            // set up the file to capture output, and write out a file.
            // The character stream class "BufferedWriter" is used to 
            // handle the character data.
            //---------------------------------------------------------------------- 
            String fileName = "output\\" + SimulationUserPanel.getOutputFileName();
            final boolean append = true;
            if (outputFile == null || outputBuffer == null)
            {
                try
                {
                    outputFile = new FileWriter(fileName, append);
                    outputBuffer = new BufferedWriter(outputFile);
                } catch (IOException exception)
                {
                    System.out.println("Encountered an IO exception problem.");
                    System.out.println("writeOutput()");
                    System.out.println("Run: " + Environment.getRunNumber());
                    System.out.println("Period: " + period);
                    exception.printStackTrace();
                }//end catch
            }//end if
            
            schedule.schedule(scheduleTime999, this, "writeOutput");

        }//end if   

    }//end Schedule()
//------------------------------------------------------------------------------


//<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
//<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
// File output
// The next method is for capturing output generated from within the model.
// It is activated only if the input parameter "Simulation tab: C1: Output file" 
// parameter is "Yes".
//<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
//<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>    

//==============================================================================
// Method:  writeOutput()
// This method is used for situations that the Repast "parameter sweep"
// feature cannot handle (which is rare). It writes out a custom-generated file.
//------------------------------------------------------------------------------

        public void writeOutput()
        {

            // Intentionally empty, until needed
            
        }//end writeOutput()
//==============================================================================


//==============================================================================
// Method:  schedule_EnvironmentBehaviors()
// In every period, environment behaviors should be scheduled first.
//------------------------------------------------------------------------------

        public void schedule_EnvironmentBehaviors()
        {

            Iterable<Object> allEnvironmentAgentsInContext = simulationContext.getObjects(Environment.class);

            for (Object environment : allEnvironmentAgentsInContext)
            {
                ISchedule schedule = RunEnvironment.getInstance().getCurrentSchedule();

                double tickCount = RunEnvironment.getInstance().getCurrentSchedule().getTickCount();

                ScheduleParameters scheduleTime0002 = ScheduleParameters.createOneTime(.0002 + tickCount);
                ScheduleParameters scheduleTime001 = ScheduleParameters.createOneTime(.001 + tickCount);

                schedule.schedule(scheduleTime0002, environment, "i1_getInputParameters");
                schedule.schedule(scheduleTime001, environment, "init_InitializeVariables_BOP");

            }//end for

        }//end schedule_EnvironmentBehaviors()
//------------------------------------------------------------------------------

//==============================================================================
// Method:  schedule_PersonBehaviors()
//------------------------------------------------------------------------------
        public void schedule_PersonBehaviors()
        {

            Iterable<Object> allPersonAgentsInContext = simulationContext.getObjects(Person.class);

            for (Object person : allPersonAgentsInContext)
            {
                ISchedule schedule = RunEnvironment.getInstance().getCurrentSchedule();

                double tickCount = RunEnvironment.getInstance().getCurrentSchedule().getTickCount();

                ScheduleParameters scheduleTime001 = ScheduleParameters.createOneTime(.001 + tickCount);
                ScheduleParameters scheduleTime05 = ScheduleParameters.createOneTime(.05 + tickCount);
                ScheduleParameters scheduleTime06 = ScheduleParameters.createOneTime(.06 + tickCount);
                ScheduleParameters scheduleTime51 = ScheduleParameters.createOneTime(.51 + tickCount);
                ScheduleParameters scheduleTime52 = ScheduleParameters.createOneTime(.52 + tickCount);
                ScheduleParameters scheduleTime81 = ScheduleParameters.createOneTime(.81 + tickCount);

                schedule.schedule(scheduleTime001, person, "init_InitializeVariables_BOP");
                schedule.schedule(scheduleTime05, person, "p1_PurchasePlan");
                schedule.schedule(scheduleTime06, person, "p2_PayPenaltyTax");
                schedule.schedule(scheduleTime51, person, "p3_RequestTreatment");
                schedule.schedule(scheduleTime52, person, "p4_PayCopayment");
                schedule.schedule(scheduleTime81, person, "p5_UpdateDiseaseStatus");

            }//end for

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Test 
// This outputs data to the console, to test this process.
// To run the test, remove comments from the code below.
// It is best to run the test with a small number of Person agents.
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//  System.out.println("Schedule schedule_PersonBehaviors() at " +
//          RunEnvironment.getInstance().getCurrentSchedule().getTickCount());
//  System.out.println("Size of Person list:  " + Environment.getPersonList().size());
//  
//  Iterable<Object> allPersonsInContext1 = simulationContext.getObjects(Person.class);
//
//     for (Object person1 : allPersonsInContext1)
//     {
//           System.out.println("Person ID:  " + ((Person) person1).getPersonIDString());
//     }
//  System.out.println("--------------------------------------");
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
            
        }//end schedule_PersonBehaviors()
//------------------------------------------------------------------------------

//==============================================================================
// Method:  schedule_HealthInsuranceCompanyBehaviors()
//------------------------------------------------------------------------------

        public void schedule_HealthInsuranceCompanyBehaviors()
        {

            Iterable<Object> allHealthInsuranceCompanyAgentsInContext = simulationContext.getObjects(HealthInsuranceCompany.class);

            for (Object healthInsuranceCompany : allHealthInsuranceCompanyAgentsInContext)
            {

                ISchedule schedule = RunEnvironment.getInstance().getCurrentSchedule();

                double tickCount = RunEnvironment.getInstance().getCurrentSchedule().getTickCount();

                ScheduleParameters scheduleTime001 = ScheduleParameters.createOneTime(.001 + tickCount);
                ScheduleParameters scheduleTime01 = ScheduleParameters.createOneTime(.01 + tickCount);
                ScheduleParameters scheduleTime82 = ScheduleParameters.createOneTime(.82 + tickCount);
                ScheduleParameters scheduleTime83 = ScheduleParameters.createOneTime(.83 + tickCount);
                ScheduleParameters scheduleTime84 = ScheduleParameters.createOneTime(.84 + tickCount);
                ScheduleParameters scheduleTime86 = ScheduleParameters.createOneTime(.86 + tickCount);
                ScheduleParameters scheduleTime88 = ScheduleParameters.createOneTime(.88 + tickCount);

                schedule.schedule(scheduleTime001, healthInsuranceCompany, "init_InitializeVariables_BOP");
                schedule.schedule(scheduleTime01, healthInsuranceCompany, "p1_SetPremiumRates");
                schedule.schedule(scheduleTime82, healthInsuranceCompany, "p2_ProvideRiskExperience");
                schedule.schedule(scheduleTime83, healthInsuranceCompany, "p3_ProvideProfitExperience");
                schedule.schedule(scheduleTime84, healthInsuranceCompany, "p4_NegotiateTreatmentCostAlpha");
                schedule.schedule(scheduleTime86, healthInsuranceCompany, "p4_NegotiateTreatmentCostAlpha");
                schedule.schedule(scheduleTime88, healthInsuranceCompany, "p4_NegotiateTreatmentCostAlpha");

            }//end for

        }//end schedule_HealthInsuranceCompanyBehaviors()
//------------------------------------------------------------------------------

//==============================================================================
// Method:  schedule_ProviderNetworkBehaviors()
//------------------------------------------------------------------------------

        public void schedule_ProviderNetworkBehaviors()
        {

            Iterable<Object> allProviderNetworkAgentsInContext = simulationContext.getObjects(ProviderNetwork.class);

            for (Object providerNetwork : allProviderNetworkAgentsInContext)
            {
                ISchedule schedule = RunEnvironment.getInstance().getCurrentSchedule();
                
                double tickCount = RunEnvironment.getInstance().getCurrentSchedule().getTickCount();

                ScheduleParameters scheduleTime001 = ScheduleParameters.createOneTime(.001 + tickCount);
                ScheduleParameters scheduleTime53 = ScheduleParameters.createOneTime(.53 + tickCount);
                ScheduleParameters scheduleTime85 = ScheduleParameters.createOneTime(.85 + tickCount);
                ScheduleParameters scheduleTime87 = ScheduleParameters.createOneTime(.87 + tickCount);

                schedule.schedule(scheduleTime001, providerNetwork, "init_InitializeVariables_BOP");
                schedule.schedule(scheduleTime53, providerNetwork, "p1_SubmitClaims");
                schedule.schedule(scheduleTime85, providerNetwork, "p2_ProvideNegotiationResponse");
                schedule.schedule(scheduleTime87, providerNetwork, "p2_ProvideNegotiationResponse");

            }//end for

        }//end schedule_ProviderNetworkBehaviors()
//------------------------------------------------------------------------------

//==============================================================================
// Method:  schedule_ExchangeBehaviors()
//------------------------------------------------------------------------------

        public void schedule_ExchangeBehaviors()
        {

            Iterable<Object> allExchangeAgentsInContext = simulationContext.getObjects(Exchange.class);

            for (Object exchange : allExchangeAgentsInContext)
            {
                ISchedule schedule = RunEnvironment.getInstance().getCurrentSchedule();
                
                double tickCount = RunEnvironment.getInstance().getCurrentSchedule().getTickCount();

                ScheduleParameters scheduleTime001 = ScheduleParameters.createOneTime(.001 + tickCount);
                ScheduleParameters scheduleTime02 = ScheduleParameters.createOneTime(.02 + tickCount);
                ScheduleParameters scheduleTime03 = ScheduleParameters.createOneTime(.03 + tickCount);
                ScheduleParameters scheduleTime07 = ScheduleParameters.createOneTime(.07 + tickCount);

                schedule.schedule(scheduleTime001, exchange, "init_InitializeVariables_BOP");
                schedule.schedule(scheduleTime02, exchange, "p1_OfferPlans");
                schedule.schedule(scheduleTime03, exchange, "p2_AdvertisePlans");
                schedule.schedule(scheduleTime07, exchange, "p3_TransferPremiums");

            }//end for

        }//end schedule_ExchangeBehaviors()
//------------------------------------------------------------------------------

//==============================================================================
// Method:  schedule_RiskAdjustmentAgencyBehaviors()
//------------------------------------------------------------------------------

        public void schedule_RiskAdjustmentAgencyBehaviors()
        {

            Iterable<Object> allRiskAdjustmentAgencyAgentsInContext = simulationContext.getObjects(RiskAdjustmentAgency.class);

            for (Object riskAdjustmentAgency : allRiskAdjustmentAgencyAgentsInContext)
            {
                ISchedule schedule = RunEnvironment.getInstance().getCurrentSchedule();
                
                double tickCount = RunEnvironment.getInstance().getCurrentSchedule().getTickCount();

                ScheduleParameters scheduleTime001 = ScheduleParameters.createOneTime(.001 + tickCount);
                ScheduleParameters scheduleTime89 = ScheduleParameters.createOneTime(.89 + tickCount);

                schedule.schedule(scheduleTime001, riskAdjustmentAgency, "init_InitializeVariables_BOP");
                schedule.schedule(scheduleTime89, riskAdjustmentAgency, "p1_ReallocatePremiums");

            }//end for

        }//end schedule_RiskAdjustmentAgencyBehaviors()
//------------------------------------------------------------------------------

//==============================================================================
// Method:  schedule_PremiumRateLimitAgencyBehaviors()
//------------------------------------------------------------------------------

        public void schedule_PremiumRateLimitAgencyBehaviors()
        {

            Iterable<Object> allPremiumRateLimitAgencyAgentsInContext = simulationContext.getObjects(PremiumRateLimitAgency.class);

            for (Object premiumRateLimitAgency : allPremiumRateLimitAgencyAgentsInContext)
            {
                ISchedule schedule = RunEnvironment.getInstance().getCurrentSchedule();
                
                double tickCount = RunEnvironment.getInstance().getCurrentSchedule().getTickCount();

                ScheduleParameters scheduleTime001 = ScheduleParameters.createOneTime(.001 + tickCount);
                ScheduleParameters scheduleTime90 = ScheduleParameters.createOneTime(.90 + tickCount);

                schedule.schedule(scheduleTime001, premiumRateLimitAgency, "init_InitializeVariables_BOP");
                schedule.schedule(scheduleTime90, premiumRateLimitAgency, "p1_SetPremiumIncreaseLimit");

            }//end for

        }//end schedule_PremiumRateLimitAgencyBehaviors()
//------------------------------------------------------------------------------

//==============================================================================
// Method:  schedule_PenaltyTaxAgencyBehaviors()
//------------------------------------------------------------------------------

        public void schedule_PenaltyTaxAgencyBehaviors()
        {

            Iterable<Object> allPenaltyTaxAgencyAgentsInContext = simulationContext.getObjects(PenaltyTaxAgency.class);

            for (Object penaltyTaxAgency : allPenaltyTaxAgencyAgentsInContext)
            {
                ISchedule schedule = RunEnvironment.getInstance().getCurrentSchedule();
                
                double tickCount = RunEnvironment.getInstance().getCurrentSchedule().getTickCount();

                ScheduleParameters scheduleTime001 = ScheduleParameters.createOneTime(.001 + tickCount);
                ScheduleParameters scheduleTime04 = ScheduleParameters.createOneTime(.04 + tickCount);

                schedule.schedule(scheduleTime001, penaltyTaxAgency, "init_InitializeVariables_BOP");
                schedule.schedule(scheduleTime04, penaltyTaxAgency, "p1_SetPenaltyTaxRate");

            }//end for

        }//end schedule_PenaltyTaxAgencyBehaviors()
//------------------------------------------------------------------------------

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


//==============================================================================
// Method:  init_IncrementPeriod()
//------------------------------------------------------------------------------    
        public void init_IncrementPeriod()
        {
            period++;

            if (period <= SimulationUserPanel.getMaximumSimulationPeriods())
            {
                System.out.println("Period: " + period); //Useful for batch runs
//              System.out.println("--------------------------------------");
            }//end if
            else
            {
                RunEnvironment.getInstance().endRun();
                cleanupAtEnd();
            }//end else

        }//end init_IncrementPeriod()
//==============================================================================

//==============================================================================
// Method:  cleanupAtEnd()
//------------------------------------------------------------------------------    

        public static void cleanupAtEnd()
        {

            try
            {
                if (outputBuffer != null)
                {
                    outputBuffer.flush();
                    outputBuffer.close();
                }//end if
                if (outputFile != null)
                {
                    outputFile.close();
                }//end if
                outputBuffer = null;
                outputFile = null;
            } catch (IOException exception)
            {
                System.out.println("Encountered an IO exception problem.");
                System.out.println("cleanupAtEnd()");
                System.out.println("Run: " + Environment.getRunNumber());
                System.out.println("Period: " + period);
                exception.printStackTrace();
            }//end catch

        }//end cleanupAtEnd()
//==============================================================================


//==============================================================================
// Methods:  getter and setter methods
//------------------------------------------------------------------------------    

    public static int getPeriod()
    {

        return period;

    }//end getPeriod()

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

}//end Schedule class
