import { Component, createRef } from "react";
import PropTypes from "prop-types";
import { gsap, ScrollTrigger } from "gsap/all";
import LazyLoad from "react-lazyload";
import { css } from "@emotion/react";

const isDebug = Number(import.meta.env.VITE_APP_DEBUG) === 1;

console.log("isDebug", isDebug);

export default function Viewer({ height, ...props }) {
  return (
    <LazyLoad height={height || "500vh"}>
      <ViewerInner height={height} {...props} />
    </LazyLoad>
  );
}
Viewer.propTypes = {
  name: PropTypes.string,
  height: PropTypes.string,
  path: PropTypes.string,
  frameCount: PropTypes.number,
  startFrame: PropTypes.number,
  enterTween: PropTypes.object,
  exitTween: PropTypes.object,
  canvasSize: PropTypes.array,
  pause: PropTypes.object,
  children: PropTypes.object,
  debug: PropTypes.bool,
};

class ViewerInner extends Component {
  constructor() {
    super();
    console.log("hello constructor");
    //ref
    this.containerRef = createRef(null);
    this.viewerRef = createRef(null);
    this.canvasContainerRef = createRef(null);
    this.loadingWrap = createRef(null);
    this.viewerOffsetRef = createRef(null);
    this.canvasRef = createRef(null);
    this.processingRef = createRef(null);
    this.preProcessingRef = createRef(null);
    this.processBarRef = createRef(null);
    this.sequence = [];
    this.loadedRenderPool = [];
    this.enterTimeline = false;
    this.exitTimeline = false;
    this.loadComplete = false;

    this.playBarTween = false;
    this.playPreBarTween = false;
    this.loadedCount = 0;
    this.progress = 0;
    this.lastFrame = -1;
    this.floatFrame = 0;
    this.frame = 0;
    this.loadedRenderTimeout = null;
    this.poolAnimateDelay = 40;

    this.context = null;
    this.width = 1552;
    this.height = 873;
    this.justScrolled = false;
    this.lastProgress = false;
    this.isBelow = true;
    this.isAbove = false;
  }
  static propTypes = Viewer.propTypes;
  componentWillUnmount() {
    console.error("remove-timeline");
    if (this.timeline) {
      this.timeline.kill(true);
    }
  }

  componentDidMount() {
    this.fullFrameCount = this.props.frameCount;
    this.frame = this.props.startFrame || 0;
    if (this.props.pause) {
      Object.keys(this.props.pause).forEach((index) => {
        this.fullFrameCount += this.props.pause[index];
      });
    }
    this.loadAssets();
    this.canvasRef.current && this.initializeCanvas();

    if (!this.timeline) {
      this.initializeTimeline();
      this.setTimeline();
    }
    if (!this.enterTimeline && this.props.enterTween) {
      this.initializeEnterTween();
    }
    if (!this.exitTimeline && this.props.exitTween) {
      this.initializeExitTween();
    }

    ScrollTrigger.refresh();
  }

  loadImage(index) {
    const img = new Image();

    img.retried = 0;
    img.src = this.getSourcePath(index);
    img.ogSrc = img.src;

    if (this.props.pause && index + "" in this.props.pause) {
      for (var r = this.props.pause[index]; r--; ) {
        this.sequence.push(img);
      }
    }
    this.sequence.push(img);

    img.onload = () => {
      index === 1 && this.renderImageToCanvas(0);
      if (
        this.frame > index &&
        this.timeline &&
        this.timeline.scrollTrigger.isActive
      ) {
        this.poolNewFrames(index - 1);
      }
      // var t = 100 - (this.frame / this.fullFrameCount) * 100 + "%";
      // this.loadingProgress.current.style.width
      this.loadedCount += 1;
      if (this.loadedCount === parseFloat(this.props.frameCount) - 1) {
        this.loadingComplete();
      }
    };
  }

  getSourcePath(index) {
    const defaultPrefix = import.meta.env.VITE_APP_SOURCE;
    return `${defaultPrefix}${this.props.path}/${"".concat(
      index.toString().padStart(4, "0")
    )}.webp`;
  }
  loadAssets() {
    this.loadImage(1);
    setTimeout(() => {
      for (var t = 2; t <= this.props.frameCount; t += 1) {
        this.loadImage(t);
      }
    }, 60);
  }
  loadingComplete() {
    console.log(this.props.path, "loading complete");
    this.loadComplete = true;
    this.isAbove && this.renderImageToCanvas(this.loadedCount - 1);
  }

  initializeCanvas() {
    this.context = this.canvasRef.current.getContext("2d", {
      alpha: false,
      desynchronized: true,
      powerPreference: "high-performance",
    });
    this.context.imageSmoothingEnabled = true;
    this.context.imageSmoothingQuality = "high";
  }
  initializeTimeline() {
    // const openLoading = () => {
    //   gsap.to(this.loadingWrap.current, {
    //     autoAlpha: 1,
    //   });
    // };
    // const closeLoading = () => {
    //   gsap.to(this.loadingWrap.current, {
    //     autoAlpha: 0,
    //   });
    // };
    // closeLoading();
    this.timeline = gsap.timeline({
      scrollTrigger: {
        trigger: this.containerRef.current,
        pin: this.viewerRef.current,
        scrub: 0.66,
        start: "top top",
        end: "bottom bottom",
        ease: "none",
        markers: isDebug,
        onUpdate: (trigger) => {
          //处理processloading
          if (!this.lastProgress) {
            this.lastProgress = trigger.progress;
          } else {
            if (this.lastProgress !== this.progress) {
              this.justScrolled = true;
              this.lastProgress = trigger.progress;
            }
          }
        },
        onScrubComplete: () => {
          this.justScrolled = true;
        },
        onEnter: () => {
          this.isAbove = false;
          console.log(this.props.path, "onEnter");
          this.enterShowElements();
        },
        onEnterBack: () => {
          // openLoading();
          console.log(this.props.path, "onEnterBack");
          this.isBelow = false;
          this.enterShowElements();
        },
        onLeave: () => {
          console.log(this.props.path, "onLeave");
          this.isAbove = true;
          this.leaveHideElements();
        },
        onLeaveBack: () => {
          console.log(this.props.path, "onLeaveBack");
          this.isBelow = true;
          this.leaveHideElements();
        },
      },
    });
  }
  setTimeline() {
    this.timeline.to(this, {
      floatFrame: this.fullFrameCount - 1,
      ease: "none",
      onUpdate: () => {
        this.frame = Math.floor(this.floatFrame);
        if (
          this.lastFrame === this.frame ||
          this.loadedRenderPool.length === 0
        ) {
          this.renderImageToCanvas(this.frame);
        }
      },
    });
  }

  initializeEnterTween() {
    const duration = this.props.enterTween.duration || 1;
    let openPin = false;
    console.warn("this.props.enterTween", duration, this.props.enterTween);
    gsap.set(this.viewerRef.current, {
      yPercent: -100 * duration,
    });
    if (void 0 !== this.props.enterTween.pin) {
      openPin = this.props.enterTween.pin;
    }
    this.enterTimeline = gsap.timeline({
      scrollTrigger: {
        trigger: this.viewerRef.current,
        scrub: true,
        pin: openPin,
        start: function () {
          return "top top";
        },
        end: function () {
          return "top top-=" + window.innerHeight * duration;
        },
      },
    });
    if (this.props.enterTween.to) {
      this.enterTimeline.to(
        this.viewerRef.current,
        Object.assign(
          {
            ease: "none",
          },
          this.props.enterTween.to
        )
      );
    }
    if (this.props.enterTween.from) {
      this.enterTimeline.from(
        this.viewerRef.current,
        Object.assign(
          {
            ease: "none",
          },
          this.props.enterTween.from
        )
      );
    }
  }
  initializeExitTween() {
    console.log(this.props.path, "initializing exit tween ");
    this.exitTimeline = gsap.timeline({
      scrollTrigger: {
        scrub: true,
        trigger: this.containerRef.current,
        pin: this.viewerRef.current,
        onLeave: this.props.exitTween.onLeave,
        onLeaveBack: this.props.exitTween.onLeaveBack,
        onEnterBack: this.props.exitTween.onEnterBack,
        start: function () {
          return "bottom bottom";
        },
        end: function () {
          return "bottom top";
        },
      },
    });

    if (this.props.exitTween.from) {
      this.exitTimeline.from(
        this.viewerRef.current,
        Object.assign(
          {
            ease: "none",
          },
          this.props.exitTween.from
        )
      );
    }
    if (this.props.exitTween.to) {
      this.exitTimeline.to(
        this.viewerRef.current,
        Object.assign(
          {
            ease: "none",
          },
          this.props.exitTween.to
        )
      );
    }
  }

  poolNewFrames(index) {
    this.loadedRenderPool.unshift(index);
    this.loadedRenderPool.sort(function (a, b) {
      return b - a;
    });
    this.animatePool();
  }

  animatePool() {
    if (!this.loadedRenderTimeout && this.loadedRenderPool.length) {
      this.loadedRenderTimeout = setTimeout(() => {
        this.loadedRenderTimeout = false;
        var poolFrame = this.loadedRenderPool[this.loadedRenderPool.length - 1];
        if (poolFrame <= this.frame) {
          var remainFrame = this.loadedRenderPool.pop();
          this.renderImageToCanvas(remainFrame);
          this.animatePool();
        }
        if (this.frame < poolFrame) {
          this.loadedRenderPool = [];
        }
      }, this.poolAnimateDelay);
    }
  }
  renderImageToCanvas(index) {
    if (this.sequence[index]) {
      if (this.context.drawImage) {
        this.context.drawImage(this.sequence[index], 0, 0);
        this.lastFrame = index;
        this.handleSyncProessBar(index);
      } else {
        this.initializeCanvas();
      }
    }
  }

  handleSyncProessBar(index) {
    const progressingPreload =
      100 - (this.frame / this.fullFrameCount) * 100 + "%";
    const progressing = 100 - (index / this.fullFrameCount) * 100 + "%";
    // console.log("handleSyncProessBar", this.processingRef.current);
    if (this.preProcessingRef.current) {
      this.playPreBarTween = gsap.to(this.preProcessingRef.current, {
        duration: 0.05,
        right: progressingPreload,
        ease: "none",
      });
      this.playBarTween = gsap.to(this.processingRef.current, {
        duration: 0.05,
        right: progressing,
        ease: "none",
      });
    }
  }

  enterShowElements() {
    gsap.set(this.processBarRef.current, {
      autoAlpha: 1,
    });
  }
  leaveHideElements() {
    gsap.set(this.processBarRef.current, {
      autoAlpha: 0,
    });
  }

  render() {
    return (
      <>
        <div
          className={`processBar ${this.props.name}`}
          ref={this.processBarRef}
          css={css`
            position: fixed;
            top: calc(100vh - 4px);
            // top: calc(var(--vh, 1vh) * 100 - 4px);
            left: 0;
            width: 100vw;
            max-width: 100%;
            border-top: 1px solid rgba(33, 33, 44, 0.6);
            background-color: rgba(17, 17, 34, 0.6);
            height: 4px;
            z-index: 9;
          `}
        >
          <div
            css={css`
              position: absolute;
              width: auto;
              height: 4px;
              left: 0;
              bottom: 0;
              z-index: 10;
              background-color: rgba(120, 120, 163, 0.33);
            `}
            ref={this.preProcessingRef}
          ></div>
          <div
            css={css`
              position: absolute;
              width: auto;
              height: 4px;
              left: 0;
              bottom: 0;
              z-index: 10;
              background-color: hsla(0, 0%, 79.2%, 0.5);
            `}
            ref={this.processingRef}
          ></div>
        </div>

        <div
          css={css`
            position: relative;
            margin: auto;
            text-align: center;
            pointer-events: none;
            max-width: 100vw;
          `}
          ref={this.containerRef}
          style={{ height: this.props.height || "500vh" }}
        >
          <div ref={this.viewerRef}>
            <div style={{ overflow: "hidden" }}>
              <canvas
                css={css`
                  width: auto;
                  margin-left: 50%;
                  transform: translateX(-50%);
                  height: 100vh;
                  height: calc(var(--vh, 1vh) * 100);
                `}
                ref={this.canvasRef}
                width={this.width}
                height={this.height}
              ></canvas>
            </div>
          </div>
          <>{this.props.children}</>
        </div>
      </>
    );
  }
}
