/* TA-LIB Copyright (c) 1999-2025, Mario Fortier
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in
 *   the documentation and/or other materials provided with the
 *   distribution.
 *
 * - Neither name of author nor the names of its contributors
 *   may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/* Important:
 *  This file is automatically generated by the utility ta_codegen.
 *  Any modifications will be lost on next execution of ta_codegen.
 *
 *  Modifications should instead be done with the "C" source file
 *  in ta-lib\src\ta_func
 */

/* List of contributors:
 *
 *  Initial  Name/description
 *  -------------------------------------------------------------------
 *  MF       Mario Fortier
 *
 * Change history:
 *
 *  MMDDYY BY     Description
 *  -------------------------------------------------------------------
 *  021807 MF     Initial Version
 */

// Import types from parent module
use super::*;

// Allow non-snake-case names to maintain TA-Lib API compatibility
#[allow(non_snake_case)]
#[allow(unused_variables)]
#[allow(dead_code)]
#[allow(unused_mut)]
#[allow(unused_assignments)]
impl Core {
    /// Lookback period for [`Core::mavp`]: the number of leading input values consumed before the
    /// first output value can be produced.
    ///
    /// # Arguments
    ///
    /// * `optInMinPeriod` — lower clamp for per-bar period (default 2, range 1..=100000)
    /// * `optInMaxPeriod` — upper clamp for per-bar period (default 30, range 1..=100000)
    /// * `optInMAType` — moving-average type applied (default 0 = SMA, values: 0=SMA, 1=EMA,
    ///   2=WMA, 3=DEMA, 4=TEMA, 5=TRIMA, 6=KAMA, 7=MAMA, 8=T3)
    ///
    /// Returns `usize::MAX` when a parameter is out of range. Integer parameters accept `i32::MIN`
    /// to select their default value.
    #[inline]
    pub fn mavp_lookback(&self, mut optInMinPeriod: i32, mut optInMaxPeriod: i32, mut optInMAType: i32) -> usize {
        if ((optInMinPeriod) as i32) == (i32::MIN) {
            optInMinPeriod = 2;
        } else if (((optInMinPeriod) as i32) < 1) || (((optInMinPeriod) as i32) > 100000) {
            return usize::MAX;
        }
        if ((optInMaxPeriod) as i32) == (i32::MIN) {
            optInMaxPeriod = 30;
        } else if (((optInMaxPeriod) as i32) < 1) || (((optInMaxPeriod) as i32) > 100000) {
            return usize::MAX;
        }
        return self.ma_lookback(optInMaxPeriod, optInMAType);
    }
    /// Moving average whose period varies per bar, driven by a companion period series. For each
    /// bar it computes an MA of the selected type over the (clamped) period given by inPeriods.
    ///
    /// # Formula
    ///
    /// ```text
    /// p_i = clamp((int)inPeriods[startIdx+i], optInMinPeriod, optInMaxPeriod); outReal[i] = MA(inReal, p_i, optInMAType) at bar startIdx+i
    /// ```
    ///
    /// # Notes
    ///
    /// * Fractional per-bar periods are truncated to whole numbers before being clamped to the
    ///   minimum and maximum period.
    /// * Period values of 1 perform no smoothing (the bar's output equals its input); the minimum
    ///   allowed period is 1 since 0.6.5.
    ///
    /// # Arguments
    ///
    /// * `startIdx` — Start index of the requested calculation range.
    /// * `endIdx` — End index of the requested calculation range (inclusive).
    /// * `inReal` — series to be averaged.
    /// * `inPeriods` — per-bar desired MA period.
    /// * `optInMinPeriod` — lower clamp for per-bar period (default 2, range 1..=100000)
    /// * `optInMaxPeriod` — upper clamp for per-bar period (default 30, range 1..=100000)
    /// * `optInMAType` — moving-average type applied (default 0 = SMA, values: 0=SMA, 1=EMA,
    ///   2=WMA, 3=DEMA, 4=TEMA, 5=TRIMA, 6=KAMA, 7=MAMA, 8=T3)
    /// * `outBegIdx` — Set to the input index of the first output value.
    /// * `outNBElement` — Set to the number of output values written.
    /// * `outReal` — variable-period moving average.
    ///
    /// Integer parameters accept `i32::MIN` to select their default value.
    ///
    /// # Errors
    ///
    /// Returns [`RetCode::OutOfRangeStartIndex`] when `endIdx < startIdx`, and
    /// [`RetCode::BadParam`] when an optional parameter is outside its documented range.
    ///
    /// # Panics
    ///
    /// Input slices must cover `startIdx..=endIdx` and output slices must hold the number of values
    /// produced for that range: undersized slices panic or, for functions that forward to unchecked
    /// internals, cause undefined behavior. Sizing every output slice to the input length is always
    /// sufficient.
    ///
    /// # Examples
    ///
    /// ```
    /// use ta_lib::{Core, RetCode};
    ///
    /// let data: Vec<f64> = (0..252).map(|i| 100.0 + 10.0 * (0.1 * i as f64).sin()).collect();
    /// let periods = vec![14.0; 252];
    ///
    /// let core = Core::new();
    /// let mut out_beg = 0;
    /// let mut out_nb = 0;
    /// let mut out = vec![0.0; 252];
    ///
    /// let ret = core.mavp(
    ///     0, data.len() - 1, &data, &periods, 2, 30, 0,
    ///     &mut out_beg, &mut out_nb, &mut out,
    /// );
    /// assert_eq!(ret, RetCode::Success);
    /// assert!(out_nb > 0);
    /// ```
    ///
    /// # See also
    ///
    /// [`Core::ma`] · [`Core::sma`] · [`Core::mama`] · [`Core::t3`]
    ///
    /// Further reading: [ta-lib.org/functions/mavp](https://ta-lib.org/functions/mavp/)
    #[doc(alias = "MovingAverageVariablePeriod")]
    #[doc(alias = "VariablePeriodMovingAverage")]
    pub fn mavp(
        &self,
        startIdx: usize,
        endIdx: usize,
        inReal: &[f64],
        inPeriods: &[f64],
        mut optInMinPeriod: i32,
        mut optInMaxPeriod: i32,
        mut optInMAType: i32,
        outBegIdx: &mut usize,
        outNBElement: &mut usize,
        outReal: &mut [f64],
    ) -> RetCode {
        if endIdx < startIdx {
            return RetCode::OutOfRangeStartIndex;
        }
        if ((optInMinPeriod) as i32) == (i32::MIN) {
            optInMinPeriod = 2;
        } else if (((optInMinPeriod) as i32) < 1) || (((optInMinPeriod) as i32) > 100000) {
            return RetCode::BadParam;
        }
        if ((optInMaxPeriod) as i32) == (i32::MIN) {
            optInMaxPeriod = 30;
        } else if (((optInMaxPeriod) as i32) < 1) || (((optInMaxPeriod) as i32) > 100000) {
            return RetCode::BadParam;
        }
        let mut startIdx = startIdx;
        let mut i: usize = 0_usize;
        let mut j: usize = 0_usize;
        let mut lookbackTotal: usize = 0_usize;
        let mut outputSize: usize = 0_usize;
        let mut tempInt: usize = 0_usize;
        let mut curPeriod: usize = 0_usize;
        let mut localPeriodArray: Vec<i32> = Vec::new();
        let mut localOutputArray: Vec<f64> = Vec::new();
        let mut localBegIdx: usize = 0_usize;
        let mut localNbElement: usize = 0_usize;
        let mut retCode: RetCode = RetCode::Success;
        // Identify the minimum number of price bar needed
        // to calculate at least one output.
        lookbackTotal = self.ma_lookback(optInMaxPeriod, optInMAType);
        // Move up the start index if there is not
        // enough initial data.
        if startIdx < lookbackTotal {
            startIdx = lookbackTotal;
        }
        // Make sure there is still something to evaluate.
        if startIdx > endIdx {
            (*outBegIdx) = 0;
            (*outNBElement) = 0;
            return RetCode::Success;
        }
        // Calculate exact output size
        if lookbackTotal > startIdx {
            tempInt = lookbackTotal;
        } else {
            tempInt = startIdx;
        }
        if tempInt > endIdx {
            // No output
            (*outBegIdx) = 0;
            (*outNBElement) = 0;
            return RetCode::Success;
        }
        outputSize = endIdx - tempInt + 1;
        // Allocate intermediate local buffer.
        localOutputArray = vec![0.0_f64; (outputSize * 1) as usize];
        localPeriodArray = vec![0_i32; (outputSize * 1) as usize];
        // Copy caller array of period into local buffer.
        // At the same time, truncate to min/max.
        // for( i = 0; i < outputSize; i += 1 )
        i = 0;
        while i < outputSize {
            tempInt = (inPeriods[startIdx + i] as usize) as usize;
            if tempInt < (optInMinPeriod) as usize {
                tempInt = (optInMinPeriod) as usize;
            } else if tempInt > (optInMaxPeriod) as usize {
                tempInt = (optInMaxPeriod) as usize;
            }
            localPeriodArray[i] = (tempInt) as i32;
            i += 1;
        }
        // Process each element of the input.
        // For each possible period value, the MA is calculated
        // only once.
        // The outReal is then fill up for all element with
        // the same period.
        // A local flag (value 0) is set in localPeriodArray
        // to avoid doing a second time the same calculation.
        // for( i = 0; i < outputSize; i += 1 )
        i = 0;
        while i < outputSize {
            curPeriod = (localPeriodArray[i]) as usize;
            if curPeriod != 0 {
                // TODO: This portion of the function can be slightly speed
                //       optimized by making the function without unstable period
                //       start their calculation at 'startIdx+i' instead of startIdx.
                // Calculation of the MA required.
                retCode = self.ma_unguarded(startIdx, endIdx, inReal, (curPeriod) as i32, optInMAType, &mut localBegIdx, &mut localNbElement, &mut localOutputArray[..]);
                if retCode != RetCode::Success {
                    (*outBegIdx) = 0;
                    (*outNBElement) = 0;
                    return retCode;
                }
                outReal[i] = ((localOutputArray[i]) as f64);
                // for( j = i + 1; j < outputSize; j += 1 )
                j = i + 1;
                while j < outputSize {
                    if (localPeriodArray[j]) as usize == curPeriod {
                        localPeriodArray[j] = 0;
                        // Flag to avoid recalculation
                        outReal[j] = ((localOutputArray[j]) as f64);
                    }
                    j += 1;
                }
            }
            i += 1;
        }
        // Done. Inform the caller of the success.
        (*outBegIdx) = startIdx;
        (*outNBElement) = outputSize;
        return RetCode::Success;
    }
    /// Unchecked variant of [`Core::mavp`], used for internal cross-indicator calls.
    ///
    /// Skips parameter validation and uses unchecked indexing internally. Every argument must
    /// satisfy the constraints documented on [`Core::mavp`]; an out-of-range parameter, an input
    /// slice not covering `startIdx..=endIdx`, or an undersized output slice may panic or cause
    /// undefined behavior. Prefer [`Core::mavp`].
    #[inline]
    pub fn mavp_unguarded(
        &self,
        mut startIdx: usize,
        endIdx: usize,
        inReal: &[f64],
        inPeriods: &[f64],
        mut optInMinPeriod: i32,
        mut optInMaxPeriod: i32,
        mut optInMAType: i32,
        outBegIdx: &mut usize,
        outNBElement: &mut usize,
        outReal: &mut [f64],
    ) -> RetCode {
        let mut i: usize = 0_usize;
        let mut j: usize = 0_usize;
        let mut lookbackTotal: usize = 0_usize;
        let mut outputSize: usize = 0_usize;
        let mut tempInt: usize = 0_usize;
        let mut curPeriod: usize = 0_usize;
        let mut localPeriodArray: Vec<i32> = Vec::new();
        let mut localOutputArray: Vec<f64> = Vec::new();
        let mut localBegIdx: usize = 0_usize;
        let mut localNbElement: usize = 0_usize;
        let mut retCode: RetCode = RetCode::Success;
        unsafe {
        assert!(endIdx < inReal.len());
        assert!(endIdx < inPeriods.len());
        let _assertLb = self.mavp_lookback(optInMinPeriod, optInMaxPeriod, optInMAType);
        let _assertStart = if startIdx > _assertLb { startIdx } else { _assertLb };
        assert!(_assertStart > endIdx || endIdx - _assertStart < outReal.len());
        lookbackTotal = self.ma_lookback(optInMaxPeriod, optInMAType);
        if startIdx < lookbackTotal {
            startIdx = lookbackTotal;
        }
        if startIdx > endIdx {
            (*outBegIdx) = 0;
            (*outNBElement) = 0;
            return RetCode::Success;
        }
        if lookbackTotal > startIdx {
            tempInt = lookbackTotal;
        } else {
            tempInt = startIdx;
        }
        if tempInt > endIdx {
            (*outBegIdx) = 0;
            (*outNBElement) = 0;
            return RetCode::Success;
        }
        outputSize = endIdx - tempInt + 1;
        localOutputArray = vec![0.0_f64; (outputSize * 1) as usize];
        localPeriodArray = vec![0_i32; (outputSize * 1) as usize];
        // for( i = 0; i < outputSize; i += 1 )
        i = 0;
        while i < outputSize {
            tempInt = (*inPeriods.as_ptr().add(startIdx + i) as usize) as usize;
            if tempInt < (optInMinPeriod) as usize {
                tempInt = (optInMinPeriod) as usize;
            } else if tempInt > (optInMaxPeriod) as usize {
                tempInt = (optInMaxPeriod) as usize;
            }
            *localPeriodArray.as_mut_ptr().add(i) = (tempInt) as i32;
            i += 1;
        }
        // for( i = 0; i < outputSize; i += 1 )
        i = 0;
        while i < outputSize {
            curPeriod = (*localPeriodArray.as_ptr().add(i)) as usize;
            if curPeriod != 0 {
                retCode = self.ma_unguarded(startIdx, endIdx, inReal, (curPeriod) as i32, optInMAType, &mut localBegIdx, &mut localNbElement, &mut localOutputArray[..]);
                if retCode != RetCode::Success {
                    (*outBegIdx) = 0;
                    (*outNBElement) = 0;
                    return retCode;
                }
                *outReal.as_mut_ptr().add(i) = ((*localOutputArray.as_ptr().add(i)) as f64);
                // for( j = i + 1; j < outputSize; j += 1 )
                j = i + 1;
                while j < outputSize {
                    if (*localPeriodArray.as_ptr().add(j)) as usize == curPeriod {
                        *localPeriodArray.as_mut_ptr().add(j) = 0;
                        *outReal.as_mut_ptr().add(j) = ((*localOutputArray.as_ptr().add(j)) as f64);
                    }
                    j += 1;
                }
            }
            i += 1;
        }
        (*outBegIdx) = startIdx;
        (*outNBElement) = outputSize;
        return RetCode::Success;
        } // unsafe
    }
}
/***************/
/* End of File */
/***************/
