/* 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
 *  JPP      JP Pienaar (j.pienaar@mci.co.za)
 *
 * Change history:
 *
 *  MMDDYY BY   Description
 *  -------------------------------------------------------------------
 *  112400 MF   Template creation.
 *  052603 MF   Adapt code to compile with .NET Managed C++
 *  080403 JPP  Fix #767653 for logic when swapping periods.
 */

// 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::macd`]: the number of leading input values consumed before the
    /// first output value can be produced.
    ///
    /// # Arguments
    ///
    /// * `optInFastPeriod` — Period of the fast EMA (default 12, range 2..=100000)
    /// * `optInSlowPeriod` — Period of the slow EMA (default 26, range 2..=100000)
    /// * `optInSignalPeriod` — Smoothing period of the signal line (default 9, range 1..=100000)
    ///
    /// Returns `usize::MAX` when a parameter is out of range. Integer parameters accept `i32::MIN`
    /// to select their default value.
    #[inline]
    pub fn macd_lookback(&self, mut optInFastPeriod: i32, mut optInSlowPeriod: i32, mut optInSignalPeriod: i32) -> usize {
        if ((optInFastPeriod) as i32) == (i32::MIN) {
            optInFastPeriod = 12;
        } else if (((optInFastPeriod) as i32) < 2) || (((optInFastPeriod) as i32) > 100000) {
            return usize::MAX;
        }
        if ((optInSlowPeriod) as i32) == (i32::MIN) {
            optInSlowPeriod = 26;
        } else if (((optInSlowPeriod) as i32) < 2) || (((optInSlowPeriod) as i32) > 100000) {
            return usize::MAX;
        }
        if ((optInSignalPeriod) as i32) == (i32::MIN) {
            optInSignalPeriod = 9;
        } else if (((optInSignalPeriod) as i32) < 1) || (((optInSignalPeriod) as i32) > 100000) {
            return usize::MAX;
        }
        let mut tempInteger: usize = 0_usize;
        // The lookback is driven by the signal line output.
        //
        // (must also account for the initial data consume
        //  by the slow period).
        // Make sure slow is really slower than
        // the fast period! if not, swap...
        if optInSlowPeriod < optInFastPeriod {
            // swap
            tempInteger = (optInSlowPeriod) as usize;
            optInSlowPeriod = optInFastPeriod;
            optInFastPeriod = (tempInteger) as i32;
        }
        return (self.ema_lookback(optInSlowPeriod) + self.ema_lookback(optInSignalPeriod)) as usize;
    }
    /// Moving Average Convergence/Divergence: the difference between a fast and a slow EMA of the
    /// input, plus an EMA-smoothed signal line and their histogram. MACD crossing its signal line
    /// and histogram sign changes flag momentum shifts.
    ///
    /// # Formula
    ///
    /// ```text
    /// MACD = EMA_fast - EMA_slow;  Signal = EMA(MACD, signalPeriod);  Hist = MACD - Signal
    /// ```
    ///
    /// # Notes
    ///
    /// * If the slow period is set smaller than the fast period, the two are swapped so the slow
    ///   EMA is always the longer one.
    /// * Under Metastock compatibility mode the EMAs are seeded from the first value instead of a
    ///   simple moving average, which changes all outputs.
    /// * A signal period of 1 disables signal-line smoothing: the signal equals the MACD line and
    ///   the histogram is zero. Before 0.6.5 this parameter value produced misaligned output
    ///   (issues #48/#59).
    ///
    /// # Arguments
    ///
    /// * `startIdx` — Start index of the requested calculation range.
    /// * `endIdx` — End index of the requested calculation range (inclusive).
    /// * `inReal` — Input series (typically close)
    /// * `optInFastPeriod` — Period of the fast EMA (default 12, range 2..=100000)
    /// * `optInSlowPeriod` — Period of the slow EMA (default 26, range 2..=100000)
    /// * `optInSignalPeriod` — Smoothing period of the signal line (default 9, range 1..=100000)
    /// * `outBegIdx` — Set to the input index of the first output value.
    /// * `outNBElement` — Set to the number of output values written.
    /// * `outMACD` — Fast EMA minus slow EMA.
    /// * `outMACDSignal` — EMA of the MACD line.
    /// * `outMACDHist` — MACD minus 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 data: 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 macd = vec![0.0; 252];
    /// let mut macd_signal = vec![0.0; 252];
    /// let mut macd_hist = vec![0.0; 252];
    ///
    /// let ret = core.macd(
    ///     0, data.len() - 1, &data, 12, 26, 9,
    ///     &mut out_beg, &mut out_nb, &mut macd, &mut macd_signal, &mut macd_hist,
    /// );
    /// assert_eq!(ret, RetCode::Success);
    /// assert!(out_nb > 0);
    /// ```
    ///
    /// # See also
    ///
    /// [`Core::macdext`] · [`Core::macdfix`] · [`Core::ema`] · [`Core::apo`]
    ///
    /// # References
    ///
    /// * Gerald Appel, *Stock Market Trading Systems*, Traders Pr (ISBN 0934380163)
    ///
    /// Further reading: [ta-lib.org/functions/macd](https://ta-lib.org/functions/macd/)
    #[doc(alias = "movingaverageconvergencedivergence")]
    pub fn macd(
        &self,
        startIdx: usize,
        endIdx: usize,
        inReal: &[f64],
        mut optInFastPeriod: i32,
        mut optInSlowPeriod: i32,
        mut optInSignalPeriod: i32,
        outBegIdx: &mut usize,
        outNBElement: &mut usize,
        outMACD: &mut [f64],
        outMACDSignal: &mut [f64],
        outMACDHist: &mut [f64],
    ) -> RetCode {
        if endIdx < startIdx {
            return RetCode::OutOfRangeStartIndex;
        }
        if ((optInFastPeriod) as i32) == (i32::MIN) {
            optInFastPeriod = 12;
        } else if (((optInFastPeriod) as i32) < 2) || (((optInFastPeriod) as i32) > 100000) {
            return RetCode::BadParam;
        }
        if ((optInSlowPeriod) as i32) == (i32::MIN) {
            optInSlowPeriod = 26;
        } else if (((optInSlowPeriod) as i32) < 2) || (((optInSlowPeriod) as i32) > 100000) {
            return RetCode::BadParam;
        }
        if ((optInSignalPeriod) as i32) == (i32::MIN) {
            optInSignalPeriod = 9;
        } else if (((optInSignalPeriod) as i32) < 1) || (((optInSignalPeriod) as i32) > 100000) {
            return RetCode::BadParam;
        }
        let mut startIdx = startIdx;
        let mut slowEMABuffer: Vec<f64> = Vec::new();
        let mut fastEMABuffer: Vec<f64> = Vec::new();
        let mut retCode: RetCode = RetCode::Success;
        let mut tempInteger: usize = 0_usize;
        let mut outBegIdx1: usize = 0_usize;
        let mut outNbElement1: usize = 0_usize;
        let mut outBegIdx2: usize = 0_usize;
        let mut outNbElement2: usize = 0_usize;
        let mut slowK: f64 = 0.0_f64;
        let mut fastK: f64 = 0.0_f64;
        let mut signalK: f64 = 0.0_f64;
        let mut lookbackTotal: usize = 0_usize;
        let mut lookbackSignal: usize = 0_usize;
        let mut useFixedK: usize = 0_usize;
        let mut i: usize = 0_usize;
        // !!! A lot of speed optimization could be done
        // !!! with this function.
        // !!!
        // !!! A better approach would be to use ema
        // !!! just to get the seeding values for the
        // !!! fast and slow EMA. Then process the difference
        // !!! in an allocated buffer until enough data is
        // !!! available for the first signal value.
        // !!! From that point all the processing can
        // !!! be done in a tight loop.
        // !!!
        // !!! That approach will have the following
        // !!! advantage:
        // !!!   1) One mem allocation needed instead of two.
        // !!!   2) The mem allocation size will be only the
        // !!!      signal lookback period instead of the
        // !!!      whole range of data.
        // !!!   3) Processing will be done in a tight loop.
        // !!!      allowing to avoid a lot of memory store-load
        // !!!      operation.
        // !!!   4) The memcpy at the end will be eliminated!
        // !!!
        // !!! If only I had time....
        // Make sure slow is really slower than
        // the fast period! if not, swap...
        if optInSlowPeriod < optInFastPeriod {
            // swap
            tempInteger = (optInSlowPeriod) as usize;
            optInSlowPeriod = optInFastPeriod;
            optInFastPeriod = (tempInteger) as i32;
        }
        // Catch special case for fix 26/12 MACD.
        // Use hardcoded k values matching the original algorithm.
        useFixedK = 0;
        if optInSlowPeriod == 0 {
            optInSlowPeriod = 26;
            // Fix 26
            slowK = 0.075;
            useFixedK = 1;
        } else {
            slowK = 2.0 / ((optInSlowPeriod + 1) as f64);
        }
        if optInFastPeriod == 0 {
            optInFastPeriod = 12;
            // Fix 12
            fastK = 0.15;
            useFixedK = 1;
        } else {
            fastK = 2.0 / ((optInFastPeriod + 1) as f64);
        }
        signalK = 2.0 / ((optInSignalPeriod + 1) as f64);
        lookbackSignal = self.ema_lookback(optInSignalPeriod);
        // Move up the start index if there is not
        // enough initial data.
        lookbackTotal = lookbackSignal;
        lookbackTotal += self.ema_lookback(optInSlowPeriod);
        if startIdx < lookbackTotal {
            startIdx = lookbackTotal;
        }
        // Make sure there is still something to evaluate.
        if startIdx > endIdx {
            (*outBegIdx) = 0;
            (*outNBElement) = 0;
            return RetCode::Success;
        }
        // Allocate intermediate buffer for fast/slow EMA.
        tempInteger = endIdx - startIdx + 1 + lookbackSignal;
        fastEMABuffer = vec![0.0_f64; (tempInteger * 1) as usize];
        slowEMABuffer = vec![0.0_f64; (tempInteger * 1) as usize];
        // Calculate the slow EMA.
        //
        // Move back the startIdx to get enough data
        // for the signal period. That way, once the
        // signal calculation is done, all the output
        // will start at the requested 'startIdx'.
        tempInteger = startIdx - lookbackSignal;
        // Use ema_private when hardcoded k is needed (MACDFIX path).
        // Use ema() for the normal path — codegen handles double/float routing.
        if useFixedK != 0 {
            retCode = self.ema_private(tempInteger, endIdx, inReal, optInSlowPeriod, slowK, &mut outBegIdx1, &mut outNbElement1, &mut slowEMABuffer[..]);
        } else {
            retCode = self.ema_unguarded(tempInteger, endIdx, inReal, optInSlowPeriod, &mut outBegIdx1, &mut outNbElement1, &mut slowEMABuffer[..]);
        }
        if retCode != RetCode::Success {
            (*outBegIdx) = 0;
            (*outNBElement) = 0;
            return retCode;
        }
        // Calculate the fast EMA.
        if useFixedK != 0 {
            retCode = self.ema_private(tempInteger, endIdx, inReal, optInFastPeriod, fastK, &mut outBegIdx2, &mut outNbElement2, &mut fastEMABuffer[..]);
        } else {
            retCode = self.ema_unguarded(tempInteger, endIdx, inReal, optInFastPeriod, &mut outBegIdx2, &mut outNbElement2, &mut fastEMABuffer[..]);
        }
        if retCode != RetCode::Success {
            (*outBegIdx) = 0;
            (*outNBElement) = 0;
            return retCode;
        }
        // Parano tests. Will be removed eventually.
        if outBegIdx1 != tempInteger || outBegIdx2 != tempInteger || outNbElement1 != outNbElement2 || outNbElement1 != endIdx - startIdx + 1 + lookbackSignal {
            (*outBegIdx) = 0;
            (*outNBElement) = 0;
            return RetCode::BadParam;
        }
        // Calculate (fast EMA) - (slow EMA).
        // for( i = 0; i < outNbElement1; i += 1 )
        i = 0;
        while i < outNbElement1 {
            fastEMABuffer[i] = fastEMABuffer[i] - slowEMABuffer[i];
            i += 1;
        }
        // Copy the result into the output for the caller.
        {
            let _n = ((endIdx - startIdx + 1) * 1) as usize;
            let _di = (0) as usize;
            let _si = (lookbackSignal) as usize;
            outMACD[_di.._di + _n].copy_from_slice(&fastEMABuffer[_si.._si + _n]);
        };
        // Calculate the signal/trigger line (on double buffer, use ema_private).
        retCode = self.ema_private(0, outNbElement1 - 1, &fastEMABuffer, optInSignalPeriod, signalK, &mut outBegIdx2, &mut outNbElement2, outMACDSignal);
        if retCode != RetCode::Success {
            (*outBegIdx) = 0;
            (*outNBElement) = 0;
            return retCode;
        }
        // Calculate the histogram.
        // for( i = 0; i < outNbElement2; i += 1 )
        i = 0;
        while i < outNbElement2 {
            outMACDHist[i] = ((outMACD[i] - outMACDSignal[i]) as f64);
            i += 1;
        }
        // All done! Indicate the output limits and return success.
        (*outBegIdx) = startIdx;
        (*outNBElement) = outNbElement2;
        return RetCode::Success;
    }
    /// Unchecked variant of [`Core::macd`], used for internal cross-indicator calls.
    ///
    /// Skips parameter validation and uses unchecked indexing internally. Every argument must
    /// satisfy the constraints documented on [`Core::macd`]; 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::macd`].
    #[inline]
    pub fn macd_unguarded(
        &self,
        mut startIdx: usize,
        endIdx: usize,
        inReal: &[f64],
        mut optInFastPeriod: i32,
        mut optInSlowPeriod: i32,
        mut optInSignalPeriod: i32,
        outBegIdx: &mut usize,
        outNBElement: &mut usize,
        outMACD: &mut [f64],
        outMACDSignal: &mut [f64],
        outMACDHist: &mut [f64],
    ) -> RetCode {
        let mut slowEMABuffer: Vec<f64> = Vec::new();
        let mut fastEMABuffer: Vec<f64> = Vec::new();
        let mut retCode: RetCode = RetCode::Success;
        let mut tempInteger: usize = 0_usize;
        let mut outBegIdx1: usize = 0_usize;
        let mut outNbElement1: usize = 0_usize;
        let mut outBegIdx2: usize = 0_usize;
        let mut outNbElement2: usize = 0_usize;
        let mut slowK: f64 = 0.0_f64;
        let mut fastK: f64 = 0.0_f64;
        let mut signalK: f64 = 0.0_f64;
        let mut lookbackTotal: usize = 0_usize;
        let mut lookbackSignal: usize = 0_usize;
        let mut useFixedK: usize = 0_usize;
        let mut i: usize = 0_usize;
        unsafe {
        assert!(endIdx < inReal.len());
        let _assertLb = self.macd_lookback(optInFastPeriod, optInSlowPeriod, optInSignalPeriod);
        let _assertStart = if startIdx > _assertLb { startIdx } else { _assertLb };
        assert!(_assertStart > endIdx || endIdx - _assertStart < outMACD.len());
        assert!(_assertStart > endIdx || endIdx - _assertStart < outMACDSignal.len());
        assert!(_assertStart > endIdx || endIdx - _assertStart < outMACDHist.len());
        if optInSlowPeriod < optInFastPeriod {
            tempInteger = (optInSlowPeriod) as usize;
            optInSlowPeriod = optInFastPeriod;
            optInFastPeriod = (tempInteger) as i32;
        }
        useFixedK = 0;
        if optInSlowPeriod == 0 {
            optInSlowPeriod = 26;
            slowK = 0.075;
            useFixedK = 1;
        } else {
            slowK = 2.0 / ((optInSlowPeriod + 1) as f64);
        }
        if optInFastPeriod == 0 {
            optInFastPeriod = 12;
            fastK = 0.15;
            useFixedK = 1;
        } else {
            fastK = 2.0 / ((optInFastPeriod + 1) as f64);
        }
        signalK = 2.0 / ((optInSignalPeriod + 1) as f64);
        lookbackSignal = self.ema_lookback(optInSignalPeriod);
        lookbackTotal = lookbackSignal;
        lookbackTotal += self.ema_lookback(optInSlowPeriod);
        if startIdx < lookbackTotal {
            startIdx = lookbackTotal;
        }
        if startIdx > endIdx {
            (*outBegIdx) = 0;
            (*outNBElement) = 0;
            return RetCode::Success;
        }
        tempInteger = endIdx - startIdx + 1 + lookbackSignal;
        fastEMABuffer = vec![0.0_f64; (tempInteger * 1) as usize];
        slowEMABuffer = vec![0.0_f64; (tempInteger * 1) as usize];
        tempInteger = startIdx - lookbackSignal;
        if useFixedK != 0 {
            retCode = self.ema_private(tempInteger, endIdx, inReal, optInSlowPeriod, slowK, &mut outBegIdx1, &mut outNbElement1, &mut slowEMABuffer[..]);
        } else {
            retCode = self.ema_unguarded(tempInteger, endIdx, inReal, optInSlowPeriod, &mut outBegIdx1, &mut outNbElement1, &mut slowEMABuffer[..]);
        }
        if retCode != RetCode::Success {
            (*outBegIdx) = 0;
            (*outNBElement) = 0;
            return retCode;
        }
        if useFixedK != 0 {
            retCode = self.ema_private(tempInteger, endIdx, inReal, optInFastPeriod, fastK, &mut outBegIdx2, &mut outNbElement2, &mut fastEMABuffer[..]);
        } else {
            retCode = self.ema_unguarded(tempInteger, endIdx, inReal, optInFastPeriod, &mut outBegIdx2, &mut outNbElement2, &mut fastEMABuffer[..]);
        }
        if retCode != RetCode::Success {
            (*outBegIdx) = 0;
            (*outNBElement) = 0;
            return retCode;
        }
        if outBegIdx1 != tempInteger || outBegIdx2 != tempInteger || outNbElement1 != outNbElement2 || outNbElement1 != endIdx - startIdx + 1 + lookbackSignal {
            (*outBegIdx) = 0;
            (*outNBElement) = 0;
            return RetCode::BadParam;
        }
        // for( i = 0; i < outNbElement1; i += 1 )
        i = 0;
        while i < outNbElement1 {
            *fastEMABuffer.as_mut_ptr().add(i) = *fastEMABuffer.as_ptr().add(i) - *slowEMABuffer.as_ptr().add(i);
            i += 1;
        }
        {
            let _n = ((endIdx - startIdx + 1) * 1) as usize;
            let _di = (0) as usize;
            let _si = (lookbackSignal) as usize;
            outMACD[_di.._di + _n].copy_from_slice(&fastEMABuffer[_si.._si + _n]);
        };
        retCode = self.ema_private(0, outNbElement1 - 1, &fastEMABuffer, optInSignalPeriod, signalK, &mut outBegIdx2, &mut outNbElement2, outMACDSignal);
        if retCode != RetCode::Success {
            (*outBegIdx) = 0;
            (*outNBElement) = 0;
            return retCode;
        }
        // for( i = 0; i < outNbElement2; i += 1 )
        i = 0;
        while i < outNbElement2 {
            *outMACDHist.as_mut_ptr().add(i) = ((*outMACD.as_ptr().add(i) - *outMACDSignal.as_ptr().add(i)) as f64);
            i += 1;
        }
        (*outBegIdx) = startIdx;
        (*outNBElement) = outNbElement2;
        return RetCode::Success;
        } // unsafe
    }
}
/***************/
/* End of File */
/***************/
