/*
 * DataBag
 *
 * DataBag provides storage for decentralized identity based self-hosting apps. It is intended to support sharing of personal data and hosting group conversations. 
 *
 * API version: 0.0.1
 * Contact: roland.osborne@gmail.com
 * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
 */
package databag

import (
  "log"
  "sync"
	"net/http"
  "encoding/json"
  "github.com/gorilla/websocket"
  "databag/internal/store"
)

type accountRevision struct {
  ProfileRevision int64
  ContentRevision int64
  ViewRevision int64
  GroupRevision int64
  LabelRevision int64
  CardRevision int64
  DialogueRevision int64
  InsightRevision int64
}

var wsSync sync.Mutex
var wsExit = make(chan bool, 1)
var statusListener = make(map[uint][]chan<-[]byte)
var upgrader = websocket.Upgrader{}

func Status(w http.ResponseWriter, r *http.Request) {

  // accept websocket connection
  conn, err := upgrader.Upgrade(w, r, nil)
  if err != nil {
      log.Println("Status: failed upgrade connection")
      return
  }
  defer conn.Close()

  log.Println("CONNECTED")
  // receive announce message
  var act uint = 0

  // get revisions for the account
  var ar accountRevision
  err = store.DB.Model(&Revision{}).Where("ID = ?", act).First(&ar).Error
  if err != nil {
    log.Println("Status - failed to get account revision")
//    return
  }
  rev := getRevision(ar)

  // send current version
  var msg []byte
  msg, err = json.Marshal(rev)
  if err != nil {
    log.Println("Status - failed to marshal revision")
    return
  }
  err = conn.WriteMessage(websocket.TextMessage, msg)
  if err != nil {
    log.Println("Status - failed to send initial revision")
    return
  }

  // open channel for revisions
  c := make(chan []byte)
  defer close(c)

  // register channel for revisions
  AddStatusListener(0, c)
  defer RemoveStatusListener(act, c)

  // send revision until channel is closed
  for {
		select {
		case msg := <-c:
      err = conn.WriteMessage(websocket.TextMessage, msg)
      if err != nil {
        log.Println("Status - failed to send revision, closing")
        return
      }
		case <-wsExit:
			log.Println("Status - server exit")
      wsExit<-true
			return
		}
	}
}

func getRevision(rev accountRevision) Revision {
  var r Revision
  r.Profile = rev.ProfileRevision
  r.Content = rev.ContentRevision
  r.Label = rev.LabelRevision
  r.Group = rev.GroupRevision
  r.Card = rev.CardRevision
  r.Dialogue = rev.DialogueRevision
  r.Insight = rev.InsightRevision
  return r
}

func ExitStatus() {
  wsExit <- true
}

func SetStatus(act uint) {

  // get revisions for the account
  var ar accountRevision
  err := store.DB.Model(&Revision{}).Where("ID = ?", act).First(&ar).Error
  if err != nil {
    log.Println("SetStatus - failed to retrieve account revisions")
    return
  }
  rev := getRevision(ar)
  var msg []byte
  msg, err = json.Marshal(rev)
  if err != nil {
    log.Println("SetStatus - failed to marshal revision")
    return
  }

  // lock access to statusListener
  wsSync.Lock()
  defer wsSync.Unlock()

  // notify all listeners
  chs, ok := statusListener[act]
  if ok {
    for _, ch := range chs {
      ch <- msg
    }
  }
}

func AddStatusListener(act uint, ch chan<-[]byte) {

  // lock access to statusListener
  wsSync.Lock()
  defer wsSync.Unlock()

  // add new listener to map
  chs, ok := statusListener[act]
  if ok {
    chs = append(chs, ch)
  } else {
    statusListener[act] = []chan<-[]byte{ch}
  }
}

func RemoveStatusListener(act uint, ch chan<-[]byte) {

  // lock access to statusListener
  wsSync.Lock()
  defer wsSync.Unlock()

  // remove channel from map
  chs, ok := statusListener[act]
  if ok {
    for i, c := range chs {
      if ch == c {
        if len(chs) == 1 {
          delete(statusListener, act)
        } else {
          chs[i] = chs[len(chs)-1]
          statusListener[act] = chs[:len(chs)-1]
        }
      }
    }
  }
}