import React from "react";
import { connect } from "react-redux";

import Controls from "./components/controls";
import Visualizer from "./components/visualizer";
import { State } from "./store";

type AudioState = {
  audioContext: AudioContext;
  audioSource: HTMLAudioElement;
  audioSourceNode: MediaElementAudioSourceNode;
  audioVolume: GainNode;
};

type MusicPlayerProps = {
  playing: boolean;
  muted: boolean;
  source?: string;
};

class MusicPlayer extends React.Component<MusicPlayerProps, {}> {
  private audioState: AudioState;

  constructor(props: MusicPlayerProps) {
    super(props);

    let context = new AudioContext();
    let source = new Audio();
    let sourceNode = context.createMediaElementSource(source);
    let volume = context.createGain();

    sourceNode.connect(volume);
    volume.connect(context.destination);

    this.audioState = {
      audioContext: context,
      audioSourceNode: sourceNode,
      audioSource: source,
      audioVolume: volume,
    };
  }

  render() {
    return (
      <div id="player" style={{ height: "100%", width: "100%" }}>
        <Visualizer
          audioContext={this.audioState.audioContext}
          audioSource={this.audioState.audioSourceNode}
        />
        <Controls />
      </div>
    );
  }

  componentDidUpdate() {
    let context = this.audioState.audioContext;
    let source = this.audioState.audioSource;
    let volume = this.audioState.audioVolume;

    // First, set the audio source (if it changed)
    if (this.props.source && source.src != this.props.source) {
      source.src = this.props.source;
    }

    if (this.props.playing) {
      source
        .play()
        .then(() => {
          console.info("Started playing audio");
        })
        .catch((error) => {
          console.error(`Could not play audio: ${error}`);
        });
    } else {
      source.pause();
    }

    if (!this.props.muted) {
      volume.gain.setValueAtTime(1, context.currentTime);
    } else {
      volume.gain.setValueAtTime(0, context.currentTime);
    }
  }
}

function mapStateToProps(state: State): MusicPlayerProps {
  return {
    playing: state.musicState.playing,
    muted: state.musicState.muted,
    source: state.musicState.source,
  };
}

export default connect(mapStateToProps)(MusicPlayer);