import {
  TMixed,
  checkArrayOfNumbers,
  checkNumber,
  funRound,
  funSMA,
} from "../common";

/**
 * - KDJ 随机指标
 * @param {number[]} CLOSE - 收盘价序列
 * @param {number[]} HIGH - 最高价序列
 * @param {number[]} LOW - 最低价序列
 * @param {number} N - 周期,default: 9
 * @param {number} M1 - 平滑参数,default: 3
 * @param {number} M2 - 平滑参数,default: 3
 * @returns {{ K: TMixed[]; D: TMixed[]; J: TMixed[] }}
 */
export function KDJ(
  CLOSE: number[],
  HIGH: number[],
  LOW: number[],
  N: number = 9,
  M1: number = 3,
  M2: number = 3
): { K: TMixed[]; D: TMixed[]; J: TMixed[] } {
  // - check input parameters
  checkArrayOfNumbers(CLOSE, "CLOSE");
  checkArrayOfNumbers(HIGH, "HIGH");
  checkArrayOfNumbers(LOW, "LOW");
  checkNumber(N, 1, CLOSE, "N", "CLOSE");
  checkNumber(M1, 1, N, "M1", "N");
  checkNumber(M2, 1, N, "M2", "N");

  // - calculate KDJ
  const llv = LOW.reduce((prev: number[], cur, i) => {
    return [
      ...prev,
      Math.min(...LOW.slice(Math.max(0, i - N + 1), i + 1).map((v) => v ?? 0)),
    ];
  }, []);

  const hhv = HIGH.reduce((prev: number[], cur, i) => {
    return [
      ...prev,
      Math.max(...HIGH.slice(Math.max(0, i - N + 1), i + 1).map((v) => v ?? 0)),
    ];
  }, []);

  const RSV = CLOSE.map(
    (v, i) => (((v ?? 0) - llv[i]) / (hhv[i] - llv[i])) * 100
  );

  const K = funSMA(RSV, M1, 1);
  const D = funSMA(K, M2, 1);
  const J = K.map((v, i) => funRound(3 * (v ?? 0) - 2 * (D[i] ?? 0), 3));

  return {
    K: K.map((v) => funRound(v, 3)),
    D: D.map((v) => funRound(v, 3)),
    J,
  };
}
