//#define NO_OUTPUTtoELMO


using System;
using Microsoft.SPOT;

using System.Text;

namespace CPF
{
    public class BuoyancyControl
    {
        public static void init()
        {
            IB = 0.75;
            OB = 2.0;
        }

        public static double IB { get; private set; }
        public static double OB { get; private set; }

        public static StringBuilder sbTemp = new StringBuilder(128);

        //TODO move bellowsPosition to this class
        //public static double bellowsPosition { get; private set; }

        // Below is legacy CPF code
        private struct PlatVel
        {
            //PID constants
            public const double K = configFile.KPlatVel,            // Constant for P term of PID
                                    Ti = configFile.TiPlatVel,          // Time constant for I term of PID (in denominator, bigger values less I term in output
                                    Td = configFile.TdPlatVel,          // Time constant for D term of PID
                                    Tf = configFile.TfPlatVel,          // Derivative filter time constant
                                    Tt = configFile.TtPlatVel,          // Anti-windup time constant
                                    B = configFile.BPlatVel,            // Setpoint weight
                                    uLow = configFile.uLowPlatVel,      // minimum possible output (RPS)
                                    uHigh = configFile.uHighPlatVel;    // maximum possible output (RPS)

            public static double h = configFile.hPlatVel;            // Sample period (seconds)

            //Precomputed constants
            public static double den = (Tf * Tf) + (2 * h * Tf) + (2 * h * h),   // denominator of measured signal filter terms
                                    p1 = Tf * Tf / den,                             // first part of state variable x2 equation
                                    p2 = 2 * h * h / den,                           // second part of state variable x1 equation
                                    p3 = K * h / Ti,                                // integral gain
                                    p4 = K * Td / h,                                // derivative gain
                                    p5 = h / Tt;                                    // anti-windup gain

            //Loop variables
            public static double I = 0,      // Integrator part of PID calculation
                                    y1 = 0,     // first state variable
                                    y2 = 0,     // second state variable
                                    v = 0.0,    // unclipped PID output
                                    u = 0.0;    // clipped PID output
            public static int uCPS = 0;  // u value in counts per second
        }

        public struct PIDLogValues
        {
            public double setPoint;
            public double PV;
            public double y1;
            public double y2;
            public double I;
            public double v;
            public double u;
            public double uCPS;
            public double h;
        }
        private static PIDLogValues BCPIDLogValues;

        public static void resetPVPID()
        {
            PlatVel.I = 0;
            PlatVel.y1 = 0;
            PlatVel.y2 = 0;
            PlatVel.v = 0.0;
            PlatVel.u = 0.0;
            PlatVel.uCPS = 0;
        }

        public static void calcPIDConstants()
        {
            PlatVel.h = SBE41.ctdSamplePeriod;

            PlatVel.den = (PlatVel.Tf * PlatVel.Tf) + (2 * PlatVel.h * PlatVel.Tf) + (2 * PlatVel.h * PlatVel.h);   // denominator of measured signal filter terms
            PlatVel.p1 = PlatVel.Tf * PlatVel.Tf / PlatVel.den;                             // first part of state variable x2 equation
            PlatVel.p2 = 2 * PlatVel.h * PlatVel.h / PlatVel.den;                           // second part of state variable x1 equation
            PlatVel.p3 = PlatVel.K * PlatVel.h / PlatVel.Ti;                                // integral gain
            PlatVel.p4 = PlatVel.K * PlatVel.Td / PlatVel.h;                                // derivative gain
            PlatVel.p5 = PlatVel.h / PlatVel.Tt;                                            // anti-windup gain

            resetPVPID();
            
            sbTemp.Clear();
            sbTemp.Append("Calculated new PID Constant, h = ");
            sbTemp.Append(PlatVel.h.ToString());
            EngrLogger.writeToColumns(sbTemp);
        }

        public static PIDLogValues PIDPlatformVelocity(double velocitySetPoint, double currentPlatVel)
        {
            PlatVel.h = SBE41.ctdSamplePeriod;

            //Astrom & Hagglund PID Algorithm
            PlatVel.y2 = (PlatVel.p1 * PlatVel.y2) + (PlatVel.p2 * (currentPlatVel - PlatVel.y1));     // update filter state y2
            PlatVel.y1 = PlatVel.y1 + PlatVel.y2;                                               // update filter state y1
            PlatVel.v = PlatVel.K * ((PlatVel.B * velocitySetPoint) - PlatVel.y1) -             // compute nominal output
                    (PlatVel.p4 * PlatVel.y2) + PlatVel.I;

            // limit the nominal output to desired range
            if (PlatVel.v < PlatVel.uLow)
                PlatVel.u = PlatVel.uLow;
            else if (PlatVel.v > PlatVel.uHigh)
                PlatVel.u = PlatVel.uHigh;
            else
                PlatVel.u = PlatVel.v;

            // update the integral
            PlatVel.I = PlatVel.I + (PlatVel.p3 * (velocitySetPoint - PlatVel.y1)) + (PlatVel.p5 * (PlatVel.u - PlatVel.v));

            //Convert Rev per second to Counts per second
            PlatVel.uCPS = (int)(PlatVel.u * configFile.COUNTSperREV);

#if(!NO_OUTPUTtoELMO)
            if (PlatVel.uCPS >= 0)
            {
                Elmo.extendBellows(PlatVel.uCPS);      //Note: extend bellows takes the absolute value of the passed in speed
            }
            else
            {
                Elmo.retractBellows(PlatVel.uCPS);     //Note: retract bellows takes the absolute value of the passed in speed
            }
#else
            Debug.Print("Output to Elmo is disabled in Buoyancy Control");
            sbTemp.Clear();
            sbTemp.Append("Output to Elmo is disabled in Buoyancy Control");
            Program.btConsole.SendLine(sbTemp);
#endif

            BCPIDLogValues.I = PlatVel.I;
            BCPIDLogValues.PV = currentPlatVel;
            BCPIDLogValues.setPoint = velocitySetPoint;
            BCPIDLogValues.u = PlatVel.u;
            BCPIDLogValues.uCPS = PlatVel.uCPS;
            BCPIDLogValues.v = PlatVel.v;
            BCPIDLogValues.y1 = PlatVel.y1;
            BCPIDLogValues.y2 = PlatVel.y2;
            BCPIDLogValues.h = PlatVel.h;
            
            return (BCPIDLogValues);
        }
    }
}