package pnyx

import (
	"slices"
  "fmt"
)

type ChannelID byte

const (
  MODE_CHANNEL ModeID = iota
  MODE_RAW
  MODE_AUDIO

  CHANNEL_JOIN byte = iota
  CHANNEL_LEAVE
  CHANNEL_MEMBERS

  AUDIO_SET_SAMPLE_RATE = iota
  AUDIO_GET_SAMPLE_RATE

  RAW_DATA = iota
)

type ModeID uint8
type CommandID uint8
type Permission string

type Channel struct {
  id ChannelID
  name string
  modes map[ModeID]Mode
  sessions []SessionID
}

func(channel *Channel) Data(session *Session, mode ModeID, data []byte) []SendPacket {
  m, has_mode := channel.modes[mode]
  if has_mode == false {
    return nil
  } else {
    return m.Data(session, channel, data)
  }
}

func(channel *Channel) Command(session *Session, packet ChannelCommandPacket) ([]SendPacket, error) {
  if packet.Mode == MODE_CHANNEL {
    switch packet.Command {
    case CHANNEL_JOIN:
      if slices.Contains(channel.sessions, session.ID) {
        return nil, fmt.Errorf("Session %s already in channel %d, can't join", session.ID, channel.id)
      } else {
        channel.sessions = append(channel.sessions, session.ID)
        return nil, nil
      }
    case CHANNEL_LEAVE:
      idx := slices.Index(channel.sessions, session.ID)
      if idx == -1 {
        return nil, fmt.Errorf("Session %s not in channel %d, can't leave", session.ID, channel.id)
      } else {
        channel.sessions = slices.Delete(channel.sessions, idx, idx+1)
        return nil, nil
      }
    default:
      return nil, fmt.Errorf("Unknown MODE_CHANNEL command: 0x%02x", packet.Command)
    }
  } else {
    mode, has_mode := channel.modes[packet.Mode]
    if has_mode == false {
      return nil, fmt.Errorf("Channel has no mode 0x%02x", mode)
    } else {
      return mode.Command(session, channel, packet)
    }
  }
}

type SendPacket struct {
  Packet *Packet
  Session SessionID
}

type Mode interface {
  // Process takes incoming packets from a session and returns a list of packets to send
  Command(session *Session, channel *Channel, packet ChannelCommandPacket) ([]SendPacket, error)
  Data(session *Session, channel *Channel, data []byte) []SendPacket
}

func multiplex_without_sender(origin SessionID, packet *Packet, sessions []SessionID) []SendPacket {
  send_packets := make([]SendPacket, len(sessions) - 1)
  i := 0
  for _, session_id := range(sessions) {
    if session_id == origin {
      continue
    }

    send_packets[i] = SendPacket{
      Packet: packet,
      Session: session_id,
    }
    i += 1
  }

  return send_packets
}

func multiplex(packet *Packet, sessions []SessionID) []SendPacket {
  send_packets := make([]SendPacket, len(sessions))
  for i, session_id := range(sessions) {
    send_packets[i] = SendPacket{
      Packet: packet,
      Session: session_id,
    }
  }

  return send_packets
}

type RawMode struct {
}

func(mode *RawMode) Command(session *Session, channel *Channel, packet ChannelCommandPacket) ([]SendPacket, error) {
  return nil, fmt.Errorf("unknown raw mode command 0x%02x", packet.Command)
}

func(mode *RawMode) Data(session *Session, channel *Channel, data []byte) []SendPacket {
  if slices.Contains(channel.sessions, session.ID) {
    new_packet := NewChannelPeerPacket(session.Peer, channel.id, MODE_RAW, data)
    return multiplex_without_sender(session.ID, new_packet, channel.sessions)
  }
  return nil
}

type SampleRate byte
const (
  SAMPLE_RATE_UNSET SampleRate = 0xFF
  SAMPLE_RATE_24KHZ = 0x01
  SAMPLE_RATE_48KHZ = 0x02
)

type AudioMode struct {
  SampleRate SampleRate
}

func(mode *AudioMode) Command(session *Session, channel *Channel, packet ChannelCommandPacket) ([]SendPacket, error) {
  switch packet.Command {
  case AUDIO_SET_SAMPLE_RATE:
    if len(packet.Data) == 1 {
      switch SampleRate(packet.Data[0]) {
      case SAMPLE_RATE_24KHZ:
        fallthrough
      case SAMPLE_RATE_48KHZ:
        mode.SampleRate = SampleRate(packet.Data[0])
        update_packet := NewChannelCommandPacket(packet.ReqID, channel.id, MODE_AUDIO, AUDIO_SET_SAMPLE_RATE, packet.Data)
        return multiplex(update_packet, channel.sessions), nil
      default:
        return nil, fmt.Errorf("Invalid sample rate: %x", packet.Data[0])
      }
    } else {
      return nil, fmt.Errorf("Invalid AUDIO_SET_SAMPLE_RATE payload: %x", packet.Data)
    }
  case AUDIO_GET_SAMPLE_RATE:
    return []SendPacket{{
      Packet: NewChannelCommandPacket(packet.ReqID, channel.id, MODE_AUDIO, AUDIO_SET_SAMPLE_RATE, []byte{byte(mode.SampleRate)}),
      Session: session.ID,
    }}, nil
  default:
    return nil, fmt.Errorf("unknown audio mode command 0x%02x", packet.Command)
  }
}

func(mode *AudioMode) Data(session *Session, channel *Channel, data []byte) []SendPacket {
  if slices.Contains(channel.sessions, session.ID) {
    new_packet := NewChannelPeerPacket(session.Peer, channel.id, MODE_AUDIO, data)
    return multiplex_without_sender(session.ID, new_packet, channel.sessions)
  }
  return nil
}
