import { useState, useEffect, useMemo, useCallback, useRef } from 'react';
import { useSelector } from 'react-redux';
import { selectActiveSession, selectInSession } from '../store/features/activeSessionSlice';
import { ChannelGroupIds, CategoryGroupIds, MIX_MODES, MIDI_TRACK } from '../helpers/globals.js';
import useVuHelpers from './useVuHelpers.js';
import useFaderHelpers from './useFaderHelpers.js';
import useSessionHelper from './useSessionHelper.js';
import usePanHelpers from './usePanHelpers.js';
import { useJamServerContext } from '../context/JamServerContext.js';
import { useGlobalContext } from '../context/GlobalContext.js';
import { useVuContext } from '../context/VuContext.js';
import { getAvatarUrl, getInstrumentIcon45, getInstrumentIcon24 } from '../helpers/utils.js';


const useMixerHelper = () => {
  const allMixersRef = useRef({});

  const [chatMixer, setChatMixer] = useState(null);
  const [broadcastMixer, setBroadcastMixer] = useState(null);
  const [recordingMixer, setRecordingMixer] = useState(null);
  const [recordingTrackMixers, setRecordingTrackMixers] = useState([]);
  const [backingTrackMixers, setBackingTrackMixers] = useState([]);
  const [jamTrackMixers, setJamTrackMixers] = useState([]);
  const [metronomeTrackMixers, setMetronomeTrackMixers] = useState([]);
  const [adhocTrackMixers, setAdhocTrackMixers] = useState([]);
  const [backingTracks, setBackingTracks] = useState([]);
  const [jamTracks, setJamTracks] = useState([]);
  const [recordedTracks, setRecordedTracks] = useState([]);
  const [metronome, setMetronome] = useState(null);
  const [mediaSummary, setMediaSummary] = useState({});


  const [session, setSession] = useState(null);
  const [masterMixers, setMasterMixers] = useState([]);
  const [personalMixers, setPersonalMixers] = useState([]);
  const [metro, setMetro] = useState(false);
  const [noAudioUsers, setNoAudioUsers] = useState([]);
  const [clientsWithAudioOverride, setClientsWithAudioOverride] = useState([]);
  const [mixMode, setMixMode] = useState(MIX_MODES.PERSONAL);
  const [mixersByResourceId, setMixersByResourceId] = useState({});
  const [mixersByTrackId, setMixersByTrackId] = useState({});
  const [allMixers, setAllMixers] = useState({});
  const [currentMixerRangeMin, setCurrentMixerRangeMin] = useState(null);
  const [currentMixerRangeMax, setCurrentMixerRangeMax] = useState(null);
  const mediaTrackGroups = useMemo(() => [ChannelGroupIds.MediaTrackGroup, ChannelGroupIds.JamTrackGroup,
  ChannelGroupIds.MetronomeGroup], []);
  const muteBothMasterAndPersonalGroups = useMemo(() => [ChannelGroupIds.AudioInputMusicGroup, ChannelGroupIds.MidiInputMusicGroup, ChannelGroupIds.MediaTrackGroup,
  ChannelGroupIds.JamTrackGroup, ChannelGroupIds.MetronomeGroup], []);
  const [simulatedMusicCategoryMixers, setSimulatedMusicCategoryMixers] = useState({});
  const [simulatedChatCategoryMixers, setSimulatedChatCategoryMixers] = useState({});

  // Phase 4: Replace CurrentSessionContext with Redux
  const currentSession = useSelector(selectActiveSession);
  const inSession = useSelector(selectInSession);
  const { jamClient, isConnected, server } = useJamServerContext();
  const { updateVU3 } = useVuContext();
  const { getParticipant } = useSessionHelper();
  const { trackVolumeObject, setTrackVolumeObject } = useGlobalContext();
  const faderHelpers = useFaderHelpers();
  const panHelpers = usePanHelpers();

  const logger = console; // for now
  const context = window;

  const isReady = useRef(false);

  useEffect(() => {
    allMixersRef.current = allMixers;
    // console.log("_XDEBUG_ useMixerHelper: allMixersRef updated", allMixersRef.current);
  }, [allMixers]);

  const getMixer = (mixerId, mode) => {
    mode = mode || mixMode;
    return allMixersRef.current[(mode ? 'M' : 'P') + mixerId];
  }

  const fillTrackVolumeObject = useCallback((mixerId, mode, currentAllMixers, broadcast = true) => {
    const mixer = getMixer(mixerId, mode, currentAllMixers);
    if (mixer == null) {
      console.error("MixerHelper: fillTrackVolumeObject: unable to find mixer with ID: #{mixerId}, mode: #{mode}");
      return null;
    }

    setTrackVolumeObject({
      id: mixer.id,
      _id: mixer._id,
      groupID: mixer.group_id,
      clientID: mixer.client_id,
      broadcast: broadcast,
      master: mixer.master,
      monitor: mixer.monitor,
      mute: mixer.mute,
      name: mixer.name,
      record: mixer.record,
      volL: mixer.volume_left,
      pan: mixer.pan,
      mediaType: mixer.media_type,
      isJamTrack: mixer.is_jam_track,
      isMetronome: mixer.is_metronome,
      volR: mixer.volume_left,
      loop: mixer.loop
    });

    setCurrentMixerRangeMin(mixer.range_low);
    setCurrentMixerRangeMax(mixer.range_high);
    return mixer;
  }, [getMixer]);

  const setMixerVolume = useCallback(async (mixer, volumePercent, relative, originalVolume, controlGroup) => {
    const newVolume = faderHelpers.convertPercentToAudioTaper(volumePercent);

    let updatedTrackVolumeObject;
    if (relative) {
      updatedTrackVolumeObject = {
        ...trackVolumeObject,
        volL: trackVolumeObject.volL + (newVolume - originalVolume),
        volR: trackVolumeObject.volR + (newVolume - originalVolume),
      };

      // Apply clamping
      if (updatedTrackVolumeObject.volL < -80) updatedTrackVolumeObject.volL = -80;
      if (updatedTrackVolumeObject.volL > 20) updatedTrackVolumeObject.volL = 20;
      if (updatedTrackVolumeObject.volR < -80) updatedTrackVolumeObject.volR = -80;
      if (updatedTrackVolumeObject.volR > 20) updatedTrackVolumeObject.volR = 20;
    } else {
      updatedTrackVolumeObject = {
        ...trackVolumeObject,
        volL: newVolume,
        volR: newVolume,
      };
    }

    // Update state
    setTrackVolumeObject(updatedTrackVolumeObject);

    // Use the computed object for jamClient call
    if (controlGroup != null) {
      const controlGroupsArg = mixer.mode === MIX_MODES.PERSONAL ? 0 : 1;
      console.log("setMixerVolume: setting session mixer category playout state for controlGroup", controlGroup, "controlGroupsArg", controlGroupsArg, "volume", updatedTrackVolumeObject.volL);
      await jamClient.setSessionMixerCategoryPlayoutState(controlGroup === 'music', controlGroupsArg, updatedTrackVolumeObject.volL);
    } else {
      console.log("setMixerVolume: setting session mixer volume for mixer", mixer.id, "mode", mixer.mode, "volume", updatedTrackVolumeObject.volL);
      await jamClient.SessionSetTrackVolumeData(mixer.id, mixer.mode, updatedTrackVolumeObject);
    }
  }, [trackVolumeObject, faderHelpers, jamClient]);

  const mediaMixers = useCallback((masterMixer, isOpener, currentAllMixers) => {
    const personalMixer = isOpener ? getMixerByResourceId(masterMixer.rid, MIX_MODES.PERSONAL, currentAllMixers) : masterMixer;
    const personalVuMixer = isOpener ? personalMixer : masterMixer;
    return {
      isOpener: isOpener,
      master: {
        mixer: masterMixer,
        muteMixer: masterMixer,
        vuMixer: masterMixer
      },
      personal: {
        mixer: personalMixer,
        muteMixer: personalMixer,
        vuMixer: personalVuMixer
      }
    };
  }, []);

  const updateMixerData = useCallback((session, masterMixers, personalMixers, metro, noAudioUsers, clientsWithAudioOverride, mixMode) => {
    //console.debug("useMixerHelper: updateMixerData called", { session, masterMixers, personalMixers, mixMode });

    setSession(session);  //sessin is set to sessionHelper
    setMasterMixers(masterMixers);
    setPersonalMixers(personalMixers);
    setMetro(metro);
    setNoAudioUsers(noAudioUsers);
    setClientsWithAudioOverride(clientsWithAudioOverride);
    setMixMode(mixMode);
  }, []);

  useEffect(() => {
    if (!session || masterMixers.length === 0 || personalMixers.length === 0) return;
    organizeMixers();
  }, [session, masterMixers, personalMixers]);

  const organizeMixers = () => {
    const newAllMixers = {};
    const newMixersByResourceId = {};
    const newMixersByTrackId = {};

    for (const masterMixer of masterMixers) {
      newAllMixers['M' + masterMixer.id] = masterMixer;

      const mixerPair = {};
      newMixersByResourceId[masterMixer.rid] = mixerPair;
      newMixersByTrackId[masterMixer.id] = mixerPair;
      mixerPair.master = masterMixer;
    }

    for (const personalMixer of personalMixers) {
      newAllMixers['P' + personalMixer.id] = personalMixer;

      let mixerPair = newMixersByResourceId[personalMixer.rid];
      if (!mixerPair) {
        if (personalMixer.group_id !== ChannelGroupIds.MonitorGroup) {
          logger.warn("there is no master version of ", personalMixer);
        }
        mixerPair = {};
        newMixersByResourceId[personalMixer.rid] = mixerPair;
      }
      newMixersByTrackId[personalMixer.id] = mixerPair;
      mixerPair.personal = personalMixer;
    }

    //console.log("useMixerHelper: setting all mixers", newAllMixers);
    setAllMixers(prev => {
      return { ...prev, ...newAllMixers };
    });
    setMixersByResourceId(prev => {
      return { ...prev, ...newMixersByResourceId };
    });
    setMixersByTrackId(prev => {
      return { ...prev, ...newMixersByTrackId };
    });

    const newChatMixer = resolveChatMixer(newMixersByResourceId, newAllMixers);
    setChatMixer(newChatMixer);

    
  }

  useEffect(() => {
    if (!session || Object.keys(allMixers).length === 0) return;
    groupTypes(allMixers);
  }, [session, allMixers]);

  useEffect(() => {
    if (Object.keys(allMixers).length > 0 && !isReady.current) {
      // console.log("useMixerHelper: isReady set to true");
      isReady.current = true;
    }
  }, [allMixers, isReady]);

   const getMixerByTrackId = useCallback((trackId, mode) => {
    const mixerPair = mixersByTrackId[trackId];

    if (!mixerPair) return null;

    if (mode === undefined) {
      return mixerPair;
    } else {
      if (mode === MIX_MODES.MASTER) {
        return mixerPair.master;
      } else {
        return mixerPair.personal;
      }
    }
  }, [mixersByTrackId]);

    const groupedMixersForClientId = useCallback((clientId, groupIds, usedMixers, mixMode, currentAllMixers) => {
    const foundMixers = {};
    const mixers = mixMode === MIX_MODES.MASTER ? masterMixers : personalMixers;

    for (const mixer of mixers) {
      if (!mixer) {
        continue;
      }

      if (mixer.client_id === clientId) {
        for (const groupId of groupIds) {
          if (mixer.group_id === groupId) {
            if ((mixer.groupId !== ChannelGroupIds.UserMusicInputGroup) && !(mixer.id in usedMixers)) {
              let mixers = foundMixers[mixer.group_id];
              if (!mixers) {
                mixers = [];
                foundMixers[mixer.group_id] = mixers;
              }
              mixers.push(mixer);
            }
          }
        }
      }
    }

    return foundMixers;
  }, [masterMixers, personalMixers]);

  const findMixerForTrack = useCallback((client_id, track, myTrack, mode) => {
    let mixer = null;
    let oppositeMixer = null;
    let vuMixer = null;
    let muteMixer = null;

    if (myTrack) {
      mixer = getMixerByTrackId(track.client_track_id, mode);
      vuMixer = mixer;
      muteMixer = mixer;

      if (mixer && (mixer.group_id !== ChannelGroupIds.AudioInputMusicGroup && mixer.group_id !== ChannelGroupIds.MidiInputMusicGroup)) {
        logger.error("found local mixer that was not of groupID: AudioInputMusicGroup", mixer);
      }

      if (mixer) {
        oppositeMixer = getMixerByTrackId(track.client_track_id, !mode);

        if (mode === MIX_MODES.PERSONAL) {
          muteMixer = oppositeMixer;
        }

        if (!oppositeMixer) {
          logger.error("unable to find opposite mixer for local mixer", mixer);
        } else if (oppositeMixer.group_id !== ChannelGroupIds.AudioInputMusicGroup && oppositeMixer.group_id !== ChannelGroupIds.MidiInputMusicGroup) {
          logger.error("found local mixer in opposite mode that was not of groupID: AudioInputMusicGroup", mixer, oppositeMixer);
        }
      } else {
        logger.debug("local track is not present: ", track, allMixers);
      }
    } else {
      switch (mode) {
        case MIX_MODES.MASTER:
          mixer = getMixerByTrackId(track.client_track_id, MIX_MODES.MASTER);

          if (mixer && (mixer.group_id !== ChannelGroupIds.PeerAudioInputMusicGroup && mixer.group_id !== ChannelGroupIds.PeerMidiInputMusicGroup)) {
            logger.warn("master: found remote mixer that was not of groupID: PeerAudioInputMusicGroup", client_id, track.client_track_id, mixer);
          }

          vuMixer = mixer;
          muteMixer = mixer;

          if (mixer) {
            const oppositeMixers = groupedMixersForClientId(client_id, [ChannelGroupIds.UserMusicInputGroup], {}, MIX_MODES.PERSONAL);
            if (oppositeMixers[ChannelGroupIds.UserMusicInputGroup]) {
              oppositeMixer = oppositeMixers[ChannelGroupIds.UserMusicInputGroup][0];
            }

            if (!oppositeMixer) {
              logger.warn("unable to find UserMusicInputGroup corresponding to PeerAudioInputMusicGroup mixer", mixer, personalMixers);
            }
          }
          break;

        case MIX_MODES.PERSONAL:
          const mixers = groupedMixersForClientId(client_id, [ChannelGroupIds.UserMusicInputGroup], {}, MIX_MODES.PERSONAL);
          if (mixers[ChannelGroupIds.UserMusicInputGroup]) {
            mixer = mixers[ChannelGroupIds.UserMusicInputGroup][0];

            vuMixer = mixer;
            muteMixer = mixer;

            if (mixer) {
              oppositeMixer = getMixerByTrackId(track.client_track_id, MIX_MODES.MASTER);
              if (!oppositeMixer) {
                logger.debug("personal: unable to find a PeerAudioInputMusicGroup master mixer matching a UserMusicInput", client_id, track.client_track_id);
              } else if (oppositeMixer.group_id !== ChannelGroupIds.PeerAudioInputMusicGroup && oppositeMixer.group_id !== ChannelGroupIds.PeerMidiInputMusicGroup) {
                logger.error("personal: found remote mixer that was not of groupID: PeerAudioInputMusicGroup", client_id, track.client_track_id, mixer);
              }
            }
          } else {
            logger.error("no UserMusicInputGroup for client_id #{client_id} in PERSONAL mode", mixers);
          }
          break;
      }
    }

    return {
      mixer: mixer,
      oppositeMixer: oppositeMixer,
      vuMixer: vuMixer,
      muteMixer: muteMixer
    };
  }, [getMixerByTrackId, groupedMixersForClientId, personalMixers, allMixers]);

  // Compute myTracks - memoized to prevent infinite re-renders
  const myTracks = useMemo(() => {
    //console.debug("useMixerHelper: computing myTracks", { isConnected, currentSession, jamClient, allMixers });

    if (!isConnected || !inSession || !jamClient || !allMixers) return [];

    //const participant = currentSession.participants?.[jamClient.clientId];
    const participant = getParticipant(server.clientId);

    if (!participant) return [];

    const tracks = [];
    const connStatsClientId = participant.client_role === 'child' && participant.parent_client_id
      ? participant.parent_client_id
      : server.clientId;

      console.debug("useMixerHelper: my participant", participant, { connStatsClientId });

    const photoUrl = getAvatarUrl(participant.user.photo_url);
    const name = participant.user.name;

    // Get VST track assignments
    let vstTrackAssignments = { vsts: [] };
    try {
      const assignments = jamClient.VSTListTrackAssignments();
      vstTrackAssignments = assignments || { vsts: [] };
    } catch (error) {
      console.warn("Failed to get VST track assignments:", error);
    }

    for (const track of participant.tracks || []) {
      const mixerData = findMixerForTrack(participant.client_id, track, true, mixMode);
      const hasMixer = !!mixerData.mixer;

      const instrumentIcon = getInstrumentIcon45(track.instrument_id || track.instrument);
      const trackName = name;

      // Check if this track has a VST plugin assigned
      const hasVst = vstTrackAssignments.vsts && Array.isArray(vstTrackAssignments.vsts)
        ? vstTrackAssignments.vsts.some(vst => vst.track === track.client_track_id)
        : false;

      tracks.push({
        track: {
          ...track,
          hasVst
        },
        mixerFinder: [participant.client_id, track, true],
        mixers: mixerData,
        hasMixer,
        name,
        trackName,
        instrumentIcon,
        photoUrl,
        clientId: participant.client_id,
        connStatsClientId
      });
    }

    return tracks;
  }, [currentSession, isConnected, jamClient, allMixers, mixMode, findMixerForTrack, getParticipant, server.clientId]);

  // useEffect(() => {
  //   console.debug("useMixerHelper: myTracks updated", myTracks);
  // }, [myTracks]);


  const mixersForGroupId = useCallback((groupId, mixMode, currentAllMixers) => {
    const foundMixers = [];
    const modePrefix = mixMode === MIX_MODES.MASTER ? 'M' : 'P';

    for (const mixerKey in currentAllMixers) {
      if (mixerKey.startsWith(modePrefix)) {
        const mixer = currentAllMixers[mixerKey];
        if (mixer && mixer.group_id === groupId) {
          foundMixers.push(mixer);
        }
      }
    }

    return foundMixers;
  }, []);

  const getGroupMixer = useCallback((categoryId, mode) => {
    const groupId = mode === MIX_MODES.MASTER ? ChannelGroupIds.MasterCatGroup : ChannelGroupIds.MonitorCatGroup;
    const oppositeGroupId = !mode === MIX_MODES.MASTER ? ChannelGroupIds.MasterCatGroup : ChannelGroupIds.MonitorCatGroup;
    const mixers = mixersForGroupId(groupId, mode, allMixers);
    const oppositeMixers = mixersForGroupId(oppositeGroupId, !mode, allMixers);

    if (mixers.length === 0) {
      return null;
    }

    let found = null;
    let oppositeFound = null;
    for (const mixer of mixers) {
      if (mixer.name === categoryId) {
        found = mixer;
        break;
      }
    }

    for (const mixer of oppositeMixers) {
      if (mixer.name === categoryId) {
        oppositeFound = mixer;
        break;
      }
    }

    if (!found) {
      logger.warn("could not find mixer with categoryId: " + categoryId);
      return null;
    } else {
      return {
        mixer: found,
        muteMixer: found,
        vuMixer: found,
        oppositeMixer: oppositeFound
      };
    }
  }, [mixersForGroupId, allMixers]);

  const mixerForGroupId = useCallback((groupId, mixMode, currentAllMixers) => {
    const mixers = mixersForGroupId(groupId, mixMode, currentAllMixers);
    if (mixers && mixers.length > 0) {
      return mixers[0];
    } else {
      return null;
    }
  }, [mixersForGroupId]);

  const getAudioInputCategoryMixer = useCallback((mode, currentAllMixers) => {
    return getGroupMixer(CategoryGroupIds.AudioInputMusic, mode, currentAllMixers);
  }, [getGroupMixer]);

  const getChatCategoryMixer = useCallback((mode, currentAllMixers) => {
    return getGroupMixer(CategoryGroupIds.AudioInputChat, mode, currentAllMixers);
  }, [getGroupMixer]);

  const getUserChatCategoryMixer = useCallback((mode, currentAllMixers) => {
    return getGroupMixer(CategoryGroupIds.UserChat, mode, currentAllMixers);
  }, [getGroupMixer]);

  const getMediaCategoryMixer = useCallback((mode, currentAllMixers) => {
    return getGroupMixer(CategoryGroupIds.MediaTrack, mode, currentAllMixers);
  }, [getGroupMixer]);

  const getUserMediaCategoryMixer = useCallback((mode, currentAllMixers) => {
    return getGroupMixer(CategoryGroupIds.UserMedia, mode, currentAllMixers);
  }, [getGroupMixer]);

  const getUserMusicCategoryMixer = useCallback((mode, currentAllMixers) => {
    return getGroupMixer(CategoryGroupIds.UserMusic, mode, currentAllMixers);
  }, [getGroupMixer]);

  const getMetronomeCategoryMixer = useCallback((mode, currentAllMixers) => {
    return getGroupMixer(CategoryGroupIds.Metronome, mode, currentAllMixers);
  }, [getGroupMixer]);

  const getOutputCategoryMixer = useCallback((mode, currentAllMixers) => {
    if (mode === MIX_MODES.MASTER) {
      return getGroupMixer(CategoryGroupIds.MasterCatGroup, mode, currentAllMixers);
    } else {
      return getGroupMixer(CategoryGroupIds.MonitorCatGroup, mode, currentAllMixers);
    }
  }, [getGroupMixer]);

  const getOutputMixer = useCallback((mode, currentAllMixers) => {
    if (mode === MIX_MODES.MASTER) {
      return mixerForGroupId(ChannelGroupIds.MasterGroup, mode, currentAllMixers);
    } else {
      return mixerForGroupId(ChannelGroupIds.MonitorGroup, mode, currentAllMixers);
    }
  }, [mixerForGroupId]);

  const setMixerPan = useCallback(async (mixer, panPercent) => {
    trackVolumeObject.pan = panHelpers.convertPercentToPan(panPercent);
    await jamClient.SessionSetTrackVolumeData(mixer.id, mixer.mode, trackVolumeObject);
  }, [trackVolumeObject, panHelpers]);

  const resolveBackingTracks = useCallback((currentBackingTrackMixers) => {
    const backingTracks = [];

    if (currentBackingTrackMixers.length === 0) return backingTracks;

    let serverBackingTracks = [];
    let backingTrackMixers = currentBackingTrackMixers;

    if (session.isPlayingRecording()) {
      backingTrackMixers = context._.filter(backingTrackMixers, (mixer) => mixer.managed || !mixer.managed);
      serverBackingTracks = session.recordedBackingTracks();
    } else {
      serverBackingTracks = session.backingTracks();
      backingTrackMixers = context._.filter(backingTrackMixers, (mixer) => !mixer.managed);
      if (backingTrackMixers.length > 1) {
        logger.error("multiple, managed backing track mixers encountered", backingTrackMixers);
        console.warn("Multiple Backing Tracks Encountered: Only one backing track can be open at a time.");
        return backingTracks;
      }
    }

    if (!serverBackingTracks || serverBackingTracks.length === 0) {
      return backingTracks;
    }

    let noCorrespondingTracks = false;
    for (const mixer of backingTrackMixers) {
      const correspondingTracks = [];
      noCorrespondingTracks = false;
      if (session.isPlayingRecording()) {
        for (const backingTrack of serverBackingTracks) {
          if (mixer.persisted_track_id === backingTrack.client_track_id || mixer.id === 'L' + backingTrack.client_track_id) {
            correspondingTracks.push(backingTrack);
          }
        }
      } else {
        correspondingTracks.push(serverBackingTracks[0]);
      }

      if (correspondingTracks.length === 0) {
        noCorrespondingTracks = true;
        logger.debug("renderBackingTracks: could not map backing tracks");
        console.warn("Unable to Open Backing Track: Could not correlate server and client tracks");
        break;
      }

      const serverBackingTrack = correspondingTracks[0];

      const oppositeMixer = getMixerByResourceId(mixer.rid, MIX_MODES.PERSONAL, allMixers);

      const isOpener = mixer.group_id === ChannelGroupIds.MediaTrackGroup;
      const data = {
        isOpener: isOpener,
        shortFilename: context.JK.getNameOfFile(serverBackingTrack.filename),
        instrumentIcon: getInstrumentIcon45(serverBackingTrack.instrument_id),
        photoUrl: "/assets/content/icon_recording.png",
        showLoop: isOpener && !session.isPlayingRecording(),
        track: serverBackingTrack,
        mixers: mediaMixers(mixer, isOpener, allMixers)
      };

      backingTracks.push(data);
    }

    return backingTracks;
  }, [session]);

  const resolveJamTracks = useCallback((currentJamTrackMixers) => {
    const _jamTracks = [];

    if (currentJamTrackMixers.length === 0) return _jamTracks;

    let jamTrackMixers = currentJamTrackMixers.slice();
    let jamTracks = [];
    let jamTrackName = null;
    const jamTrackMixdown = session?.jamTrackMixdown() || { id: null };

    if (session?.isPlayingRecording()) {
      jamTracks = session.recordedJamTracks();
      jamTrackName = session.recordedJamTrackName();
    } else {
      jamTracks = session?.jamTracks();
      jamTrackName = session?.jamTrackName();
    }

    const isOpener = jamTrackMixers[0]?.group_id === ChannelGroupIds.JamTrackGroup;

    if (jamTracks) {
      let noCorrespondingTracks = false;

      if (jamTrackMixdown.id) {
        logger.debug("MixerHelper: mixdown is active. id: #{jamTrackMixdown.id}");
        if (jamTrackMixers.length === 0) {
          noCorrespondingTracks = true;
          logger.error("could not correlate mixdown tracks", jamTrackMixers, jamTrackMixdown);
          // session?.app?.notify({
          //   title: "Unable to Open Custom Mix",
          //   text: "Could not correlate server and client tracks",
          //   icon_url: "/assets/content/icon_alert_big.png"
          // });
          return _jamTracks;
        } else if (jamTrackMixers.length > 1) {
          logger.warn("ignoring wrong amount of mixers for JamTrack in mixdown mode");
          return _jamTracks;
        } else {
          const instrumentIcon = getInstrumentIcon24('other');
          const part = null;
          const instrumentName = 'Custom Mix';
          const trackName = 'Custom Mix';

          const data = {
            name: jamTrackName,
            trackName: trackName,
            part: part,
            isOpener: isOpener,
            instrumentIcon: instrumentIcon,
            track: jamTrackMixdown,
            mixers: mediaMixers(jamTrackMixers[0], isOpener, allMixers)
          };

          _jamTracks.push(data);
        }
      } else {
        logger.debug("MixerHelper: full jamtrack is active");

        if (jamTrackMixers.length === 1) {
          logger.warn("ignoring wrong amount of mixers for JamTrack in Full Track mode");
          return _jamTracks;
        }

        for (const jamTrack of jamTracks) {
          let mixer = null;
          const correspondingTracks = [];

          for (const matchMixer of currentJamTrackMixers) {
            if (matchMixer.id === jamTrack.id) {
              correspondingTracks.push(jamTrack);
              mixer = matchMixer;
            }
          }

          if (correspondingTracks.length === 0) {
            noCorrespondingTracks = true;
            logger.error("could not correlate jam tracks", jamTrackMixers, jamTracks);
            // session?.app?.notify({
            //   title: "Unable to Open JamTrack",
            //   text: "Could not correlate server and client tracks",
            //   icon_url: "/assets/content/icon_alert_big.png"
            // });
            return _jamTracks;
          }

          jamTrackMixers.splice(jamTrackMixers.indexOf(mixer), 1);

          const oneOfTheTracks = correspondingTracks[0];
          const instrumentIcon = getInstrumentIcon24(oneOfTheTracks.instrument.id);

          const part = oneOfTheTracks.part;

          let instrumentName = oneOfTheTracks.instrument.description;

          let trackName;
          if (part) {
            trackName = `${instrumentName}: ${part}`;
          } else {
            trackName = instrumentName;
          }

          if (jamTrack.track_type === 'Click') {
            trackName = 'Clicktrack';
          }

          const data = {
            name: jamTrackName,
            trackName: trackName,
            part: part,
            isOpener: isOpener,
            instrumentIcon: instrumentIcon,
            track: oneOfTheTracks,
            mixers: mediaMixers(mixer, isOpener, allMixers)
          };

          _jamTracks.push(data);
        }
      }
    }

    return _jamTracks;
  }, [session, allMixers]);

  const resolveRecordedTracks = useCallback((currentRecordingTrackMixers, currentAllMixers) => {
    const recordedTracks = [];

    if (currentRecordingTrackMixers.length === 0) return recordedTracks;

    const serverRecordedTracks = session?.recordedTracks();

    const isOpener = currentRecordingTrackMixers[0]?.group_id === ChannelGroupIds.MediaTrackGroup;

    if (serverRecordedTracks) {
      const recordingName = session?.recordingName();
      let noCorrespondingTracks = false;
      for (const mixer of currentRecordingTrackMixers) {
        const correspondingTracks = [];
        for (const recordedTrack of serverRecordedTracks) {
          if (mixer.id.indexOf("L") === 0) {
            if (mixer.id.substring(1) === recordedTrack.client_track_id) {
              correspondingTracks.push(recordedTrack);
            }
          } else if (mixer.id.indexOf("C") === 0) {
            if (mixer.id.substring(1) === recordedTrack.client_id) {
              correspondingTracks.push(recordedTrack);
            }
          } else {
            alert("Invalid state: the recorded track had neither persisted_track_id or persisted_client_id");
          }
        }

        if (correspondingTracks.length === 0) {
          noCorrespondingTracks = true;
          // session?.app?.notify({
          //   title: "Unable to Open Recording",
          //   text: "Could not correlate server and client tracks",
          //   icon_url: "/assets/content/icon_alert_big.png"
          // });
          return recordedTracks;
        }

        const oneOfTheTracks = correspondingTracks[0];
        const instrumentIcon = getInstrumentIcon24(oneOfTheTracks.instrument_id);
        let userName = oneOfTheTracks.user.name;
        if (!userName) {
          userName = oneOfTheTracks.user.first_name + ' ' + oneOfTheTracks.user.last_name;
        }

        const data = {
          recordingName: recordingName,
          isOpener: isOpener,
          userName: userName,
          instrumentIcon: instrumentIcon,
          track: oneOfTheTracks,
          mixers: mediaMixers(mixer, isOpener, currentAllMixers)
        };

        recordedTracks.push(data);
      }
    }

    return recordedTracks;
  }, [session]);

  const resolveMetronome = useCallback((currentMetronomeTrackMixers, currentAllMixers) => {
    if (currentMetronomeTrackMixers.length === 0) return null;

    const mixer = currentMetronomeTrackMixers[0];

    const instrumentIcon = "/assets/content/icon_metronome.png";

    const metronome = {
      instrumentIcon: instrumentIcon,
      mixers: mediaMixers(mixer, true, currentAllMixers)
    };

    return metronome;
  }, []);

  const resolveChatMixer = useCallback((currentMixersByResourceId, currentAllMixers) => {
    const masterChatMixers = mixersForGroupId(ChannelGroupIds.AudioInputChatGroup, MIX_MODES.MASTER, currentAllMixers);

    if (masterChatMixers.length === 0) return null;

    const personalChatMixers = mixersForGroupId(ChannelGroupIds.AudioInputChatGroup, MIX_MODES.PERSONAL, currentAllMixers);

    if (personalChatMixers.length === 0) {
      logger.warn("unable to find personal mixer for voice chat");
      return null;
    }

    const masterChatMixer = masterChatMixers[0];
    const personalChatMixer = personalChatMixers[0];

    return {
      master: {
        mixer: masterChatMixer,
        muteMixer: masterChatMixer,
        vuMixer: masterChatMixer,
        oppositeMixer: personalChatMixer
      },
      personal: {
        mixer: personalChatMixer,
        muteMixer: personalChatMixer,
        vuMixer: personalChatMixer,
        oppositeMixer: masterChatMixer
      }
    };
  }, []);

  const groupTypes = useCallback(() => {
    // console.debug("useMixerHelper: groupTypes called", {session, allMixers});

    if(!session || Object.keys(allMixers).length === 0) return;

   
    const localMediaMixers = mixersForGroupIds(mediaTrackGroups, MIX_MODES.MASTER, allMixers);
    const peerLocalMediaMixers = mixersForGroupId(ChannelGroupIds.PeerMediaTrackGroup, MIX_MODES.MASTER, allMixers);

    const newRecordingTrackMixers = [];
    const newBackingTrackMixers = [];
    const newJamTrackMixers = [];
    const newMetronomeTrackMixers = [];
    const newAdhocTrackMixers = [];

    const groupByType = (mixers, isLocalMixer) => {
      for (const mixer of mixers) {
        const mediaType = mixer.media_type;
        const groupId = mixer.group_id;

        if (mediaType === 'MetronomeTrack' || groupId === ChannelGroupIds.MetronomeGroup) {
          newMetronomeTrackMixers.push(mixer);
        } else if (mediaType === null || mediaType === "" || mediaType === 'RecordingTrack') {
          let isJamTrack = false;

          if (mixer.id === session.jamTrackMixdown()?.id) {
            isJamTrack = true;
          }

          if (!isJamTrack && session.jamTracks()) {
            for (const jamTrack of session.jamTracks()) {
              if (mixer.id === jamTrack.id) {
                isJamTrack = true;
                break;
              }
            }
          }

          if (!isJamTrack && session.recordedJamTracks()) {
            for (const recordedJamTrack of session.recordedJamTracks()) {
              if (mixer.id === recordedJamTrack.id) {
                isJamTrack = true;
                break;
              }
            }
          }

          if (isJamTrack) {
            newJamTrackMixers.push(mixer);
          } else {
            let isBackingTrack = false;
            if (session.recordedBackingTracks()) {
              for (const recordedBackingTrack of session.recordedBackingTracks()) {
                if (mixer.id === 'L' + recordedBackingTrack.client_track_id) {
                  isBackingTrack = true;
                  break;
                }
              }
            }

            if (session.backingTracks()) {
              for (const backingTrack of session.backingTracks()) {
                if (mixer.id === 'L' + backingTrack.client_track_id) {
                  isBackingTrack = true;
                  break;
                }
              }
            }

            if (isBackingTrack) {
              newBackingTrackMixers.push(mixer);
            } else {
              newRecordingTrackMixers.push(mixer);
            }
          }
        } else if (mediaType === 'PeerMediaTrack' || mediaType === 'BackingTrack') {
          newBackingTrackMixers.push(mixer);
        } else if (mediaType === 'JamTrack') {
          newJamTrackMixers.push(mixer);
        } else if (mediaType === null || mediaType === "" || mediaType === 'RecordingTrack') {
          newRecordingTrackMixers.push(mixer);
        } else {
          if (mediaType !== 'Broadcast') {
            logger.warn("Unknown track type: " + mediaType);
            newAdhocTrackMixers.push(mixer);
          }
        }
      }
    };

    groupByType(localMediaMixers, true);
    groupByType(peerLocalMediaMixers, false);

    setRecordingTrackMixers(newRecordingTrackMixers);
    setBackingTrackMixers(newBackingTrackMixers);
    setJamTrackMixers(newJamTrackMixers);
    setMetronomeTrackMixers(newMetronomeTrackMixers);
    setAdhocTrackMixers(newAdhocTrackMixers);

    const newBackingTracks = resolveBackingTracks(newBackingTrackMixers, allMixers);
    const newJamTracks = resolveJamTracks(newJamTrackMixers, allMixers);
    const newRecordedTracks = resolveRecordedTracks(newRecordingTrackMixers, allMixers);
    const newMetronome = resolveMetronome(newMetronomeTrackMixers, allMixers);

    setBackingTracks(newBackingTracks);
    setJamTracks(newJamTracks);
    setRecordedTracks(newRecordedTracks);
    setMetronome(newMetronome);

    const newMediaSummary = {
      recordingOpen: newRecordedTracks.length > 0,
      jamTrackOpen: newJamTracks.length > 0,
      backingTrackOpen: newBackingTracks.length > 0,
      metronomeOpen: session.isMetronomeOpen()
    };

    let mediaOpenSummary = false;
    for (const mediaType in newMediaSummary) {
      if (newMediaSummary[mediaType]) {
        mediaOpenSummary = true;
        break;
      }
    }

    newMediaSummary.mediaOpen = mediaOpenSummary;
    newMediaSummary.userNeedsMediaControls = newMediaSummary.mediaOpen || window.JamTrackStore?.jamTrack;
    newMediaSummary.jamTrack = window.JamTrackStore?.jamTrack;

    let isOpener = false;
    if (newMediaSummary.recordingOpen) {
      isOpener = newRecordedTracks[0].isOpener;
    } else if (newMediaSummary.jamTrackOpen) {
      isOpener = newJamTracks[0].isOpener;
    } else if (newMediaSummary.backingTrackOpen) {
      isOpener = newBackingTracks[0].isOpener;
    }

    newMediaSummary.isOpener = isOpener;
    setMediaSummary(newMediaSummary);

    prepareSimulatedMixers(allMixers);
  }, [session, allMixers]);

  const mixersForGroupIds = useCallback((groupIds, mixMode, currentAllMixers) => {
    const foundMixers = [];
    const modePrefix = mixMode === MIX_MODES.MASTER ? 'M' : 'P';

    for (const mixerKey in currentAllMixers) {
      if (mixerKey.startsWith(modePrefix)) {
        const mixer = currentAllMixers[mixerKey];
        if (mixer && groupIds.includes(mixer.group_id)) {
          foundMixers.push(mixer);
        }
      }
    }

    return foundMixers;
  }, []);

 



  const getMixerByResourceId = useCallback((resourceId, mode) => {
    const mixerPair = mixersByResourceId[resourceId];

    if (!mixerPair) return null;

    if (!mode) {
      return mixerPair;
    } else {
      if (mode === MIX_MODES.MASTER) {
        return mixerPair.master;
      } else {
        return mixerPair.personal;
      }
    }
  }, [mixersByResourceId]);

  

  const mute = useCallback(async (mixerId, mode, muting) => {
    if (mode == null) { mode = mixMode; }

    const mixer = fillTrackVolumeObject(mixerId, mode);
    if (!mixer) return;

    context.trackVolumeObject.mute = muting;
    await context.jamClient.SessionSetTrackVolumeData(mixerId, mode, context.trackVolumeObject);

    const updatedMixer = getMixer(mixerId, mode);
    updatedMixer.mute = muting;
  }, [mixMode, getMixer, fillTrackVolumeObject]);

  const getOriginalVolume = useCallback((mixers, gainType) => {
    let originalVolume = null;
    if (gainType === 'music') {
      for (const mixer of mixers) {
        if (mixer.name !== CategoryGroupIds.UserMedia && mixer.name !== CategoryGroupIds.MediaTrack) {
          originalVolume = mixer.volume_left;
          break;
        }
      }
    } else {
      originalVolume = mixers[0].volume_left;
    }

    return originalVolume;
  }, []);

  const faderChanged = useCallback(async (data, mixers, gainType, controlGroup) => {
    //console.log("MixerHelper: faderChanged called", { data, mixers, gainType, controlGroup });
    if (!Array.isArray(mixers)) {
      mixers = [mixers];
    }
    const originalVolume = getOriginalVolume(mixers, gainType);

    console.log("MixerHelper: faderChanged called", { data, mixers, gainType, controlGroup, originalVolume });

    // Handle multiple mixers (master + personal pairs like web version)
    const mixerIds = mixers.map(m => m.id);
    const hasMasterAndPersonalControls = mixerIds.length === 2;

    for (let i = 0; i < mixers.length; i++) {
      const m = mixers[i];

      // Broadcast only when NOT dragging (matches web version logic)
      const broadcast = !(data.dragging);

      // Determine mode for multiple mixers (like web version)
      let mode = m.mode;
      if (hasMasterAndPersonalControls) {
        mode = i === 0 ? MIX_MODES.MASTER : MIX_MODES.PERSONAL;
      }

      const mixer = fillTrackVolumeObject(m.id, mode, allMixers, broadcast);
      if (mixer == null) {
        console.error("MixerHelper: faderChanged: mixer is null, skipping", m, gainType, controlGroup);
        continue;
      }

      // Handle relative volume adjustments for music category (matches web version)
      const relative = gainType === 'music' && (mixer.name === CategoryGroupIds.UserMedia || mixer.name === CategoryGroupIds.MediaTrack);

      await setMixerVolume(mixer, data.percentage, relative, originalVolume, controlGroup, allMixers);

      // Update local mixer state - create new state object to trigger React re-renders
      setAllMixers(prev => ({
        ...prev,
        [`${mixer.mode ? 'M' : 'P'}${mixer.id}`]: {
          ...prev[`${mixer.mode ? 'M' : 'P'}${mixer.id}`],
          volume_left: trackVolumeObject.volL
        }
      }));
    }
  }, [getOriginalVolume, getMixer, fillTrackVolumeObject, setMixerVolume, allMixers]);

  const initGain = useCallback((mixer) => {
    if (Array.isArray(mixer)) {
      mixer = mixer[0];
    }

    // In React version, fader initialization is handled through component state
    // This function is kept for API compatibility with web version
    // No DOM manipulation needed since React handles state updates
    console.debug('initGain called for mixer:', mixer?.id);
  }, []);

  const panChanged = useCallback(async (data, mixers, groupId) => {
    if (!Array.isArray(mixers)) { mixers = [mixers]; }
    const result = [];
    for (const mixer of mixers) {
      const broadcast = !(data.dragging);
      const filledMixer = fillTrackVolumeObject(mixer.id, mixer.mode, allMixers, broadcast);

      await setMixerPan(filledMixer, data.percentage);

      const updatedMixer = getMixer(filledMixer.id, filledMixer.mode, allMixers);
      updatedMixer.pan = trackVolumeObject.pan;
      result.push(trackVolumeObject.pan);
    }
    return result;
  }, [getMixer, fillTrackVolumeObject, setMixerPan, allMixers]);

  const initPan = useCallback((mixer) => {
    const panPercent = panHelpers.convertPanToPercent(mixer.pan);
    faderHelpers.setFaderValue(mixer.id, panPercent, Math.abs(mixer.pan));
    faderHelpers.showFader(mixer.id);
  }, [faderHelpers]);

  const loopChanged = useCallback(async (mixer, shouldLoop) => {
    fillTrackVolumeObject(mixer.id, mixer.mode, allMixers);
    //context.trackVolumeObject.loop = shouldLoop;
    setTrackVolumeObject(prev => ({
      ...prev,
      loop: shouldLoop
    }));
    await context.jamClient.SessionSetTrackVolumeData(mixer.id, mixer.mode, context.trackVolumeObject);

    const updatedMixer = getMixer(mixer.id, mixer.mode, allMixers);
    updatedMixer.loop = context.trackVolumeObject.loop;
  }, [getMixer, fillTrackVolumeObject, allMixers]);

  const percentFromMixerValue = useCallback((min, max, value) => {
    try {
      const range = Math.abs(max - min);
      const magnitude = value - min;
      const percent = Math.round(100 * (magnitude / range));
      return percent;
    } catch (err) {
      return 0;
    }
  }, []);

  const percentToMixerValue = useCallback((min, max, percent) => {
    const range = Math.abs(max - min);
    const multiplier = percent / 100;
    let value = min + (multiplier * range);

    if (value < min) {
      value = min;
    }

    if (value > max) {
      value = max;
    }

    return value;
  }, []);

  const collectStats = useCallback((mixer, currentVuStats) => {
    let mixerStats = currentVuStats[mixer.id];

    if (!mixerStats) {
      mixerStats = { count: 0, group_name: context.JK.groupIdDisplay(mixer) };
      currentVuStats[mixer.id] = mixerStats;
    }

    mixerStats.count++;
  }, []);

  const dumpVUStats = useCallback((currentVuStats) => {
    logger.debug("VU STAT DUMP");
    for (const mixerId in currentVuStats) {
      const mixerStat = currentVuStats[mixerId];
      logger.debug("VU STAT: #{mixerStat.group_name} count=#{mixerStat.count}");
    }
  }, []);

  // Change updateVU to use the ref
  const updateVU = useCallback((mixerId, mode, leftValue, leftClipping, rightValue, rightClipping) => {
    if (!isReady.current) return;

    const mixer = getMixer(mixerId, mode);

    if (mixer) {
      // console.log("useMixerHelper: updateVU mixer", allMixersRef.current, mixerId, mode, mixer);
      updateVU3(mixer, leftValue, leftClipping, rightValue, rightClipping);
    }
  }, []);

  const getTrackInfo = useCallback(async () => {
    return await context.JK.TrackHelpers.getTrackInfo(context.jamClient, masterMixers);
  }, [masterMixers]);

  const prepareSimulatedMixers = useCallback(() => {
    const newSimulatedMusicCategoryMixers = {};
    const newSimulatedChatCategoryMixers = {};

    newSimulatedMusicCategoryMixers[MIX_MODES.MASTER] = getSimulatedMusicCategoryMixer(MIX_MODES.MASTER, allMixers);
    newSimulatedMusicCategoryMixers[MIX_MODES.PERSONAL] = getSimulatedMusicCategoryMixer(MIX_MODES.PERSONAL, allMixers);

    // Call getSimulatedChatCategoryMixer once and assign parts like web project
    const chatMixerData = getSimulatedChatCategoryMixer(allMixers);
    newSimulatedChatCategoryMixers[MIX_MODES.MASTER] = chatMixerData?.master;
    newSimulatedChatCategoryMixers[MIX_MODES.PERSONAL] = chatMixerData?.personal;

    setSimulatedMusicCategoryMixers(newSimulatedMusicCategoryMixers);
    setSimulatedChatCategoryMixers(newSimulatedChatCategoryMixers);
  }, [allMixers]);

  const getSimulatedMusicCategoryMixer = useCallback((mode, currentAllMixers) => {
    const myInputs = getAudioInputCategoryMixer(mode, currentAllMixers)?.mixer;
    const peerInputs = getUserMusicCategoryMixer(mode, currentAllMixers)?.mixer;
    const myMedia = getMediaCategoryMixer(mode, currentAllMixers)?.mixer;
    const peerMedia = getUserMediaCategoryMixer(mode, currentAllMixers)?.mixer;
    const metronome = getMetronomeCategoryMixer(mode, currentAllMixers)?.mixer;
    const output = getOutputMixer(mode, currentAllMixers);
    const oppositeOutput = getOutputMixer(!mode, currentAllMixers);

    if (myInputs) {
      return {
        first: myInputs,
        mixer: [myInputs, peerInputs, myMedia, peerMedia, metronome].filter(m => m),
        muteMixer: [myInputs, peerInputs, myMedia, peerMedia, metronome].filter(m => m),
        vuMixer: output
      };
    } else {
      return null;
    }
  }, [getAudioInputCategoryMixer, getUserMusicCategoryMixer, getMediaCategoryMixer, getUserMediaCategoryMixer, getMetronomeCategoryMixer, getOutputMixer]);

  const getSimulatedChatCategoryMixer = useCallback((currentAllMixers) => {
    // Find chat mixers by group_id like the web project does
    
    const masterChatMixers = mixersForGroupId(ChannelGroupIds.AudioInputChatGroup, MIX_MODES.MASTER, currentAllMixers);
    const personalChatMixers = mixersForGroupId(ChannelGroupIds.AudioInputChatGroup, MIX_MODES.PERSONAL, currentAllMixers);

    

    if (masterChatMixers.length === 0 || personalChatMixers.length === 0) {
      return null;
    }

    const masterChatMixer = masterChatMixers[0];
    const personalChatMixer = personalChatMixers[0];

    console.debug("useMixerHelper: getSimulatedChatCategoryMixer", masterChatMixer, personalChatMixer);

    // Return the same structure as web project's resolveChatMixer
    return {
      master: {
        mixer: masterChatMixer,
        muteMixer: masterChatMixer,
        vuMixer: masterChatMixer,
        oppositeMixer: personalChatMixer
      },
      personal: {
        mixer: personalChatMixer,
        muteMixer: personalChatMixer,
        vuMixer: personalChatMixer,
        oppositeMixer: masterChatMixer
      }
    };
  }, []);

  const refreshMixer = useCallback((mixers, currentAllMixers) => {
    if (!mixers || !mixers.mixer) return null;

    let updateMixers = null;
    if (Array.isArray(mixers.mixer)) {
      if (mixers.mixer.length > 0) {
        updateMixers = [];
        for (const mixer of mixers.mixer) {
          updateMixers.push(getMixer(mixer.id, mixer.mode, currentAllMixers));
        }
      }
    } else {
      updateMixers = getMixer(mixers.mixer.id, mixers.mixer.mode, currentAllMixers);
    }

    let updatedVUMixers = null;
    if (Array.isArray(mixers.vuMixer)) {
      updatedVUMixers = [];
      for (const vuMixer of mixers.vuMixer) {
        updatedVUMixers.push(getMixer(vuMixer.id, vuMixer.mode, currentAllMixers));
      }
    } else {
      updatedVUMixers = getMixer(mixers.vuMixer.id, mixers.vuMixer.mode, currentAllMixers);
    }

    let updateMuteMixers = null;
    if (Array.isArray(mixers.muteMixer)) {
      updateMuteMixers = [];
      for (const muteMixer of mixers.muteMixer) {
        updateMuteMixers.push(getMixer(muteMixer.id, muteMixer.mode, currentAllMixers));
      }
    } else {
      updateMuteMixers = getMixer(mixers.muteMixer.id, mixers.muteMixer.mode, currentAllMixers);
    }

    const oppositeMixer = mixers.oppositeMixer ? getMixer(mixers.oppositeMixer.id, mixers.oppositeMixer.mode, currentAllMixers) : null;

    if (updateMixers) {
      return {
        mixer: updateMixers,
        vuMixer: updatedVUMixers,
        muteMixer: updateMuteMixers,
        oppositeMixer: oppositeMixer
      };
    } else {
      return null;
    }
  }, [getMixer]);

  const recordingName = useCallback(() => {
    return session.recordingName();
  }, [session]);

  const jamTrackName = useCallback(() => {
    return session.jamTrackName();
  }, [session]);


  return {
    isReady,
    session,
    // masterMixers,
    // personalMixers,
    mixers: allMixers,
    myTracks,
    findMixerForTrack,
    updateMixerData,
    updateVU,
    simulatedMusicCategoryMixers,
    simulatedChatCategoryMixers,
    mixMode,
    faderChanged,
    initGain,
    setMixerPan
  };
}

export default useMixerHelper;
