//------------------------------------------------------------------------------
// Copyright (C) 2008 Invensys Systems Inc.  All rights reserved.
// 
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//------------------------------------------------------------------------------

using System;
using System.Runtime.InteropServices;

using ArchestrA.Core;
using ArchestrA.Toolkit;

namespace ArchestrA.Toolkit.Watchdog
{
    [Guid("85ca8612-8c40-4e51-b8b5-8588f2af5e59")]
    /// <summary>
    /// Summary description for Watchdog
    /// </summary>
    public class StatsRuntime : RuntimeBase
    {
        #region Attributes - Toolkit generated code
        // The following C# properties have been defined to simplify access to
        // attribute values - do not modify the contents of this region with
        // the code editor.
        private CMxTime LastTimeout
        {
            get { return InternalReferenceOnly.LastTimeout; }
            set { InternalReferenceOnly.LastTimeout.Set(value); }
        }

        private CMxInteger TimeoutCnt
        {
            get { return InternalReferenceOnly.TimeoutCnt; }
            set { InternalReferenceOnly.TimeoutCnt.Set(value); }
        }

        private CMxBoolean Reset
        {
            get { return InternalReferenceOnly.Reset; }
            set { InternalReferenceOnly.Reset.Set(value); }
        }

        private CMxElapsedTime DelayMax
        {
            get { return InternalReferenceOnly.DelayMax; }
            set { InternalReferenceOnly.DelayMax.Set(value); }
        }

        private CMxElapsedTime DelayAverage
        {
            get { return InternalReferenceOnly.DelayAverage; }
            set { InternalReferenceOnly.DelayAverage.Set(value); }
        }



        #endregion Attributes

        #region Internal Reference Only
        //Toolkit code required to access the attributes declared in the Object Class
        private Stats InternalReferenceOnly = null;
        #endregion Internal Reference Only

        #region Declarations
        //Declare Runtime Class Variables here.
        //Variables declared in this region are available to all methods 
        //in the Runtime Class.

        bool m_PreviousTimeoutCondition;
        TimeSpan m_PreviousTimeSinceChange;
        TimeSpan m_DelayTotal;
        long m_ChangeDetectedCount;

        #endregion Declarations


        public StatsRuntime()
        {
            InternalReferenceOnly = new Stats();
            this.AObjectInstance = InternalReferenceOnly;

            #region Runtime Event Registration - Toolkit generated code
            // Required region for Runtime Events - do not modify 
            // the contents of this region with the code editor.
            base.RuntimeGetStatusDesc += new RuntimeGetStatusDescDelegate(StatsRuntime_RuntimeGetStatusDesc);
            base.RuntimeSetScanState += new RuntimeSetScanStateDelegate(StatsRuntime_RuntimeSetScanState);
            base.RuntimeExecute += new RuntimeExecuteDelegate(StatsRuntime_RuntimeExecute);
            base.RuntimeShutdown += new RuntimeShutdownDelegate(StatsRuntime_RuntimeShutdown);
            base.RuntimeStartup += new RuntimeStartupDelegate(StatsRuntime_RuntimeStartup);
            base.RuntimeDynamic += new RuntimeDynamicSetHandlerDelegate(StatsRuntime_RuntimeDynamic);
            base.RuntimeInitialize += new RuntimeInitializeDelegate(StatsRuntime_RuntimeInitialize);
            #endregion Runtime Event Registration

            #region Runtime Set Handler Registration – Toolkit generated code
            // Required method for Runtime SetHandlers.  
            // Changes made using the code editor will appear in the Toolkit Editor.
            // 
            // WARNING: Changes made to this region will result in a Shape Change.

            //	TODO: Runtime SetHandler registration
            this.RegisterRuntimeSetHandler("Reset", new RuntimeSetHandlerDelegate(ResetSetHandler));
            #endregion Runtime Set Handler Registration

            #region Primitive Wrapper Initialization - Toolkit generated code
            #endregion Primitive Wrapper Initialization

            #region Primitive Advise only Activate Registration - Toolkit generated code
            #endregion Primitive Advise only Activate Initialization
        }

        private void StatsRuntime_RuntimeInitialize(object sender)
        {
            //------------------------------------------------------------------------------
            //  TODO: Runtime - Initialize
            //
            //  Do not read or write attribute values of this primitive during Initialize
            //------------------------------------------------------------------------------
        }

        private void StatsRuntime_RuntimeExecute(object sender)
        {
            //------------------------------------------------------------------------------
            //  TODO: Runtime - Execute
            //
            //  This is where the main logic of the object periodically executes when
            //  the object is on scan.
            //
            //  Read or write the attributes of this primitive using the generated 
            //  properties (Value, Quality).
            //------------------------------------------------------------------------------
            #region Example code
            //          // The lines below both increment the value of the attribute "Attribute1" by 1
            //          Example_001 ++;  
            //          Example_001 = Example_001 + 1;
            //
            //          //Example_001 quality is set to Bad on Condition_001 being true.
            //          Example_001.Quality = (Condition_001 ?  DataQuality.DataQualityBad : DataQuality.DataQualityGood);
            //
            //          // The attribute "ElapsedTime" contains the time elapsed since the last reset
            //          ElapsedTime = GetScanTime() - TimeOfLastReset;
            //
            //          // Increment the 3rd element of an array
            //          FloatArray[3] ++; 
            //
            //          // Increment all the elements of an array
            //          for( short counter = 1; counter <= FloatArray.Length; counter ++)
            //          {
            //            FloatArray[counter] ++;
            //          }
            #endregion

            bool CurrentTimeoutCondition;
            GetBoolean("myparent.Timeout", out CurrentTimeoutCondition);
            if (!m_PreviousTimeoutCondition && CurrentTimeoutCondition)
            {
                TimeoutCnt++;
                LastTimeout = GetScanTime();
            }

            //Compute the average delay by maintaining a sum/count of all the time elapsed between input changes, 
            //(since the last reset) and dividing by the number input changes detected
            //
            TimeSpan CurrentTimeSinceChange;
            GetElapsedTime("myparent.TimeSinceChange", out CurrentTimeSinceChange);
            if (CurrentTimeSinceChange.Ticks == 0) //The input just changed
            {
                m_DelayTotal = m_DelayTotal + m_PreviousTimeSinceChange;
                DelayAverage = new TimeSpan(m_DelayTotal.Ticks / ++m_ChangeDetectedCount);
            }

            //Compute the maximum delay
            if (CurrentTimeSinceChange > DelayMax)
            {
                DelayMax = CurrentTimeSinceChange;
            }

            //Save these values for tests made during the next scan (see above)
            m_PreviousTimeoutCondition = CurrentTimeoutCondition;
            m_PreviousTimeSinceChange = CurrentTimeSinceChange;
        }

        private void StatsRuntime_RuntimeShutdown(object sender)
        {
            //------------------------------------------------------------------------------
            //  TODO: Runtime Event - Shutdown
            //
            //  Clean up dynamic allocation, release open resources, etc.
            //------------------------------------------------------------------------------

            // Activate marked attributes before shutting down
            AdviseOnlyActivateAttributes();
        }

        private void StatsRuntime_RuntimeStartup(object sender, RuntimeStartupEventArgs e)
        {
            //------------------------------------------------------------------------------
            //  TODO: Runtime Event - Startup
            //
            //  Cache attributes. Cache Primitive IDs of other known primitives.
            //------------------------------------------------------------------------------

            // RestoreDynamicAttributes will recreate the Dynamic Attributes and restore the check pointed 
            // values during failover startup. The Failover Support for Dynamic Attributes option must
            // be enabled via the Object Editor to support this function. To maintain the 
            // Dynamic Attribute values the CheckpointDynamicAttributeData function must be called
            // when Dynamic Attribute values are modified (refer to the Dynamic Attribute Set Handler).

            //RestoreDynamicAttributes();

            // Execute offscan code to initialize the object in its offscan state.
            SetScanState(false);

            // Suspend marked attributes at startup
            AdviseOnlySuspendAttributes();
        }


        private void AdviseOnlySuspendAttributes()
        {
            // Required Method for Advise only Active.
            // Do not modify this method via the code editor.
            //
            // WARNING: Changes made to this method will be overwritten by the code generator.

            if (AdviseOnlyActiveEnabled)
            {
            }
        }

        private void AdviseOnlyActivateAttributes()
        {
            // Required Method for Advise only Active.
            // Do not modify this method via the code editor.
            //
            // WARNING: Changes made to this method will be overwritten by the code generator.

            if (AdviseOnlyActiveEnabled)
            {
            }
        }


        private void StatsRuntime_RuntimeSetScanState(object sender, RuntimeSetScanStateEventArgs e)
        {
            //------------------------------------------------------------------------------
            //  TODO: Runtime Event - SetScanState
            //
            //  Perform actions when the object goes on or off scan.
            //------------------------------------------------------------------------------
        }

        private void StatsRuntime_RuntimeGetStatusDesc(object sender, ref RuntimeGetStatusDescEventArgs e)
        {
            //------------------------------------------------------------------------------
            //  TODO: Runtime Event - GetStatusDesc
            //
            //  This routine provides a String for an error code when a client requests it.
            //  By default this method looks for an entry in the dictionary that has the 
            //  DetailedErrorCode as the PhraseID.
            //
            //  You need to change this implmentation if you want to provide embedded values 
            //  within your messages, or you want to use string PhraseIDs instead of integer 
            //  PhraseIDs.
            //------------------------------------------------------------------------------

            switch (e.detailedErrorCode)
            {
                default:
                    e.status = GetText((int)e.detailedErrorCode);
                    break;
            }
        }

        private void StatsRuntime_RuntimeDynamic(object sender, ref RuntimeSetHandlerEventArgs e)
        {
            string attrName = Get(e.attributeHandle.shAttributeId, e.attributeHandle.shPrimitiveId, EATTRIBUTEPROPERTY.idxAttribPropName);

            //------------------------------------------------------------------------------
            //	TODO: Runtime Event - Dynamic Set Handler
            //
            //  Implement set handler code for any dynamic attributes you create with set handlers  
            //------------------------------------------------------------------------------

            // CheckpointDynamicAttributeData will update the checkpoint values for all Dynamic Attributes. 
            // To recreate Dynamic Attributes and restore Dynamic Attribute values the RestoreDynamicAttributes 
            // function must be called when starting up from failover (refer to the Startup method).

            //CheckpointDynamicAttributeData();

            #region Example
            // if (attrName == "MyDynamic")
            // {
            //     if (e.Value > 10)
            //     {
            //         LogWarning(string.Format("value for {0} must be less or equal to 10", attrName));
            //     }
            //     else
            //     {
            //         SetValue(attrName, e.Value);
            //     }
            //
            //    
            //     return;
            // }   
            #endregion

            // if unhandled a warning is shown as a reminder
            SetValue(e.attributeHandle.shAttributeId, e.attributeHandle.shPrimitiveId, e.Value);
            LogWarning(string.Format("Uknown set handler for attribute '{0}'", attrName));

        }

        private void ResetSetHandler(object sender, ref RuntimeSetHandlerEventArgs e)
        {

            TimeoutCnt = 0;
            LastTimeout = DateTime.MinValue;
            DelayAverage = new TimeSpan(0);
            DelayMax = new TimeSpan(0);

            m_ChangeDetectedCount = 0;
            m_DelayTotal = new TimeSpan(0);
        }
    }
}

