import React, { useEffect, useState } from 'react';
import { map, drawBackground } from '../function/canvasDefault';
import { Col, Row, Slider } from 'antd';
import { CONFIG, COLORS } from '../types/configTypes';

import { useCanvasHooks } from '../hooks/useCanvasHooks'; // variables and functions

import useCanvasMouseText from '../hooksUseEffect/useCanvasMouseText';
import useCanvasRetry from '../hooksUseEffect/useCanvasRetry';
import useCanvasUpdatePitch from '../hooksUseEffect/useCanvasUpdatePitch';
import useCanvasAdjustHeight from '../hooksUseEffect/useCanvasAdjustHeight';
import useCanvasInitializeGetPitch from '../hooksUseEffect/useCanvasInitializeGetPitch';
import useCanvasChangeHzAndNotes from '../hooksUseEffect/useCanvasChangeHzAndNotes';
import useCanvasRedrawBackground from '../hooksUseEffect/useCanvasRedrawBackground';


interface HeteronymProps {
  size: number[];
  config: CONFIG;
  COLORS: COLORS;
  isPlaying: boolean;
  showNotesPar: boolean;
  playLyricCount: number;
  setPlaying: () => void;
  isRetry: boolean;
  setPlayingPause: () => void;
  txtShow: string;
  divisor: number;
  initialRange: number[];
  setMaxLyricCount: (num: number) => void;
  heteronymFile: string[];
}


const Heteronym: React.FC<HeteronymProps> = ({ setMaxLyricCount, initialRange, divisor, txtShow, isRetry, setPlayingPause, playLyricCount, setPlaying, config, COLORS, size, isPlaying, showNotesPar, heteronymFile }) => {

  const {
    pitch,
    setPitch,
    mouseHeight,
    realVoiceColor,
    targetVoiceColor,
    closeVoiceColor,
    ballYCurr,
    canvasRef,
    rectWidth,
    CanvasLength,
    canvasHeight,
    setCanvasHeight,
    showNotes,
    setShowNotes,
    offset,
    currentX,
    setCurrentX,
    initialcurrentX,
    notesLabel,
    setNotesLabel,
    freqLabel,
    setFreqLabel,

    updateBallY,
    updateCanvasHeight,
    handleMouseMove,
  } = useCanvasHooks(size, divisor, COLORS, initialRange);


  const [ballY, setBallY] = useState<number>(size[0]);
  const desiredLength = Math.floor(size[1] / divisor);
  const [inputValue, setInputValue] = useState<number[]>([110, 200]);
  const initialColorChangesFull = new Array(CanvasLength).fill(false);
  const initialBallHistoryFull = new Array(CanvasLength).fill(NaN);
  const [colorChangesFull, setColorChangesFull] = useState<boolean[]>(initialColorChangesFull);
  const [ballHistoryFull, setBallHistoryFull] = useState<number[]>(initialBallHistoryFull);
  const resetStatesFull = () => {
    setColorChangesFull([...initialColorChangesFull]);
    setBallHistoryFull([...initialBallHistoryFull]);
    setCurrentX(initialcurrentX);
  };

  const initialPitchArrayCus = new Array(desiredLength).fill(NaN);

  const [pitchArrayCus, setPitchArrayCus] = useState<number[]>(initialPitchArrayCus);
  const [shouldDisabled, setShouldDisabled] = useState<boolean>(false);

  // +++++++++++++++++++++++++++++++++++++++++++++++++
  // Hooks useEffect parts \\ 

  // initialize get pitch function + pause + draw background
  useCanvasInitializeGetPitch(config, setPitch, updateCanvasHeight, setPlayingPause, canvasRef, initialRange, showNotes);

  // set canvas size for different resolutions
  useCanvasAdjustHeight(canvasRef, setCanvasHeight);

  // store current pitch
  useCanvasUpdatePitch(isPlaying, pitch, updateBallY);

  // update mouse text
  useCanvasMouseText(canvasRef, mouseHeight, initialRange);

  // change Notes and hz display
  useCanvasChangeHzAndNotes(setShowNotes, showNotes, showNotesPar, canvasRef, initialRange, setNotesLabel, setFreqLabel, canvasHeight);

  // retry
  useCanvasRetry(isRetry, divisor, setPlayingPause, resetStatesFull);

  // redraw background when hz or notes changed
  useCanvasRedrawBackground(canvasRef, showNotesPar, initialRange);


  // +++++++++++++++++++++++++++++++++++++++++++++++++
  // Unique useEffect parts \\
  const onChange = (newValue: number[]) => {
    setInputValue(newValue);
  };

  // change Notes and hz display
  useCanvasChangeHzAndNotes(setShowNotes, showNotes, showNotesPar, canvasRef, initialRange, setNotesLabel, setFreqLabel, canvasHeight);

  const updatePitchArrayCus = () => {
    const partLength = Math.floor(CanvasLength / 5);
    const lastPartStart = partLength * 4;
    const midValue = (inputValue[0] + inputValue[1]) / 2;

    const updatedArray = new Array(CanvasLength);
    for (let i = 0.5 * partLength; i < 1.5 * partLength; i++) {
      updatedArray[i] = inputValue[1] + (midValue - inputValue[1]) * ((i - (1.5 * partLength + offset)) / partLength) - 50;
    }

    for (let i = 1.5 * partLength + offset; i < 2.5 * partLength + offset; i++) {

      updatedArray[i] = midValue + (inputValue[1] - midValue) * ((i - 0.5 * partLength) / partLength) - 50;
    }

    setPitchArrayCus(updatedArray);
  }

  useEffect(() => {
    updatePitchArrayCus();
  }, [inputValue]);

  useEffect(() => {

    const canvas = canvasRef.current;
    if (canvas) {
      const ctx = canvas.getContext('2d');
      if (ctx) {
        const textX = canvas.width - 100;
        const textY = 20;
        ctx.clearRect(0, 0, textX - 5, canvas.height);
        ctx.clearRect(0, textY + 5, canvas.width, canvas.height - (textY + 5));

        drawBackground(canvasRef, initialRange[1], initialRange[0], showNotes);

        // stair 
        for (let i = 0 + offset; i < pitchArrayCus.length; i++) {
          const mappedJsonValue = map(pitchArrayCus[i], initialRange[0], initialRange[1], size[0], 0);
          // Set fill color based on condition
          if (colorChangesFull[i]) {
            ctx.fillStyle = closeVoiceColor;
          } else {
            ctx.fillStyle = targetVoiceColor;
          }
          // Draw a rectangle
          ctx.fillRect(i, mappedJsonValue, rectWidth, rectWidth);
        }

        let temTxtShow = false;
        if (txtShow === "false") {
          temTxtShow = false;
        } else {
          temTxtShow = true;
        }

        if (temTxtShow) {
          const fraction = size[1] / 5;
          const heights = [inputValue[0], (inputValue[0] + inputValue[1]) / 2, inputValue[1], (inputValue[0] + inputValue[1]) / 2, inputValue[0]]; // 定义高度数组
          ctx.fillStyle = "black";
          ctx.font = "16px Arial";
          for (let j = 0; j <= 4; j++) {
            let xPosition = fraction * (j + 1) - (fraction / 2);
            let yPosition = map(heights[j] + 5, initialRange[0], initialRange[1], size[0], 0);
          }
        }

        // Draw the ball's historical positions
        for (var i = 0; i < ballHistoryFull.length; i++) {
          // for (var i = currentX; i > 0; i--) {
          ctx.beginPath();
          ctx.arc(i, ballHistoryFull[i], 5, 0, 2 * Math.PI);
          ctx.fillStyle = realVoiceColor;
          ctx.fill();
          ctx.closePath();
        }
        // Plot current value
        ctx.beginPath();
        ctx.arc(currentX * divisor, ballYCurr, 10, 0, 2 * Math.PI);
        ctx.fillStyle = "black";
        ctx.fill();
        ctx.closePath();

      }
    }
  }, [COLORS, txtShow, pitchArrayCus, initialRange, ballHistoryFull, playLyricCount, showNotes, showNotesPar]);

  useEffect(() => {
    const canvas = canvasRef.current;
    if (canvas) {
      const ctx = canvas.getContext('2d');
      if (ctx) {
        // ctx.clearRect(0, 0, canvas.width, canvas.height);
        drawBackground(canvasRef, initialRange[1], initialRange[0], showNotes);

        ctx.font = '16px Arial';

        ctx.fillStyle = 'black';
        // Calculate text position (near top right corner)
        const textX = canvas.width - 100;
        const textY = 20;
        // txt
        const mappedHeight = map(mouseHeight, 0, 100, initialRange[1], initialRange[0]);

        const text = `pitch: ${mappedHeight.toFixed(2)}`;
        const metrics = ctx.measureText(text);
        const textWidth = metrics.width;
        const textHeight = 16;

        const clearX = textX;
        const clearY = textY - textHeight;
        const clearWidth = textWidth;
        const clearHeight = textHeight + 5;

        ctx.clearRect(clearX, clearY, clearWidth, clearHeight);

        ctx.fillText(`pitch: ${mappedHeight.toFixed(2)}`, textX, textY);
      }
    }
  }, [mouseHeight])
  // next
  useEffect(() => {
    setPlayingPause();
    resetStatesFull();
  }, [playLyricCount])

  const updateBallHistoryFull = (ballY: number) => {
    let tempHistoryFull = [...ballHistoryFull];
    let ctxdiv = currentX * divisor;


    if (currentX < desiredLength - 1) {
      setShouldDisabled(false);
      tempHistoryFull[ctxdiv] = ballY;
      setColorChangesFull(currentColors => {
        const newColors = [...currentColors];
        const mappedJsonValue = map(pitchArrayCus[ctxdiv], initialRange[0], initialRange[1], size[0], 0);
        const difference = Math.abs(mappedJsonValue - ballY);
        if (difference <= 50 && !isNaN(difference)) {
          for (let j = ctxdiv - divisor; j <= ctxdiv + divisor && j < CanvasLength; j++) {
            newColors[j] = true;
          }
        }
        return newColors;
      });

    } else {
      setShouldDisabled(true);
      if (!shouldDisabled) {
        setPlayingPause();
      }
    }
    setCurrentX(currentX + 1);
    setBallHistoryFull(tempHistoryFull);
  };


  useEffect(() => {
    // Only update ballHistory at currentX position
    if (isPlaying) {
      updateBallHistoryFull(ballY);
    }
  }, [pitch, isPlaying]);

  useEffect(() => {
    if (shouldDisabled && isPlaying) {
      resetStatesFull();
      setShouldDisabled(false);
    }
  }, [isPlaying]);

  return (
    <Row style={{ maxHeight: "27vw", marginBottom: "5vw", maxWidth: "100vw" }}>
      <Col span={1}>
      </Col>
      <Col span={22}>
        <div className="yAxisArea" style={{ height: canvasHeight }}>
          <Row style={{ height: canvasHeight }}>
            <Col span={8}>
              {showNotesPar
                ? <div style={{ height: canvasHeight }} className="yAxisLabel">Pitch (Notes)</div>
                : <div style={{ height: canvasHeight }} className="yAxisLabel">Pitch (Hz)</div>}
            </Col>
            <Col span={8}>
              <div className="yAxisNumbers" style={{ height: canvasHeight * 1.039 }}>
                {showNotesPar
                  ? notesLabel?.map((note, index) => <div key={index}>{note}</div>) ?? []
                  : freqLabel?.map((freq, index) => <div key={index}>{freq}</div>) ?? []}
              </div>
            </Col>
            <Col span={8}>
              <div className="yAxisLines" style={{ height: canvasHeight }}>
                {freqLabel?.map((_, index) => (
                  <div></div>
                )) ?? []}
              </div>
            </Col>
          </Row>
        </div>

        <div style={{ height: canvasHeight }}>
          <canvas
            ref={canvasRef}
            onMouseMove={handleMouseMove}
            id="pitchCanvas"
            width={size[1]}
            height={size[0]}
            style={{ border: '1px solid #000' }}
          ></canvas>
          <div
                        className='XAxisNum'
                        style={{
                            top: canvasHeight,
                            width: canvasHeight * 3.486,

                        }}
          >
            {[...Array(11)].map((_, index) => (
              <div key={index} style={{ position: 'relative' }}>
                <div className="timeMarkerLine" />
                <span className='timeMarkerNum'>
                  {((15 / (divisor / 4)) * (index / 10)).toFixed(1)}
                </span>
              </div>
            ))}
          </div>
          <div
                        style={{
                          top: canvasHeight * (1.12),
                          width: canvasHeight * 3.486,
                      }}
                      className='XAxis'
          >
            <div>Time (Seconds)</div>

          </div>

        </div>

      </Col>
      <Col span={1}>
        <div>
          <Slider
            vertical
            range
            min={initialRange[0]}
            max={initialRange[1]}
            style={{ marginTop: 0, height: canvasHeight }}
            onChange={onChange}
            defaultValue={inputValue} />
        </div>
      </Col>
    </Row>
  );
}

export default Heteronym;