///Licensed under Creative Commons Attribution 4.0 License, Written by Clemens Winter and Vladislav Nachev
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;


namespace Exploitation
{
    /// <summary>
    /// Implements physiological parameters that determine when to make the next visit (see IntervalToNextEvent()) and
    /// how the current visit affects physiology (see ReturnActionOutcome())
    /// </summary>
    class BatPhysiology : Physiology
    {
        int speed;
        int bodyMass;
        int stomachCapacity;
        int maxPortionSize;
        double digestionRate;
        double collectedVolume;
        double collectedSugar;
        double collectedVolumeTotal;
        double collectedSugarTotal;
        double satiety;
        int distanceTravelled;
        static SimpleRNG rand = new SimpleRNG();

        /// <summary>
        /// Physiological parameters of the agent
        /// </summary>
        /// <param name="stomachCapacity">the total volume of the stomach (currently unused parameter)</param>
        /// <param name="digestionRate">the rate at which sugars are digested (plays a role in satiety, but currently unused)</param>
        /// <param name="maxPortionSize">the maximal reward amount an agent can extract. Can be interpreted as "mouth capacity"</param>
        public BatPhysiology(int stomachCapacity, double digestionRate, int maxPortionSize)
        {
            this.stomachCapacity = stomachCapacity;
            this.digestionRate = digestionRate;
            this.maxPortionSize = maxPortionSize;
        }

        /// <summary>
        /// Generate time interval to next visitation event
        /// </summary>
        /// <returns>time interval, in seconds, to the next visitation event</returns>
        public int IntervalToNextEvent()
        {
            double interval = 0;
            double boutbreak = 0.3; // the boutbreak criterion switches from short revisit times to long ones, 
            //drawn from two different lognromal distributions boutbreak = 0.3 corresponds to the typical bout break criterion in the field and lab
            

            if (digestionRate > 0) // if digestion rate is larger than 0 than the bout behavior depends on the stomach contents, 
            //otherwise see previous line
            {
                boutbreak = satiety;
            }
            
            if (rand.GetUniform() < boutbreak)
            {
                while ((interval < 1) || (interval > 8000))
                {
                    interval = rand.GetLogNormal(5, 2); //Values 5, 2 correspond to typical visits for 3 bats in lab; Take 3.5, 2 for 9 bats in lab
                    // field bats = 6.1, 1.2
                }
            }
            else
            {
                while ((interval < 1) || (interval > 8000))
                {
                    interval = rand.GetLogNormal(3.2, 1.8); //Values 3.2, 1.8 correspond to typical visits for 3 bats in lab; Take 1.7, 0.5 for 9 bats in lab
                    // field bats = 2.8, 0.7
                }
            }

            if (digestionRate < 0) interval = 30; //if regular revisit intervals are to be simulated, hardcode here, and use negative digestion rate

            return Convert.ToInt32(interval);

        }

        /// <summary>
        /// Update the satiety and the physiological response to the collected reward
        /// </summary>
        /// <param name="reward">reward properties struct</param>
        /// <param name="intervalLastVisit">the time interval in seconds since the previous visit</param>
        public void ReturnActionOutcome(RewardStruct reward, long intervalLastVisit)
        {
            collectedVolume = reward.Reward[1];
            //collectedSugar = reward.Reward[0] * reward.Reward[1];
            collectedVolumeTotal += reward.Reward[1];
            //collectedSugarTotal += reward.Reward[0] * reward.Reward[1];
            satiety = satiety - intervalLastVisit * digestionRate + collectedVolume / stomachCapacity;
        }

        /// <summary>
        /// resets satiety values at the beginning of each run/generation
        /// </summary>
        public void ResetSatiety()
        {
            collectedVolume = 0;
            collectedSugar = 0;
            collectedVolumeTotal = 0;
            collectedSugarTotal = 0;
            satiety = 0;
        }


        public double Satiety { get { return satiety; } }
        public int MealMax { get { return maxPortionSize; } }
        public double DigestionRate { get { return digestionRate; } }
    }
}
