/* 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
 *  EKO      echo999@ifrance.com
 *
 * Change history:
 *
 *  MMDDYY BY   Description
 *  -------------------------------------------------------------------
 *  010802 MF   Template creation.
 *  051103 EKO  Found bug and fix related to outFastD.
 *  052603 MF   Adapt code to compile with .NET Managed C++
 */

// 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::stochf`]: the number of leading input values consumed before the
    /// first output value can be produced.
    ///
    /// # Arguments
    ///
    /// * `optInFastK_Period` — Lookback window for the highest-high/lowest-low of Fast-K (default
    ///   5, range 1..=100000)
    /// * `optInFastD_Period` — Smoothing period for the Fast-D line (default 3, range 1..=100000)
    /// * `optInFastD_MAType` — Moving-average type used to smooth Fast-D (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 stochf_lookback(&self, mut optInFastK_Period: i32, mut optInFastD_Period: i32, mut optInFastD_MAType: i32) -> usize {
        if ((optInFastK_Period) as i32) == (i32::MIN) {
            optInFastK_Period = 5;
        } else if (((optInFastK_Period) as i32) < 1) || (((optInFastK_Period) as i32) > 100000) {
            return usize::MAX;
        }
        if ((optInFastD_Period) as i32) == (i32::MIN) {
            optInFastD_Period = 3;
        } else if (((optInFastD_Period) as i32) < 1) || (((optInFastD_Period) as i32) > 100000) {
            return usize::MAX;
        }
        let mut retValue: usize = 0_usize;
        // Account for the initial data needed for Fast-K.
        retValue = (optInFastK_Period - 1) as usize;
        // Add the smoothing being done for Fast-D
        retValue += self.ma_lookback(optInFastD_Period, optInFastD_MAType);
        return retValue;
    }
    /// Fast Stochastic Oscillator: the raw %K line and its moving-average-smoothed %D line. Unlike
    /// STOCH (which slows both lines), STOCHF returns the unsmoothed FastK and FastD. Oscillates
    /// 0-100; >80 overbought, \<20 oversold.
    ///
    /// # Formula
    ///
    /// ```text
    /// FastK = 100 * (Close - LowestLow) / (HighestHigh - LowestLow), over the last FastK_Period bars (incl. today)
    /// FastD = MA(FastK, FastD_Period, FastD_MAType)
    /// ```
    ///
    /// # Notes
    ///
    /// * When the high-low range over the window is zero, %K is set to 0 instead of being
    ///   undefined.
    ///
    /// # Arguments
    ///
    /// * `startIdx` — Start index of the requested calculation range.
    /// * `endIdx` — End index of the requested calculation range (inclusive).
    /// * `inHigh` — High prices per bar.
    /// * `inLow` — Low prices per bar.
    /// * `inClose` — Close prices per bar.
    /// * `optInFastK_Period` — Lookback window for the highest-high/lowest-low of Fast-K (default
    ///   5, range 1..=100000)
    /// * `optInFastD_Period` — Smoothing period for the Fast-D line (default 3, range 1..=100000)
    /// * `optInFastD_MAType` — Moving-average type used to smooth Fast-D (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.
    /// * `outFastK` — Raw %K stochastic line.
    /// * `outFastD` — MA-smoothed %K (signal line)
    ///
    /// 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 high: Vec<f64> = (0..252).map(|i| 101.0 + 10.0 * (0.1 * i as f64).sin()).collect();
    /// let low: Vec<f64> = (0..252).map(|i| 99.0 + 10.0 * (0.1 * i as f64).sin()).collect();
    /// let close: Vec<f64> = (0..252).map(|i| 100.0 + 10.0 * (0.1 * i as f64).sin()).collect();
    ///
    /// let core = Core::new();
    /// let mut out_beg = 0;
    /// let mut out_nb = 0;
    /// let mut fast_k = vec![0.0; 252];
    /// let mut fast_d = vec![0.0; 252];
    ///
    /// let ret = core.stochf(
    ///     0, high.len() - 1, &high, &low, &close, 5, 3, 0,
    ///     &mut out_beg, &mut out_nb, &mut fast_k, &mut fast_d,
    /// );
    /// assert_eq!(ret, RetCode::Success);
    /// assert!(out_nb > 0);
    /// ```
    ///
    /// # See also
    ///
    /// [`Core::stoch`] · [`Core::stochrsi`] · [`Core::ma`]
    ///
    /// Further reading: [ta-lib.org/functions/stochf](https://ta-lib.org/functions/stochf/)
    #[doc(alias = "StochasticFast")]
    #[doc(alias = "FastStochasticOscillator")]
    pub fn stochf(
        &self,
        startIdx: usize,
        endIdx: usize,
        inHigh: &[f64],
        inLow: &[f64],
        inClose: &[f64],
        mut optInFastK_Period: i32,
        mut optInFastD_Period: i32,
        mut optInFastD_MAType: i32,
        outBegIdx: &mut usize,
        outNBElement: &mut usize,
        outFastK: &mut [f64],
        outFastD: &mut [f64],
    ) -> RetCode {
        if endIdx < startIdx {
            return RetCode::OutOfRangeStartIndex;
        }
        if ((optInFastK_Period) as i32) == (i32::MIN) {
            optInFastK_Period = 5;
        } else if (((optInFastK_Period) as i32) < 1) || (((optInFastK_Period) as i32) > 100000) {
            return RetCode::BadParam;
        }
        if ((optInFastD_Period) as i32) == (i32::MIN) {
            optInFastD_Period = 3;
        } else if (((optInFastD_Period) as i32) < 1) || (((optInFastD_Period) as i32) > 100000) {
            return RetCode::BadParam;
        }
        let mut startIdx = startIdx;
        let mut retCode: RetCode = RetCode::Success;
        let mut lowest: f64 = 0.0_f64;
        let mut highest: f64 = 0.0_f64;
        let mut tmp: f64 = 0.0_f64;
        let mut diff: f64 = 0.0_f64;
        let mut tempBuffer: Vec<f64> = Vec::new();
        let mut outIdx: usize = 0_usize;
        let mut lowestIdx: i32 = 0_i32;
        let mut highestIdx: i32 = 0_i32;
        let mut lookbackTotal: usize = 0_usize;
        let mut lookbackK: usize = 0_usize;
        let mut lookbackFastD: usize = 0_usize;
        let mut trailingIdx: usize = 0_usize;
        let mut today: usize = 0_usize;
        let mut i: usize = 0_usize;
        let mut bufferIsAllocated: usize = 0_usize;
        // With stochastic, there is a total of 4 different lines that
        // are defined: FASTK, FASTD, SLOWK and SLOWD.
        //
        // The D is the signal line usually drawn over its
        // corresponding K function.
        //
        //                    (Today's Close - LowestLow)
        //  FASTK(Kperiod) =  --------------------------- * 100
        //                     (HighestHigh - LowestLow)
        //
        //  FASTD(FastDperiod, MA type) = MA Smoothed FASTK over FastDperiod
        //
        //  SLOWK(SlowKperiod, MA type) = MA Smoothed FASTK over SlowKperiod
        //
        //  SLOWD(SlowDperiod, MA Type) = MA Smoothed SLOWK over SlowDperiod
        //
        // The HighestHigh and LowestLow are the extreme values among the
        // last 'Kperiod'.
        //
        // SLOWK and FASTD are equivalent when using the same period.
        //
        // The following shows how these four lines are made available in TA-LIB:
        //
        //  TA_STOCH  : Returns the SLOWK and SLOWD
        //  TA_STOCHF : Returns the FASTK and FASTD
        //
        // The TA_STOCH function correspond to the more widely implemented version
        // found in many software/charting package. The TA_STOCHF is more rarely
        // used because its higher volatility cause often whipsaws.
        // Identify the lookback needed.
        lookbackK = (optInFastK_Period - 1) as usize;
        lookbackFastD = self.ma_lookback(optInFastD_Period, optInFastD_MAType);
        lookbackTotal = lookbackK + lookbackFastD;
        // 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 {
            // Succeed... but no data in the output.
            (*outBegIdx) = 0;
            (*outNBElement) = 0;
            return RetCode::Success;
        }
        // Do the K calculation:
        //
        //    Kt = 100 x ((Ct-Lt)/(Ht-Lt))
        //
        // Kt is today stochastic
        // Ct is today closing price.
        // Lt is the lowest price of the last K Period (including today)
        // Ht is the highest price of the last K Period (including today)
        // Proceed with the calculation for the requested range.
        // Note that this algorithm allows the input and
        // output to be the same buffer.
        outIdx = 0;
        // Calculate just enough K for ending up with the caller
        // requested range. (The range of k must consider all
        // the lookback involve with the smoothing).
        trailingIdx = startIdx - lookbackTotal;
        today = trailingIdx + lookbackK;
        highestIdx = 0 - 1;
        lowestIdx = highestIdx;
        lowest = 0.0;
        highest = lowest;
        diff = highest;
        // Allocate a temporary buffer large enough to
        // store the K.
        //
        // If the output is the same as the input, great
        // we just save ourself one memory allocation.
        bufferIsAllocated = 0;
        if outFastK.as_ptr() == inHigh.as_ptr() || outFastK.as_ptr() == inLow.as_ptr() || outFastK.as_ptr() == inClose.as_ptr() {
            tempBuffer = outFastK.to_vec();
        } else if outFastD.as_ptr() == inHigh.as_ptr() || outFastD.as_ptr() == inLow.as_ptr() || outFastD.as_ptr() == inClose.as_ptr() {
            tempBuffer = outFastD.to_vec();
        } else {
            bufferIsAllocated = 1;
            tempBuffer = vec![0.0_f64; ((endIdx - today + 1) * 1) as usize];
        }
        // Do the K calculation
        while today <= endIdx {
            // Set the lowest low
            tmp = inLow[today];
            if lowestIdx < (trailingIdx) as i32 {
                lowestIdx = (trailingIdx) as i32;
                lowest = inLow[(lowestIdx) as usize];
                i = (lowestIdx) as usize;
                while { i += 1; i } <= today {
                    tmp = inLow[i];
                    if tmp < lowest {
                        lowestIdx = (i) as i32;
                        lowest = tmp;
                    }
                }
                diff = (highest - lowest) / 100.0;
            } else if tmp <= lowest {
                lowestIdx = (today) as i32;
                lowest = tmp;
                diff = (highest - lowest) / 100.0;
            }
            // Set the highest high
            tmp = inHigh[today];
            if highestIdx < (trailingIdx) as i32 {
                highestIdx = (trailingIdx) as i32;
                highest = inHigh[(highestIdx) as usize];
                i = (highestIdx) as usize;
                while { i += 1; i } <= today {
                    tmp = inHigh[i];
                    if tmp > highest {
                        highestIdx = (i) as i32;
                        highest = tmp;
                    }
                }
                diff = (highest - lowest) / 100.0;
            } else if tmp >= highest {
                highestIdx = (today) as i32;
                highest = tmp;
                diff = (highest - lowest) / 100.0;
            }
            // Calculate stochastic.
            if diff != 0.0 {
                tempBuffer[outIdx] = (inClose[today] - lowest) / diff;
                outIdx += 1;
            } else {
                tempBuffer[outIdx] = 0.0;
                outIdx += 1;
            }
            trailingIdx += 1;
            today += 1;
        }
        // Fast-K calculation completed. This K calculation is returned
        // to the caller. It is smoothed to become Fast-D.
        retCode = self.ma_unguarded(0, outIdx - 1, &tempBuffer, optInFastD_Period, optInFastD_MAType, outBegIdx, outNBElement, outFastD);
        if retCode != RetCode::Success || ((*outNBElement) as usize) == 0 {
            if bufferIsAllocated != 0 {
            }
            // Something wrong happen? No further data?
            (*outBegIdx) = 0;
            (*outNBElement) = 0;
            return retCode;
        }
        // Copy tempBuffer into the caller buffer.
        // (Calculation could not be done directly in the
        //  caller buffer because more input data then the
        //  requested range was needed for doing %D).
        {
            let _n = ((((*outNBElement) as usize)) as usize * 1) as usize;
            let _di = (0) as usize;
            let _si = (lookbackFastD) as usize;
            outFastK[_di.._di + _n].copy_from_slice(&tempBuffer[_si.._si + _n]);
        };
        // Don't need K anymore, free it if it was allocated here.
        if bufferIsAllocated != 0 {
        }
        if retCode != RetCode::Success {
            // Something wrong happen while processing %D?
            (*outBegIdx) = 0;
            (*outNBElement) = 0;
            return retCode;
        }
        // Note: Keep the outBegIdx relative to the
        //       caller input before returning.
        (*outBegIdx) = startIdx;
        return RetCode::Success;
    }
    /// Unchecked variant of [`Core::stochf`], used for internal cross-indicator calls.
    ///
    /// Skips parameter validation and uses unchecked indexing internally. Every argument must
    /// satisfy the constraints documented on [`Core::stochf`]; 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::stochf`].
    #[inline]
    pub fn stochf_unguarded(
        &self,
        mut startIdx: usize,
        endIdx: usize,
        inHigh: &[f64],
        inLow: &[f64],
        inClose: &[f64],
        mut optInFastK_Period: i32,
        mut optInFastD_Period: i32,
        mut optInFastD_MAType: i32,
        outBegIdx: &mut usize,
        outNBElement: &mut usize,
        outFastK: &mut [f64],
        outFastD: &mut [f64],
    ) -> RetCode {
        let mut retCode: RetCode = RetCode::Success;
        let mut lowest: f64 = 0.0_f64;
        let mut highest: f64 = 0.0_f64;
        let mut tmp: f64 = 0.0_f64;
        let mut diff: f64 = 0.0_f64;
        let mut tempBuffer: Vec<f64> = Vec::new();
        let mut outIdx: usize = 0_usize;
        let mut lowestIdx: i32 = 0_i32;
        let mut highestIdx: i32 = 0_i32;
        let mut lookbackTotal: usize = 0_usize;
        let mut lookbackK: usize = 0_usize;
        let mut lookbackFastD: usize = 0_usize;
        let mut trailingIdx: usize = 0_usize;
        let mut today: usize = 0_usize;
        let mut i: usize = 0_usize;
        let mut bufferIsAllocated: usize = 0_usize;
        unsafe {
        assert!(endIdx < inHigh.len());
        assert!(endIdx < inLow.len());
        assert!(endIdx < inClose.len());
        let _assertLb = self.stochf_lookback(optInFastK_Period, optInFastD_Period, optInFastD_MAType);
        let _assertStart = if startIdx > _assertLb { startIdx } else { _assertLb };
        assert!(_assertStart > endIdx || endIdx - _assertStart < outFastK.len());
        assert!(_assertStart > endIdx || endIdx - _assertStart < outFastD.len());
        lookbackK = (optInFastK_Period - 1) as usize;
        lookbackFastD = self.ma_lookback(optInFastD_Period, optInFastD_MAType);
        lookbackTotal = lookbackK + lookbackFastD;
        if startIdx < lookbackTotal {
            startIdx = lookbackTotal;
        }
        if startIdx > endIdx {
            (*outBegIdx) = 0;
            (*outNBElement) = 0;
            return RetCode::Success;
        }
        outIdx = 0;
        trailingIdx = startIdx - lookbackTotal;
        today = trailingIdx + lookbackK;
        highestIdx = 0 - 1;
        lowestIdx = highestIdx;
        lowest = 0.0;
        highest = lowest;
        diff = highest;
        bufferIsAllocated = 0;
        if outFastK.as_ptr() == inHigh.as_ptr() || outFastK.as_ptr() == inLow.as_ptr() || outFastK.as_ptr() == inClose.as_ptr() {
            tempBuffer = outFastK.to_vec();
        } else if outFastD.as_ptr() == inHigh.as_ptr() || outFastD.as_ptr() == inLow.as_ptr() || outFastD.as_ptr() == inClose.as_ptr() {
            tempBuffer = outFastD.to_vec();
        } else {
            bufferIsAllocated = 1;
            tempBuffer = vec![0.0_f64; ((endIdx - today + 1) * 1) as usize];
        }
        while today <= endIdx {
            tmp = *inLow.as_ptr().add(today);
            if lowestIdx < (trailingIdx) as i32 {
                lowestIdx = (trailingIdx) as i32;
                lowest = *inLow.as_ptr().add((lowestIdx) as usize);
                i = (lowestIdx) as usize;
                while { i += 1; i } <= today {
                    tmp = *inLow.as_ptr().add(i);
                    if tmp < lowest {
                        lowestIdx = (i) as i32;
                        lowest = tmp;
                    }
                }
                diff = (highest - lowest) / 100.0;
            } else if tmp <= lowest {
                lowestIdx = (today) as i32;
                lowest = tmp;
                diff = (highest - lowest) / 100.0;
            }
            tmp = *inHigh.as_ptr().add(today);
            if highestIdx < (trailingIdx) as i32 {
                highestIdx = (trailingIdx) as i32;
                highest = *inHigh.as_ptr().add((highestIdx) as usize);
                i = (highestIdx) as usize;
                while { i += 1; i } <= today {
                    tmp = *inHigh.as_ptr().add(i);
                    if tmp > highest {
                        highestIdx = (i) as i32;
                        highest = tmp;
                    }
                }
                diff = (highest - lowest) / 100.0;
            } else if tmp >= highest {
                highestIdx = (today) as i32;
                highest = tmp;
                diff = (highest - lowest) / 100.0;
            }
            if diff != 0.0 {
                *tempBuffer.as_mut_ptr().add(outIdx) = (*inClose.as_ptr().add(today) - lowest) / diff;
                outIdx += 1;
            } else {
                *tempBuffer.as_mut_ptr().add(outIdx) = 0.0;
                outIdx += 1;
            }
            trailingIdx += 1;
            today += 1;
        }
        retCode = self.ma_unguarded(0, outIdx - 1, &tempBuffer, optInFastD_Period, optInFastD_MAType, outBegIdx, outNBElement, outFastD);
        if retCode != RetCode::Success || ((*outNBElement) as usize) == 0 {
            if bufferIsAllocated != 0 {
            }
            (*outBegIdx) = 0;
            (*outNBElement) = 0;
            return retCode;
        }
        {
            let _n = ((((*outNBElement) as usize)) as usize * 1) as usize;
            let _di = (0) as usize;
            let _si = (lookbackFastD) as usize;
            outFastK[_di.._di + _n].copy_from_slice(&tempBuffer[_si.._si + _n]);
        };
        if bufferIsAllocated != 0 {
        }
        if retCode != RetCode::Success {
            (*outBegIdx) = 0;
            (*outNBElement) = 0;
            return retCode;
        }
        (*outBegIdx) = startIdx;
        return RetCode::Success;
        } // unsafe
    }
}
/***************/
/* End of File */
/***************/
