package pnyx

import (
	"slices"
  "fmt"
)

type ChannelID uint32

const (
  MODE_CHANNEL ModeID = iota
  MODE_RAW

  CHANNEL_JOIN byte = iota
  CHANNEL_LEAVE
  CHANNEL_MEMBERS

  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, mode ModeID, command byte, data []byte) ([]SendPacket, error) {
  if mode == MODE_CHANNEL {
    switch 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", command)
    }
  } else {
    mode, has_mode := channel.modes[mode]
    if has_mode == false {
      return nil, fmt.Errorf("Channel has no mode 0x%02x", mode)
    } else {
      return mode.Command(session, channel, command, data)
    }
  }
}

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, command byte, data []byte) ([]SendPacket, error)
  Data(session *Session, channel *Channel, data []byte) []SendPacket
}

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

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

  return send_packets
}

type RawMode struct {
}

func(mode *RawMode) Command(session *Session, channel *Channel, command byte, data []byte) ([]SendPacket, error) {
  return nil, fmt.Errorf("unknown raw mode command 0x%02x", 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(session, new_packet, channel.sessions)
  }
  return nil
}
