/* Override the methods given in Strip.cs to add functionality
     */
    public override void Action(GameObject caller)
    {
        SmartBrokenLight sbl               = caller.GetComponent <SmartBrokenLight>();
        bool             flicker           = false,
                         flickerBroken     = false,
                         stopFlickerBroken = false,
                         stopFlicker       = false;
        float probFlicker                  = UnityEngine.Random.Range(0.0f, 1.0f),
              probStopFlicker              = UnityEngine.Random.Range(0.0f, 1.0f),
              probBreak                    = UnityEngine.Random.Range(0.0f, 1.0f);
        int nextState                      = -1;

        // Can't do anything if the calling object does not have the correct functionality
        if (sbl != null)
        {
            // Check preconditions and state
            //
            // A light will flicker if one of the following conditions are met:
            //  (1) A nearby light is not flickering
            //  (2) The light hasn't flickered in the past lastFlicker seconds
            //  (3) It is going to break
            // ... and it is not already flickering and not currently broken
            //
            // A light will stop flickering if one of the following conditions are met:
            //  (1) It has been flickering for flickerDuration seconds
            //  (2) It randomly decides to stop flickering

            // Grab preconditions by id
            List <string> properties = bb.GetProperties(sbl.GetKey());

            // Conditionally stop flickering
            flicker = LightIsOn(properties) &&
                      (
                RollFlicker(probFlicker, sbl.probOfFlicker) ||
                LastFlickerTooLongAgo(sbl, properties)
                      ) &&
                      !OnCooldown(sbl, properties);

            if (!flicker)
            {
                for (int i = (int)TupleElem.BROKEN + 1; i < properties.Count; i++)
                {
                    List <string> otherProperties = bb.GetProperties(properties[i]);
                    if (otherProperties != null)
                    {
                        flicker &= !LightIsFlicker(otherProperties);
                    }
                }
            }

            flickerBroken = flicker && RollBreak(probBreak, sbl.probBreak);

            stopFlicker = LightIsFlicker(properties) &&
                          (
                RollStopFlicker(probStopFlicker, sbl.probOfStopFlicker) ||
                StartFlickerTooLongAgo(sbl, properties)
                          ) &&
                          HasFlickeredMinTime(sbl, properties);

            stopFlickerBroken = stopFlicker &&
                                WillBreak(properties);

            // Add/Delete conditions from the BlackBoard
            //  Add condition: Changed STATE to ON, FLICKER, or DEAD
            //  Add condition: Is the light broken?
            if (flickerBroken)
            {
                nextState = (int)SmartBrokenLight.States.FLICKER;
                bb.UpdateProperty(sbl.GetKey(), (int)TupleElem.BROKEN, "will break");
                bb.UpdateProperty(sbl.GetKey(), (int)TupleElem.START_FLICKER, Time.time.ToString());
            }
            else if (flicker)
            {
                nextState = (int)SmartBrokenLight.States.FLICKER;
                bb.UpdateProperty(sbl.GetKey(), (int)TupleElem.START_FLICKER, Time.time.ToString());
            }
            else if (stopFlickerBroken)
            {
                nextState = (int)SmartBrokenLight.States.DEAD;
                bb.UpdateProperty(sbl.GetKey(), (int)TupleElem.BROKEN, "true");
            }
            else if (stopFlicker)
            {
                nextState = (int)SmartBrokenLight.States.ON;
            }

            if (nextState > -1)
            {
                bb.UpdateProperty(sbl.GetKey(), (int)TupleElem.STATE, nextState.ToString());

                // Perform some action, maybe change state
                // If not flickering, determine whether or not to start flickering
                // Otherwise, determine whether or not to stop flickering

                sbl.InduceTransition(nextState);
            }

            // Deferred-Add/Delete conditions from the BlackBoard
            //  Continue flickering
            //      Add condition: LAST_FLICKER is += Time.deltaTime if still flickering
            //  Stop flickering
            //      (Change nothing)
            if (flicker)
            {
                bb.UpdateProperty(sbl.GetKey(), (int)TupleElem.LAST_FLICKER, (float.Parse(properties[(int)TupleElem.LAST_FLICKER]) + Time.deltaTime).ToString());
            }
        }
    }
 // Check that the light has flickered for at least minFlickerDuration seconds
 private bool HasFlickeredMinTime(SmartBrokenLight sbl, List <string> properties)
 {
     return(Time.time - float.Parse(properties[(int)TupleElem.START_FLICKER]) < sbl.minFlickerDuration);
 }
 // Check tht the light has been flickering for flickerDuration seconds
 private bool StartFlickerTooLongAgo(SmartBrokenLight sbl, List <string> properties)
 {
     return(Time.time - float.Parse(properties[(int)TupleElem.START_FLICKER]) > sbl.flickerDuration);
 }
 // Check that flickering isn't on cooldown
 private bool OnCooldown(SmartBrokenLight sbl, List <string> properties)
 {
     return(Time.time - float.Parse(properties[(int)TupleElem.START_FLICKER]) < sbl.flickerCooldown);
 }
 // Check that the light hasn't flickered in the past lastFlicker seconds
 private bool LastFlickerTooLongAgo(SmartBrokenLight sbl, List <string> properties)
 {
     return(Time.time - float.Parse(properties[(int)TupleElem.LAST_FLICKER]) > sbl.maxLastFlicker);
 }