import { send } from './signalPlugin'
import store from './index'

var lastPeer // horrible

const configuration = {
  iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
}

const state = {
  name: null,
  peers: []
}

const getters = {
  displayName: state => state.name
}

const actions = {
  setName ({ commit }, name) {
    commit('SET_NAME', name)
  },
  async makeOffer ({ commit }, targetName) {
    commit('CREATE_PEER_CONNECTION', targetName)
    commit('OFFER', targetName)
  },
  async offer ({ commit }, { offer, senderName }) {
    console.log('offer from ' + senderName)
    commit('CREATE_PEER_CONNECTION', senderName)
    commit('ANSWER', { target: senderName, offer: offer })
  },
  answer ({ commit }, { answer, senderName }) {
    console.log('answer from ' + senderName)
    commit('FINALIZE', { target: senderName, answer: answer })
  },
  candidate ({ commit }, { candidate, senderName }) {
    commit('CANDIDATE', { target: senderName, candidate: candidate })
  },
  leave ({ commit }) {
    commit('LEAVE')
  },
  kick ({ commit }, target) {
    commit('KICK', target)
  },
  broadcast ({ commit }, { message, type }) {
    commit('BROADCAST', { message: message, type: type })
  }
}

const mutations = {
  SET_NAME (state, name) {
    state.name = name
  },
  CREATE_PEER_CONNECTION (state, target) {
    console.log('[RTC] create peer connection with ' + target)
    var peer = {
      name: target,
      connection: new RTCPeerConnection(configuration),
      dataChannel: null
    }

    state.peers.push(peer)

    peer.connection.onicecandidate = function (event) {
      if (event.candidate) {
        send({
          type: 'candidate',
          name: state.name,
          target: target,
          data: event.candidate
        })
      }
    }

    peer.connection.onnegotiationneeded = function () { handleNegotiationNeededEvent(target) }
    peer.connection.onsignalingstatechange = function () { handleSignalingStateChangeEvent(peer.connection) }
    peer.connection.oniceconnectionstatechange = function () { handleICEConnectionStateChangeEvent(peer.connection) }
    peer.connection.onicegatheringstatechange = function () { handleICEGatheringStateChangeEvent(peer.connection) }
  },
  async OFFER (state, target) {
    console.log('[RTC] make offer to ' + target)
    var peer = state.peers.find(peer => peer.name === target)

    peer.dataChannel = peer.connection.createDataChannel('dataChannel')
    peer.dataChannel.onmessage = handleDataChannelMessage
    peer.dataChannel.onopen = handleDataChannelStateChangeEvent
    peer.dataChannel.onclose = handleDataChannelStateChangeEvent

    var offer = await peer.connection.createOffer()
    send({
      type: 'offer',
      name: state.name,
      target: target,
      data: offer
    })
    await peer.connection.setLocalDescription(offer)
  },
  async ANSWER (state, { target, offer }) {
    console.log('[RTC] answer to ' + target)
    var peer = state.peers.find(peer => peer.name === target)

    // Permet d'associer le datachannel de la callback au bon peer
    lastPeer = peer

    peer.connection.ondatachannel = handleDataChannelCallback

    await peer.connection.setRemoteDescription(new RTCSessionDescription(offer))

    var answer = await peer.connection.createAnswer()

    await peer.connection.setLocalDescription(answer)

    send({
      type: 'answer',
      name: state.name,
      target: target,
      data: answer
    })
  },
  FINALIZE (state, { target, answer }) {
    var peer = state.peers.find(peer => peer.name === target)
    peer.connection.setRemoteDescription(new RTCSessionDescription(answer))
  },
  CANDIDATE (state, { target, candidate }) {
    var peer = state.peers.find(peer => peer.name === target)
    peer.connection.addIceCandidate(new RTCIceCandidate(candidate))
  },
  LEAVE (state) {
    state.peers.forEach((peer) => {
      peer.dataChannel.close()
      peer.connection.close()
    })
    state.peers = []
  },
  KICK (state, target) {
    var peer = state.peers.find(peer => peer.name === target)
    peer.dataChannel.close()
    peer.connection.close()
    const index = state.peers.indexOf(peer)
    if (index > -1) {
      state.peers.splice(index, 1)
    }
  },
  BROADCAST (state, { message, type }) {
    const data = JSON.stringify({
      type: type,
      message: message
    })
    console.log('[RTC] broadcast message ' + data)
    state.peers.forEach(peer => {
      peer.dataChannel.send(data)
    })
  }
}

function handleICEConnectionStateChangeEvent (connection) {
  console.log('[RTC] ice connection change to ' + connection.iceConnectionState)
}

function handleICEGatheringStateChangeEvent (connection) {
  console.log('[RTC] ice gathering change to ' + connection.iceGatheringState)
}

async function handleNegotiationNeededEvent (target) {
  console.log('[RTC] negotiation needed from ' + target)
}

async function handleSignalingStateChangeEvent (connection) {
  console.log('[RTC] state changed to ' + connection.signalingState)
  switch (connection.signalingState) {
    case 'closed':
      await connection.close()
      break
  }
}

function handleDataChannelStateChangeEvent () {
  console.log('[RTC] data channel state change')
}

function handleDataChannelCallback (event) {
  console.log('[RTC] data channel callback ' + event + ' target ' + event.target)
  var peer = lastPeer
  peer.dataChannel = event.channel
  peer.dataChannel.onmessage = handleDataChannelMessage
  peer.dataChannel.onopen = handleDataChannelStateChangeEvent
  peer.dataChannel.onclose = handleDataChannelStateChangeEvent

  store.dispatch('rtc/broadcast', { message: store.state.room.roomStatus, type: 'status' })
}

function handleDataChannelMessage (event) {
  console.log('[RTC] data channel message ' + event.data)
  var data = JSON.parse(event.data)
  console.log('[RTC] data channel message type ' + data.type)
  switch (data.type) {
    case 'status':
      if (!store.state.room.admin) store.dispatch('room/setRoomStatus', data.message)
      break
    case 'settings':
      if (!store.state.room.admin) store.dispatch('room/setRoomSettings', data.message)
      break
    case 'vote':
      store.dispatch('room/vote', { link: data.message.link, linkID: data.message.linkID, isPositive: data.message.isPositive, voterName: data.message.voterName })
      break
    case 'userCommand':
      if (store.state.room.roomSettings.userControl) store.dispatch('room/setUserCommand', data.message)
      break
  }
}

export default {
  namespaced: true,
  state,
  getters,
  actions,
  mutations
}
