func MoveFile(wsConn *websocket.Conn, fileMoveRequest fileRequests.FileMoveRequest) {
	session, collection := managers.GetMGoCollection("Files")
	defer session.Close()

	// Check that file exists
	file, err := GetFileById(fileMoveRequest.BaseRequest.ResId)
	if err != nil {
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-300, fileMoveRequest.BaseRequest.Tag, nil))
		return
	}

	file.Version++

	err = collection.Update(bson.M{"_id": fileMoveRequest.BaseRequest.ResId}, bson.M{"$set": bson.M{"relative_path": fileMoveRequest.NewPath, "version": file.Version}})
	if err != nil {
		if mgo.IsDup(err) {
			managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-307, fileMoveRequest.BaseRequest.Tag, nil))
			return
		}
		managers.LogError("Error renaming file", err)
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-303, fileMoveRequest.BaseRequest.Tag, nil))
		return
	}

	managers.SendWebSocketMessage(wsConn, baseModels.NewSuccessResponse(fileMoveRequest.BaseRequest.Tag, nil))
	managers.NotifyProjectClients(file.Project, fileMoveRequest.GetNotification(), wsConn)
}
func GetUserByUsername(username string) (*User, error) {
	// Get new DB connection
	session, collection := managers.GetMGoCollection("Users")
	defer session.Close()

	result := new(User)
	err := collection.Find(bson.M{"username": username}).One(&result)
	if err != nil {
		managers.LogError("Failed to retrieve User", err)
		return nil, err
	}

	return result, nil
}
func GetFilesByProjectId(projectId string) ([]File, error) {
	// Get new DB connection
	session, collection := managers.GetMGoCollection("Files")
	defer session.Close()

	var result []File
	err := collection.Find(bson.M{"project": projectId}).All(&result)
	if err != nil {
		managers.LogError("Failed to retrieve Files for project "+projectId, err)
		return nil, err
	}

	return result, nil
}
func GetFileById(id string) (*File, error) {
	// Get new DB connection
	session, collection := managers.GetMGoCollection("Files")
	defer session.Close()

	result := new(File)
	err := collection.Find(bson.M{"_id": id}).One(&result)
	if err != nil {
		managers.LogError("Failed to retrieve File", err)
		return nil, err
	}

	return result, nil
}
func GetChangesByFile(fileId string) ([]FileChange, error) {
	// Get new DB connection
	session, collection := managers.GetMGoCollection("Changes")
	defer session.Close()

	var result []FileChange
	err := collection.Find(bson.M{"file_id": fileId}).Sort("version").All(&result)
	if err != nil {
		managers.LogError("Failed to retrieve FileChanges", err)
		return nil, err
	}

	return result, nil
}
func UserProjects(wsConn *websocket.Conn, userProjectsRequest userRequests.UserProjectsRequest) {

	// Get new DB connection
	session, collection := managers.GetMGoCollection("Projects")
	defer session.Close()

	var projects []projectModels.Project
	if err := collection.Find(bson.M{"permissions." + userProjectsRequest.BaseRequest.Username: bson.M{"$gt": 0}}).All(&projects); err != nil {
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-100, userProjectsRequest.BaseRequest.Tag, nil))
		return
	}

	data := map[string]interface{}{"Projects": projects}

	managers.SendWebSocketMessage(wsConn, baseModels.NewSuccessResponse(userProjectsRequest.BaseRequest.Tag, data))
}
func LookupUser(wsConn *websocket.Conn, userLookupRequest userRequests.UserLookupRequest) {

	// Get new DB connection
	session, collection := managers.GetMGoCollection("Users")
	defer session.Close()

	user := User{}
	if err := collection.Find(bson.M{"username": userLookupRequest.LookupUsername}).One(&user); err != nil {
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-100, userLookupRequest.BaseRequest.Tag, nil))
		return
	}

	data := map[string]interface{}{"User": user}

	managers.SendWebSocketMessage(wsConn, baseModels.NewSuccessResponse(userLookupRequest.BaseRequest.Tag, data))
}
// Rename project (?)
func RenameProject(wsConn *websocket.Conn, projectRenameRequest projectRequests.ProjectRenameRequest) {

	// Get new DB connection
	session, collection := managers.GetMGoCollection("Projects")
	defer session.Close()

	// Rename the project
	err := collection.Update(bson.M{"_id": projectRenameRequest.BaseRequest.ResId}, bson.M{"$set": bson.M{"name": projectRenameRequest.NewName}})
	if err != nil {
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-202, projectRenameRequest.BaseRequest.Tag, nil))
		return
	}

	managers.SendWebSocketMessage(wsConn, baseModels.NewSuccessResponse(projectRenameRequest.BaseRequest.Tag, nil))
	managers.NotifyProjectClients(projectRenameRequest.BaseRequest.ResId, projectRenameRequest.GetNotification(), wsConn)
}
// Grant permission <Level> to <User>
//  - Check if user exists
//  - Grants permission level to user, overwriting if necessary.
func GrantProjectPermissions(wsConn *websocket.Conn, projectGrantPermissionsRequest projectRequests.ProjectGrantPermissionsRequest) {

	project, err := GetProjectById(projectGrantPermissionsRequest.BaseRequest.ResId)
	if err != nil {
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-200, projectGrantPermissionsRequest.BaseRequest.Tag, nil))
		return
	}

	if !CheckUserHasPermissions(project, projectGrantPermissionsRequest.BaseRequest.Username, 5) {
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-207, projectGrantPermissionsRequest.BaseRequest.Tag, nil))
		return
	}

	// Make sure that there is still an owner of the project.
	owner := ""
	for key, value := range project.Permissions {
		if value == 10 && key != projectGrantPermissionsRequest.GrantUsername {
			owner = key
		}
	}
	if owner == "" {
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-205, projectGrantPermissionsRequest.BaseRequest.Tag, nil))
		return
	}

	project.Permissions[projectGrantPermissionsRequest.GrantUsername] = projectGrantPermissionsRequest.PermissionLevel

	// Get new DB connection
	session, collection := managers.GetMGoCollection("Projects")
	defer session.Close()

	// Update permissions
	err = collection.Update(bson.M{"_id": projectGrantPermissionsRequest.BaseRequest.ResId}, bson.M{"$set": bson.M{"permissions": project.Permissions}})
	if err != nil {
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-202, projectGrantPermissionsRequest.BaseRequest.Tag, nil))
		return
	}

	managers.SendWebSocketMessage(wsConn, baseModels.NewSuccessResponse(projectGrantPermissionsRequest.BaseRequest.Tag, nil))
	managers.NotifyProjectClients(projectGrantPermissionsRequest.BaseRequest.ResId, projectGrantPermissionsRequest.GetNotification(), wsConn)
}
func CheckUserAuth(baseRequest baseRequests.BaseRequest) bool {

	// Get new DB connection
	session, collection := managers.GetMGoCollection("Users")
	defer session.Close()

	userAuthData := User{}
	if err := collection.Find(bson.M{"username": baseRequest.Username}).One(&userAuthData); err != nil {
		// Could not find user
		return false
	}

	for _, token := range userAuthData.Tokens {
		if token == baseRequest.Token {
			// Found matching token
			return true
		}
	}

	// No matching token found
	return false
}
func DeleteFile(wsConn *websocket.Conn, fileDeleteRequest fileRequests.FileDeleteRequest) {
	session, collection := managers.GetMGoCollection("Files")
	defer session.Close()

	// Check that file exists
	file, err := GetFileById(fileDeleteRequest.BaseRequest.ResId)
	if err != nil {
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-300, fileDeleteRequest.BaseRequest.Tag, nil))
		return
	}

	err = os.Remove(file.GetPath())

	err = collection.Remove(bson.M{"_id": fileDeleteRequest.BaseRequest.ResId})
	if err != nil {
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-304, fileDeleteRequest.BaseRequest.Tag, nil))
		return
	}

	managers.SendWebSocketMessage(wsConn, baseModels.NewSuccessResponse(fileDeleteRequest.BaseRequest.Tag, nil))
	managers.NotifyProjectClients(file.Project, fileDeleteRequest.GetNotification(), wsConn)
}
// Create new project
func CreateProject(wsConn *websocket.Conn, projectCreateRequest projectRequests.ProjectCreateRequest) {

	// Create new Project object
	project := new(Project)
	project.Id = managers.NewObjectIdString()
	project.Name = projectCreateRequest.Name
	project.ServerPath = project.Id
	project.Permissions = map[string]int{projectCreateRequest.BaseRequest.Username: 10} // Set creator to owner permissions

	// Get new DB connection
	session, collection := managers.GetMGoCollection("Projects")
	defer session.Close()

	// Create the project
	err := collection.Insert(project)
	if err != nil {
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-201, projectCreateRequest.BaseRequest.Tag, nil))
		return
	}

	managers.SendWebSocketMessage(wsConn, baseModels.NewSuccessResponse(projectCreateRequest.BaseRequest.Tag, map[string]interface{}{"ProjectId": project.Id}))
}
func LoginUser(wsConn *websocket.Conn, loginRequest userRequests.UserLoginRequest) {

	// Get new DB connection
	session, collection := managers.GetMGoCollection("Users")
	defer session.Close()

	user, err := GetUserByUsername(loginRequest.Username)
	if err != nil {
		// Could not find user
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-104, loginRequest.BaseRequest.Tag, nil))
		return
	}

	if err := bcrypt.CompareHashAndPassword([]byte(user.Password_Hash), []byte(loginRequest.Password)); err != nil {
		// Password did not match.
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-104, loginRequest.BaseRequest.Tag, nil))
		return
	}

	tokenBytes, err := bcrypt.GenerateFromPassword([]byte(loginRequest.Username+time.Now().String()), bcrypt.DefaultCost)
	if err != nil {
		managers.LogError("Failed to generate token", err)
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-103, loginRequest.BaseRequest.Tag, nil))
		return
	}

	token := string(tokenBytes[:])

	err = addToken(collection, user, token)
	if err != nil {
		managers.LogError("Failed to save token", err)
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-103, loginRequest.BaseRequest.Tag, nil))
		return
	}

	managers.SendWebSocketMessage(wsConn, baseModels.NewSuccessResponse(loginRequest.BaseRequest.Tag, map[string]interface{}{"Token": token}))
}
func RegisterUser(wsConn *websocket.Conn, registrationRequest userRequests.UserRegisterRequest) {

	matched, err := regexp.MatchString("^[\\w]+$", registrationRequest.Username)
	if !matched {
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-106, registrationRequest.BaseRequest.Tag, nil))
		return
	}

	// Hash password using bcrypt
	pwHashBytes, err := bcrypt.GenerateFromPassword([]byte(registrationRequest.Password), bcrypt.DefaultCost)
	if err != nil {
		managers.LogError("Failed to hash password", err)
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-101, registrationRequest.BaseRequest.Tag, nil))
		return
	}

	// Create new UserAuthData object
	userAuthData := new(User)
	userAuthData.Id = managers.NewObjectIdString()
	userAuthData.FirstName = registrationRequest.FirstName
	userAuthData.LastName = registrationRequest.LastName
	userAuthData.Username = registrationRequest.Username
	userAuthData.Email = registrationRequest.Email
	userAuthData.Password_Hash = string(pwHashBytes[:])

	// Get new DB connection
	session, collection := managers.GetMGoCollection("Users")
	defer session.Close()

	// Make sure email is unique
	index := mgo.Index{
		Key:        []string{"email"},
		Unique:     true,
		DropDups:   true,
		Background: true,
		Sparse:     true,
	}
	err = collection.EnsureIndex(index)
	if err != nil {
		managers.LogError("Failed to ensure email index", err)
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-101, registrationRequest.BaseRequest.Tag, nil))
		return
	}

	// Make sure username is unique
	index = mgo.Index{
		Key:        []string{"username"},
		Unique:     true,
		DropDups:   true,
		Background: true,
		Sparse:     true,
	}
	err = collection.EnsureIndex(index)
	if err != nil {
		managers.LogError("Failed to ensure username index", err)
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-101, registrationRequest.BaseRequest.Tag, nil))
		return
	}

	// Register new user
	err = collection.Insert(userAuthData)
	if err != nil {
		// Duplicate entry
		if mgo.IsDup(err) {
			managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-102, registrationRequest.BaseRequest.Tag, nil))
			return
		}
		managers.LogError("Error registering user", err)
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-101, registrationRequest.BaseRequest.Tag, nil))
		return
	}

	managers.SendWebSocketMessage(wsConn, baseModels.NewSuccessResponse(registrationRequest.BaseRequest.Tag, nil))
}
func CreateFile(wsConn *websocket.Conn, fileCreateRequest fileRequests.FileCreateRequest) {

	file := new(File)
	file.Id = managers.NewObjectIdString()
	file.Name = fileCreateRequest.Name
	file.RelativePath = fileCreateRequest.RelativePath
	file.Version = 0
	file.Project = fileCreateRequest.ProjectId

	session, collection := managers.GetMGoCollection("Files")
	defer session.Close()

	// Create indexes
	index := mgo.Index{
		Key:        []string{"name", "relative_path"},
		Unique:     true,
		DropDups:   true,
		Background: true,
		Sparse:     true,
	}
	err := collection.EnsureIndex(index)
	if err != nil {
		managers.LogError("Failed to ensure file name/path index", err)
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-301, fileCreateRequest.BaseRequest.Tag, nil))
		return
	}

	// Insert file record
	err = collection.Insert(file)
	if err != nil {
		if mgo.IsDup(err) {
			managers.LogError("Error creating file record", err)
			managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-305, fileCreateRequest.BaseRequest.Tag, nil))
			return
		}
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-301, fileCreateRequest.BaseRequest.Tag, nil))
		return
	}

	// Write file to disk

	fileCreateRequest.RelativePath = filepath.Clean(fileCreateRequest.RelativePath)
	if fileCreateRequest.RelativePath[0:2] == ".." || filepath.IsAbs(fileCreateRequest.RelativePath) {
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-308, fileCreateRequest.BaseRequest.Tag, nil))
		return
	}

	err = os.MkdirAll("files/"+fileCreateRequest.ProjectId+"/"+fileCreateRequest.RelativePath, os.ModeExclusive)
	if err != nil {
		managers.LogError("Failed to create file directory", err)
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-301, fileCreateRequest.BaseRequest.Tag, nil))
		return
	}
	err = ioutil.WriteFile(file.GetPath(), fileCreateRequest.FileBytes, os.ModeExclusive)
	if err != nil {
		managers.LogError("Failed to write file", err)
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-301, fileCreateRequest.BaseRequest.Tag, nil))
		return
	}

	managers.SendWebSocketMessage(wsConn, baseModels.NewSuccessResponse(fileCreateRequest.BaseRequest.Tag, map[string]interface{}{"FileId": file.Id}))
	managers.NotifyProjectClients(file.Project, fileCreateRequest.GetNotification(file.Id), wsConn)
}
func InsertChange(wsConn *websocket.Conn, fileChangeRequest fileRequests.FileChangeRequest) {

	// Check that file exists
	file, err := GetFileById(fileChangeRequest.BaseRequest.ResId)
	if err != nil {
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-300, fileChangeRequest.BaseRequest.Tag, nil))
		return
	}

	// Check that user is on latest version, then increment. Otherwise, throw error
	if fileChangeRequest.FileVersion < file.Version {
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-401, fileChangeRequest.BaseRequest.Tag, nil))
		return
	}
	fileChangeRequest.FileVersion++

	fileChange := new(FileChange)
	fileChange.Id = managers.NewObjectIdString()
	fileChange.Changes = fileChangeRequest.Changes
	fileChange.FileId = fileChangeRequest.BaseRequest.ResId
	fileChange.Version = fileChangeRequest.FileVersion
	fileChange.Username = fileChangeRequest.BaseRequest.Username
	fileChange.Date = time.Now().UTC()

	changesSession, changesCollection := managers.GetMGoCollection("Changes")
	defer changesSession.Close()

	index := mgo.Index{
		Key:        []string{"file", "version"},
		Unique:     true,
		DropDups:   true,
		Background: true,
		Sparse:     true,
	}
	err = changesCollection.EnsureIndex(index)
	if err != nil {
		managers.LogError("Failed to ensure changes index", err)
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-400, fileChangeRequest.BaseRequest.Tag, nil))
	}

	err = changesCollection.Insert(fileChange)
	if err != nil {
		if mgo.IsDup(err) {
			managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-401, fileChangeRequest.BaseRequest.Tag, nil))
			return
		}
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-400, fileChangeRequest.BaseRequest.Tag, nil))
		return
	}

	filesSession, filesCollection := managers.GetMGoCollection("Files")
	defer filesSession.Close()
	err = filesCollection.Update(bson.M{"_id": fileChangeRequest.BaseRequest.ResId}, bson.M{"$set": bson.M{"version": fileChangeRequest.FileVersion}})
	if err != nil {
		managers.SendWebSocketMessage(wsConn, baseModels.NewFailResponse(-400, fileChangeRequest.BaseRequest.Tag, nil))
		return
	}

	// TODO: Change when fileVersion is changed to atomic int
	if fileChange.Version%100 == 0 {
		scrunching.ScrunchDB(fileChange.FileId)
	}

	managers.SendWebSocketMessage(wsConn, baseModels.NewSuccessResponse(fileChangeRequest.BaseRequest.Tag, map[string]interface{}{"FileVersion": fileChangeRequest.FileVersion}))
	managers.NotifyProjectClients(file.Project, fileChangeRequest.GetNotification(fileChangeRequest.FileVersion), wsConn)
}