window.addEventListener("DOMContentLoaded", () => {
  //Create progress bar
  const book_bar = new ProgressBar();

  //Pat Lilac
  const websocket = new ReconnectingWebSocket("wss://lilac.thewisewolf.dev/tracker/timer/ws");
  communicate(websocket, book_bar);

});


class ProgressBar {
  constructor() {
    this.current_progress = 25;
    this.max_progress = 45;
    this.anim_progress = 0;
    this.animating = false;
    this.simming = false;
    this.target_element = document.getElementById("progressBar");
    this.bar_back = document.getElementById("barBack");
    this.bar_front = document.getElementById("barFront");
    //Pre-load and hold background images
    let image_cache = [];
    for (let i = 0; i < 46; i++) {
      const img_fetcher = new Image();
      let index_string = i.toString();
      if (index_string.length < 2) {
        index_string = "0" + index_string;
      }
      img_fetcher.src = "src/Books/BookBar_v8_f00" + index_string + ".png";
      image_cache.push(img_fetcher);
    };
  }

  //SET NUMBER OF COLORED BOOKS

  //Set current campaign progress
  setProgress(val) {
    if (Number.isInteger(val)
        && (val >= 0)
        && (val <= this.max_progress)
        && (val != this.current_progress)) {
      this.current_progress = val;
    this.anim_progress = 0;
    } else {
      console.warn("Progress update failed.")
    }
  }

  //Render current animation state to the screen
  renderProgress(val) {
    if (Number.isInteger(val)
        && (this.target_element != null)
        && (this.bar_back != null)
        && (this.bar_front != null)
        && (val >= 0)
        && (val <= this.current_progress)
        && (val <= this.max_progress)) {
      let val_string = val.toString();
      if (val_string.length < 2) {
        val_string = "0" + val_string;
      }
      this.bar_back.style.backgroundImage = "url(src/Books/BookBar_v8_f00" + val_string + ".png)";
      setTimeout(this.antiFlash.bind(this), 150, val_string, val);
    } else {
      console.warn("Progress render failed.")
    }
  }

  //Prevent image flicker by pre-loading a version of the image then rendering the top image with a delay
  antiFlash(val_string, new_val) {
    this.bar_front.style.backgroundImage = "url(src/Books/BookBar_v8_f00" + val_string + ".png)";
    //Increment the animation ticker (plus an extra round of safety checks)
    if ((new_val >= 0)
        && (new_val <= this.current_progress)
        && (this.anim_progress <= this.max_progress)
        && (new_val <= this.max_progress)) {
      this.anim_progress = new_val;
    }
    else {
      console.warn("Anim antiFlash render is out of range.");
    }
  }

  //Progress Animation
  animProgress() {
    //animate
    if (this.animating == true) {
      if ((this.anim_progress >= 0)
        && (this.anim_progress < this.current_progress)
        && (this.anim_progress < this.max_progress)) {
      this.renderProgress(this.anim_progress + 1);
      setTimeout(this.animProgress.bind(this), 500);
      }
      //loop
      if (this.anim_progress == this.current_progress) {
        setTimeout(this.renderProgress.bind(this, 0), 5000);
        setTimeout(this.animProgress.bind(this), 5500);
      }
    }
  }

  //Toggle animation on
  animStart() {
    if (this.animating != true){
      this.animating = true;
      this.animProgress();
    } else {
      console.warn("Animation already running");
    }
  }

  //Toggle animation off
  animEnd() {
    this.animating = false;
  }

  //TEST SIMULATION
  simProgress() {
    //sim
    if (this.simming == true) {
      if (this.current_progress < this.max_progress) {
      this.setProgress(this.current_progress + 1);
      setTimeout(this.simProgress.bind(this), 38500);
      }
      //loop
      if (this.current_progress == this.max_progress) {
        setTimeout(() => this.setProgress(0), 10000);
        setTimeout(this.simProgress.bind(this), 95500);
      }
    }
  }

  simStart() {
    console.log("starting");
    this.simming = true;
    this.simProgress();
    console.log("started");
  }

  simEnd() {
    console.log("ending");
    this.simming = false;
  }
  
}

class ReconnectingWebSocket {
  constructor(url) {
    this.url = url;
    this.ws = null;
    this.attempt = 0;
    this.connect();

    this.closing = false;
    this.onopen = null;
    this.onmessage = null;
    this.onclose = null;
    this.onerror = null;
  }

  connect() {
    this.ws = new WebSocket(this.url);

    this.ws.onopen = () => {
      console.log('WebSocket connecting');
      this.attempt = 0;
      if (this.onopen != null) {
        this.onopen();
      }
    };

    this.ws.onmessage = (msg) => {
      console.log('Received:', msg.data);
      if (this.onmessage != null) {
        this.onmessage(msg);
      }
    };

    this.ws.onclose = () => {
      if (this.closing) {
        console.log('Closing Websocket.')
      } {
        console.warn('WebSocket closed. Reconnecting...');
        this.reconnect();
      }
    };

    this.ws.onerror = (err) => {
      console.error('WebSocket error:', err);
      if (this.onerror != null) {
        this.onerror();
      }
      this.close();
    };
  }

  reconnect() {
    const delay = this.getBackoffDelay(this.attempt);
    console.log(`Reconnecting in ${delay}ms`);

    setTimeout(() => {
      this.attempt++;
      this.connect();
    }, delay);
  }

  close() {
    this.closing = true;
    this.ws.close();
    if (this.onclose != null) {
      this.onclose();
    }
  }

  getBackoffDelay(attempt) {
    const base = 500; // 0.5 second
    const max = 30000; // 30 seconds
    const jitter = Math.random() * 1000;
    return Math.min(base * 2 ** attempt + jitter, max);
  }

  send(data) {
    if (this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(data);
    } else {
      console.warn('WebSocket not connected');
    }
  }
}


function communicate(websocket, book_bar) {
  console.log("Communicating");
  const params = new URLSearchParams(window.location.search);
  // INITIALIZE CLASS HERE
  //queue animation start after a few seconds to allow for initial status from server
  setTimeout(book_bar.animStart.bind(book_bar),5000);

  websocket.onopen = () => {
    websocket.send(
      JSON.stringify(
        {
          type: "init",
          channel: "CampaignRewards",
          community: params.get('community')
        }
      )
    );
  };

  websocket.onmessage = ({ data }) => {
    console.log("messaged");
    const event = JSON.parse(data);
    switch (event.type) {
      case "DO":
        let args = event.args;

        // Call the specified method
        switch (event.method) {
          case "setTimer":
            //do something with the new data
            console.log("set timer: " + args.reward_progress);
            book_bar.setProgress(args.reward_progress);
            //animEnd();
            //setTimeout(book_bar.setProgress(args.reward_progress), 7500);
            //setTimeout(book_bar.animStart, 7800);
            break;
          case "noTimer":
            //do something default
            console.log("no timer: " + args);
            break;
          case "endTimer":
            //do something for the final time
            console.log("end timer: " + args);
            //book_bar.renderProgress(args.reward_progress);
            //book_bar.animEnd();
            //setTimeout(book_bar.renderProgress(args.reward_progress), 7500);
            //setTimeout(book_bar.animStart, 7800);
            break
          default:
            throw new Error(`Unsupported method requested: ${event.method}.`)
        }
        break;
      default:
        throw new Error(`Unsupported event type: ${event.type}.`);
    }
  };
}
