package flowserver

import (
	"encoding/json"
	"errors"
	"flow"
	"flowserver/flowmsg"
	"log"
	"net/http"
	"sync"

	"github.com/gorilla/websocket"
)

//FlowSessionManager or FlowServerCore
type FlowSessionManager struct {
	// List of flow sessions?
	sessions map[string]*FlowSession
	chats    map[string]*ChatRoom

	sync.Mutex
}

//NewFlowSessionManager creates a New initialized FlowSessionManager
func NewFlowSessionManager() *FlowSessionManager {
	return &FlowSessionManager{
		sessions: map[string]*FlowSession{},
	}
}

//CreateSession creates a new session
func (fsm *FlowSessionManager) CreateSession() *FlowSession {
	fsm.Lock()
	defer fsm.Unlock()
	for {
		ID := flow.RandString(10)
		sess, ok := fsm.sessions[ID]
		if !ok {
			sess = NewSession(ID)
			fsm.sessions[ID] = sess
			return sess
		}
	}
}

//LoadSession loads or creates a new Session
func (fsm *FlowSessionManager) LoadSession(ID string) (*FlowSession, error) {
	fsm.Lock()
	defer fsm.Unlock()
	if ID == "" {
		return nil, errors.New("Cannot be null")
	}
	sess, ok := fsm.sessions[ID]
	if !ok {
		sess = NewSession(ID)
		fsm.sessions[ID] = sess
	}
	return sess, nil

}

var upgrader = websocket.Upgrader{}

func (fsm *FlowSessionManager) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	c, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println("upgrade:", err)
		return
	}
	defer c.Close()

	// Room
	var sess *FlowSession
	defer func() {
		if sess == nil {
			return
		}
		// Remove client on exit
		sess.ClientRemove(c)
	}()

	/////////////////
	// Message handling
	// ////////

	// Websocket IO loop
	for {
		mt, data, err := c.ReadMessage()
		if err != nil {
			log.Println("Err:", err)
			break
		}
		if mt != websocket.TextMessage {
			log.Println("Not a text message?")
			break
		}

		m := flowmsg.RecvMessage{}
		err = json.Unmarshal(data, &m)
		if err != nil {
			log.Println("Err parsing message:", err)
			c.WriteJSON("bye")
			break
		}
		switch m.OP {
		/////////////////////////////
		// NEWSESSION request
		//////////////////
		case "sessionNew":
			err = func() error {
				log.Println("We want a new session so")
				sess = fsm.CreateSession()
				return sess.ClientAdd(c)
			}()
			//////////////////////////////////
			// LOADSESSION request
			////////////////////////////////////
		case "sessionLoad":
			err = func() error {
				sessID := string(m.ID)

				if sess != nil {
					sess.ClientRemove(c)
				}
				sess, err = fsm.LoadSession(sessID) // Set our session
				if err != nil {
					return err
				}
				return sess.ClientAdd(c)
			}()

		///////////////////////
		// DOCUMENTUPDATE Receive a document
		//////
		case "documentUpdate":
			err = func() error {
				sess, err = fsm.LoadSession(m.ID)
				if err != nil {
					return err
				}
				return sess.DocumentUpdate(c, m.Data)
			}()
		case "nodeUpdate":
			err = func() error {
				if sess == nil {
					return errors.New("nodeUpdate: invalid session")
				}
				return sess.Broadcast(c, flowmsg.SendMessage{OP: m.OP, Data: m.Data})
			}()
		case "nodeProcess":
			err = func() error {
				if sess == nil {
					return errors.New("nodeRun: invalid session")
				}
				return sess.NodeRun(c, m.Data)
			}()
		case "chatJoin":
			err = func() error {
				if sess == nil {
					return nil // just do nothing
				}
				var handle string
				json.Unmarshal(m.Data, &handle)
				log.Println("Joining with:", handle)
				sess.ChatJoin(c, handle)
				return nil
			}()
		case "chatRename":
			err = func() error {
				if sess == nil {
					return nil // just do nothing
				}
				var handle string
				json.Unmarshal(m.Data, &handle)
				sess.Chat.ClientRename(c, handle)
				return nil
			}()

		case "chatEvent":
			err = func() error {
				if sess == nil {
					return errors.New("invalid session")
				}
				sess.Chat.Event(c, m.Data)
				return nil
			}()
		}

		if err != nil {
			log.Println("Err Writing", err)
		}
	}
	log.Println("ws Is disconnecting", r.RemoteAddr)
}
