import { useState, useEffect, useRef } from "react";
import { Howl, Howler } from 'howler';
import { invoke } from "@tauri-apps/api/core";
import { listen } from '@tauri-apps/api/event';
import "./App.css";
import { OSC_ADDRESS } from "./utils/constant";
import { Light } from "./utils/light";


const DefaultFadeDuration = 3; // 1 second
const CLIENT_COUNT = 13;
const CUE_FILE = 'cuelist_1009.json';
// const OSC_BROADCAST_IP='192.168.51.255:8000';
// const SCS_IP='192.168.51.100:58200';
const OSC_BROADCAST_IP = '192.168.234.255:8000';
const UNITY_BROADCAST_IP = '192.168.234.255:9000';
const SCS_IP = '192.168.234.41:58200';


const CueType = {
  Bg: 'bg',
  Announce: 'announce',
  Light: 'light'
}
const EmojiType = {
  bg: '🎵',
  announce: '📢',
  light: '💡'
}

function App() {

  const [cuelist, setCuelist] = useState([]);
  const [currentCue, setCurrentCue] = useState(null);
  const [fadeDuration, setFadeDuration] = useState(DefaultFadeDuration); // Default fade duration in seconds
  const [clientStatus, setClientStatus] = useState({});

  const refCue = useRef(null);
  // const refNextCue = useRef(null);
  const refLight = useRef();
  const refDuration = useRef();
  const refTotalTime=useRef();
  const refTimer = useRef();


  function sendOsc(addr, message, id) {

    invoke('send_osc_message', {
      key: addr,
      message: `${id}#${message}`,
      host: '0.0.0.0:0',
      target: OSC_BROADCAST_IP,
    });

  }


  function sendOscToSound(addr, message) {

    invoke('send_osc_message', {
      key: addr,
      message,
      host: '0.0.0.0:0',
      target: SCS_IP,
    });

  }

  function onOsc(message) {

    const addr = message.addr;
    const [id, status, name] = message.args[0]?.split('#');
    // console.log('receive osc:', id, status,name);

    switch (addr) {
      case OSC_ADDRESS.CLIENT_STATUS:

        setClientStatus(prev => ({
          ...prev,
          [id]: {
            status,
            name,
            timestamp: new Date().toLocaleTimeString(),
          }
        }));
        break;
      case OSC_ADDRESS.CLIENT_DURATION:
        setClientStatus(prev => ({
          ...prev,
          [id]: {
            ...prev[id],
            duration: status,
            timestamp: new Date().toLocaleTimeString(),
          }
        }));
        break;
      case OSC_ADDRESS.CLIENT_INPUT:
        setClientStatus(prev => ({
          ...prev,
          [id]: {
            ...prev[id],
            input: status,
            timestamp: new Date().toLocaleTimeString(),
          }
        }));
        break;
    }
  }

  function getTotalLeftTime(id){
    const cue=cuelist.find(el=>el.id==id);
    const index=cuelist.indexOf(cue);
    if(index==-1) return;

    let sum=0;
    for(var i=index;i<cuelist.length;++i){
      if(cuelist[i].duration) sum+=cuelist[i].duration;
    } 

    return sum;

  }


  function playCue({ id, name, description, type, auto, audioFile, ...props }) {

    console.log('Playing cue:', { id, name, description, type, auto, audioFile, ...props });
    setCurrentCue({ id, name, description, type, auto, audioFile, ...props });

    // Handle other cue types and properties here
    if (props.audioCue) {
      sendOscToSound(OSC_ADDRESS.SCS_GO_CUE, props.audioCue);
    }

    if (props.clientCue) {
      sendOsc(OSC_ADDRESS.PLAYCUE, props.clientCue, 'all');
    }

    if (props.lightCue) {
      // sendOsc(OSC_ADDRESS.CLIENT_INPUT, props.lightCue);

      if (props.lightCue == 'fade_in_light'){
        refLight.current.fadeIn(props.lightDuration,  props.syncUnityLight?UNITY_BROADCAST_IP:null); // Fade in light for conversation start
      }
      if (props.lightCue == 'fade_out_light'){
        refLight.current.fadeOut(props.lightDuration, props.syncUnityLight?UNITY_BROADCAST_IP:null); // Fade out light for conversation end
      }

    }
    if (props.reset) {
      resetLight();
    }

     if (refTimer.current) {
        clearTimeout(refTimer.current);
      }

    if (props.duration) {

      // calculate last time
      let totalLefTime=getTotalLeftTime(id);
      
      // Clear any existing timer
     
      // Store the end time of the timer
      const totalEndTime=new Date().getTime()+totalLefTime*1000;
      const endTime = new Date().getTime() + (props.duration * 1000);

      // Function to update the timer
      const tick = () => {
        const now = new Date().getTime();
        const timeLeft = endTime - now;
        const totalTimeLeft=totalEndTime-now;
        //console.log(totalTimeLeft);

        if (timeLeft <= 0) {
          // Timer has finished
          clearTimeout(refTimer.current);
          refTimer.current = null;

          if (auto) {
            // const next = cuelist.find(c => c.id === id + 1);
            const index = cuelist.findIndex(c => c.id === id);
            const next = cuelist[index + 1];
            if (!next) return;

            console.log('Auto play next cue:', next);
            playCue(next);
          }
          return;
        }

        // Update the displayed duration
        refDuration.current.innerText = secondToTime(timeLeft / 1000);
        refTotalTime.current.innerText=`${secondToTime(totalTimeLeft/1000)} / ${secondToTime(totalLefTime)}`;



        // Call the next tick
        refTimer.current = setTimeout(tick, 100);
      };

      // Start the timer
      console.log('Start timer:', props.duration);
      refTimer.current = setTimeout(tick, 100);
    }

  }
  function stop() {
    console.log('Stop all');

    sendOsc(OSC_ADDRESS.STOPCUE, '', 'all');
    sendOscToSound(OSC_ADDRESS.SCS_FADE_ALL, '');

    // cear timer
    if (refTimer.current) clearInterval(refTimer.current);
  }
  function reset() {
    console.log('Reset all');

    sendOsc(OSC_ADDRESS.RESETCUE, '', 'all');
    sendOscToSound(OSC_ADDRESS.SCS_STOP_ALL, '');
    resetLight();
  } 
  function resetLight(){
    refLight.current.set(1);
    invoke('send_osc_message', {
          key: '/amplitude',
          message: 1.0.toString(),
          host:`0.0.0.0:0`,
          target: UNITY_BROADCAST_IP,
    });

  }
  function secondToTime(seconds) {
    const minutes = Math.floor(seconds / 60);
    const secs = Math.floor(seconds % 60);
    return `${minutes.toString().padStart(2,'0')}:${secs < 10 ? '0' : ''}${secs}`;
  }
  function getAudioDuration() {

    const type = refCue.current?.type;
    // console.log('getAudioDuration', type, refCue.current);
    // switch(type) {
    //   case CueType.Bg:
    if (refAudioBg.current) {
      return `${secondToTime(refAudioBg.current.seek())} / ${secondToTime(refAudioBg.current.duration())} ${refAudioBg.current?.volume()}`;
    } else {
      return 'N/A';
    }
    //   case CueType.Announce:
    //     return refAudioAnnounce.current ? `${secondToTime(refAudioAnnounce.current.seek())} / ${secondToTime(refAudioAnnounce.current.duration())}` : 'N/A';
    //   default:
    //     return 'N/A';
    // }


  }
  useEffect(() => {
    if (currentCue) {
      refCue.current = currentCue;
      // console.log('Current Cue:', currentCue);
    }
  }, [currentCue]);
  useEffect(() => {
    fetch(`/${CUE_FILE}`)
      .then(response => response.json())
      .then(data => {
        console.log('Cuelist data:', data);
        setCuelist(data.cuelist);
      })
      .catch(error => {
        console.error('Error fetching cuelist:', error);
      });


    listen('osc_message', (event) => {
      // console.log(`Received OSC message: ${JSON.stringify(event.payload)}`);
      onOsc(event.payload);
    });



  }, []);

  return (
    <main className="overflow-y-auto flex flex-row gap-8 p-4 min-h-screen">
      <section className="flex-1 flex flex-col gap-4">
        <section className="grid grid-cols-4 items-center gap-2">
            <div className="grid grid-cols-1 font-bold items-stretch justify-between bg-pink-300 gap-4 p-2">
              <div className="text-4xl">{currentCue ? `${currentCue.name}` : 'None'}</div>
              <div className="text-4xl" ref={refDuration}></div>
            </div>
          <button className="text-4xl self-stretch" onClick={stop}>stop all</button>
          <button className="text-4xl self-stretch" onClick={reset}>reset all</button>
          <Light ref={refLight} />
          <span className="flex flex-col gap-2 items-stretch hidden">
            <label htmlFor="fade_duration">Fade Duration</label>
            <input type="range" id="fade_duration" min="0" max="30" step="0.1" defaultValue={DefaultFadeDuration} className="slider"
              onChange={(e) => {
                const value = parseFloat(e.target.value);
                setFadeDuration(value);
              }}></input>
            <span className="text-2xl">{fadeDuration}s</span>
          </span>
        </section>
        <table className="border-collapse w-full **:px-1 border-green-500 border-4">
          <thead className="bg-green-500">
            <tr className="text-left lowercase font-[900]">
              {/* <th>ID</th> */}
              <th></th>
              <th>Name</th>
              <th>Description</th>
              <th>Type</th>
              <th>Auto</th>
              <th>Due</th>
              <th>Light</th>
              <th>Audio</th>
              <th>Client</th>
            </tr>
          </thead>
          <tbody>
            {cuelist?.map(({ id, name, description, type, auto, ...props }, index) => (
              <tr key={id} className={currentCue?.id === id ? 'bg-green-200' : `${props.debug && 'text-red-500'}`}>
                <td className="flex flex-row gap-2">
                  <button
                    onClick={() => {
                      playCue({ id, name, description, type, auto, ...props });
                    }}>go</button>
                </td>
                <td>{name}</td>
                <td className="text-sm">{description}</td>
                <td>{EmojiType[type]}</td>
                <td>{auto ? '⤵️' : ''}</td>
                <td>{props.duration}</td>
                <td className="text-sm">{props.lightCue && `L${props.lightCue}`}</td>
                <td className="text-sm">{props.audioCue && `S${props.audioCue}`}</td>
                <td className={`text-sm ${props.clientCue && 'bg-green-300 rounded-full text-center font-[900]'}`}>{props.clientCue || ''}</td>
              </tr>
            ))}
          </tbody>
        </table>
        <div className="text-6xl text-center font-bold bg-blue-500 text-white p-4" ref={refTotalTime}></div>          
            
      </section>
      <table className="flex-1 text-sm">
        <thead className="bg-blue-500">
          <tr className="text-center lowercase font-[900]">
            <th>id</th>
            <th>status</th>
            <th>cue</th>
            <th>input</th>
            <th>due</th>
            <th>timestamp</th>
            <th></th>
          </tr>
        </thead>
        <tbody>
          {Array.from(Array(CLIENT_COUNT).keys()).map((i) => {
            const id = (i + 1).toString();//.padStart(2,'0');
            const log = clientStatus[id.toString()];
            return (
              <tr key={id} className="text-left lowercase">
                <td className="font-[900]">{id}</td>
                {<>
                  <td>{log?.status}</td>
                  <td>
                    <span className="bg-green-200 rounded-full p-2">{log?.name}</span>
                  </td>
                  <td className="flex-1 text-[9px] min-w-[80px]">
                    {log?.input?.substr(log.input.length - 20)?.split('').map((ch, i) => i % 3 < 1 ? ch : '*').join('')}
                  </td>
                  <td>{log?.duration}</td>
                  <td className="text-[12px]">{log?.timestamp}</td>
                </>}
                <td className="flex flex-row justify-end gap-1 p-1">
                  <button onClick={() => sendOsc(OSC_ADDRESS.STOPCUE, '', id)}>stop</button>
                  <button onClick={() => sendOsc(OSC_ADDRESS.RESETCUE, '', id)}>reset</button>
                  {cuelist?.filter(c => c.clientCue && !c.debug).map((c, index) => (
                    <button key={`${id}${c.clientCue}${index}`} onClick={() => {
                      sendOsc(OSC_ADDRESS.PLAYCUE, c.clientCue, id);
                    }}>{c.clientCue}</button>
                  ))}
                </td>
              </tr>
            );
          })}
        </tbody>
      </table>
    </main>
  );
}

export default App;
