
maquette        = require "maquette"
nmd             = require "nano-markdown"
LoginForm       = require "../lib/authd/client/login-form.ls"
todows          = require "./todowebsocket.ls"
AuthWS          = require "./authws.ls"
bulma           = require "./bulma.ls"
Task            = require "./task.ls"
Project         = require "./project.ls"
Modal           = require "./modal.ls"
Navbar          = require "./navbar.ls"
UsersCache      = require "./users-cache.ls"

{create-projector, h} = maquette
projector = create-projector!

model = {
	# view: login, project-list, project, network-error (TODO: other views, such as rights)
	# FIXME: Replace model.current-view by an object that has a .render
	#        method.
	current-view: "login"

	viewed-project: void

	# list of Project objects
	project-list: []

	port: 9999
	authd-url: undefined
	todod-url: undefined

	authd-ws: undefined
	todod-ws: undefined

	previous-error: undefined
	error: undefined

	# { uid => user data }
	users: {}

	# uninitialized UserCache
	users-cache: void
}

model.authd-url =
	(if location.protocol == 'https' then 'wss' else 'ws') +
	'://' + location.hostname + \: +
	model.port +
	"/auth.JSON"

model.todod-url =
	(if location.protocol == 'https' then 'wss' else 'ws') +
	'://' + location.hostname + \: +
	model.port +
	"/todo.JSON"

console.log "authd url: " + model.authd-url
console.log "todod url: " + model.todod-url

#
# network configuration
#

model.authd-ws = AuthWS model.authd-url
model.todod-ws = todows.create-socket model.todod-url


model.users-cache = UsersCache model.authd-ws, ->
	projector.schedule-render!

#
# authd messages management
#

# authd socket errors

authd-on-websocket-error = (event) ->
	console.log "WebSocket error.", event
	model.current-view := "network-error"
	model.last-network-error := event
	projector.schedule-render!

authd-on-websocket-close = (event) ->
	if model.current-view == "network-error"
		return

	model.current-view := "login"
	console.log "WebSocket has been closed.", event
	# model.todod-ws.reopen!

	projector.schedule-render!

# record changes that need to happen on a network event
model.authd-ws.user-on-socket-error ++= [ authd-on-websocket-error ]
model.authd-ws.user-on-socket-close ++= [ authd-on-websocket-close ]


# authd message handlers

# HANDLED with the "on-login" callback in the LoginForm component
# model.authd-ws.add-event-listener \token, (message) ->
# 	model.current-view := "project-list"
# 	model.authd-ws.token := message.token
# 	model.todod-ws.token := message.token
# 	model.todod-ws.list-lists!
# 	model.authd-ws.list-users!
# 	projector.schedule-render!

model.authd-ws.add-event-listener \error, (message) ->
	console.log "authd error", message
	projector.schedule-render!

model.authd-ws.add-event-listener \user, (message) ->
	model.users[message.user.uid] := message.user
	projector.schedule-render!

model.authd-ws.add-event-listener \users-list, (message) ->
	console.log "Received users: ", message
	message.users.map (user) ->
		model.users[user.uid] := user
	projector.schedule-render!

# TODO: user-added, user-edited


#
# todod messages management
#

# todod socket errors

on-websocket-error = (event) ->
	console.log "WebSocket error.", event
	model.current-view := "network-error"
	model.last-network-error := event
	projector.schedule-render!

on-websocket-close = (event) ->
	if model.current-view == "network-error"
		return

	model.current-view := "login"
	console.log "WebSocket has been closed.", event
	# model.todod-ws.reopen!

	projector.schedule-render!

# record changes that need to happen on a network event
model.todod-ws.user-on-socket-error ++= [ on-websocket-error ]
model.todod-ws.user-on-socket-close ++= [ on-websocket-close ]

model.todod-ws.add-event-listener \lists-list, (message) ->
	console.log "Projects list received", message

	model.project-list := message.lists.map (x) ->
		old-project = model.project-list.find((.id == x.id))
		new-project = Project x, model.todod-ws, model.users-cache

		if old-project && new-project.id == old-project.id
			new-project.tasks = old-project.tasks

		new-project

	projector.schedule-render!

model.todod-ws.add-event-listener \new-list, (message) ->
	console.log "New project", message

	model.project-list := model.project-list ++ [ (Project message.list, model.todod-ws, model.users-cache) ]
	projector.schedule-render!

model.todod-ws.add-event-listener \list-updated, (message) ->
	console.log "Project updated", message

	new-project = Project message.list, model.todod-ws, model.users-cache

	model.project-list := model.project-list.map (project) ->
		if project.id == message.list.id
			new-project
		else
			project

	if model.viewed-project && model.viewed-project.id == message.list.id
		# create tasks in order to have the updated project properties
		new-project.tasks = model.viewed-project.tasks.map (task) -> Task task, new-project, model.todod-ws, model.users-cache
		model.viewed-project = new-project

	projector.schedule-render!

model.todod-ws.add-event-listener \list-removed, (message) ->
	console.log "A list has been removed", message

	if model.current-view == "project" && model.viewed-project.id == message.list
		model.current-view := "project-list"
		model.viewed-project := void
		projector.schedule-render!

	model.project-list := model.project-list.filter((.id != message.list))


# tasks

model.todod-ws.add-event-listener \tasks, (message) ->
	console.log "Tasks received", message

	project = model.project-list.find((.id == message.list))
	project.tasks := message.tasks.map (e) -> Task e, project, model.todod-ws, model.users-cache

	if model.viewed-project && model.viewed-project.id == project.id
		model.viewed-project := project

	projector.schedule-render!

model.todod-ws.add-event-listener \task-created, (message) ->
	console.log "A task has been created", message

	task = message.task
	list = model.project-list.find((.id == task.list))
	if model.viewed-project && list.id == model.viewed-project.id
		model.viewed-project := list

	if list
		list.tasks ++= [ Task task, list, model.todod-ws, model.users-cache ]
		projector.schedule-render!


model.todod-ws.add-event-listener \task-updated, (message) ->
	console.log "A task has been updated", message

	task = message.task
	list = model.project-list.find((.id == task.list))

	if list
		list.tasks = list.tasks.map (e) ->
			if e.id == task.id
				Task task, list, model.todod-ws, model.users-cache
			else
				e
		projector.schedule-render!

model.todod-ws.add-event-listener \task-removed, (message) ->
	console.log "A task has been removed", message

	task = message.task
	for project in model.project-list
		if project.tasks.find((.id == task))
			project.tasks := project.tasks.filter((.id != task))
			projector.schedule-render!

render-project-list = ->
	h \div.cards-list model.project-list.map (project) ->
		h \div.card.is-grey {
			key: project.id
			onclick: ->
				model.current-view := "project"
				model.viewed-project := project
				model.todod-ws.subscribe project.id
				model.todod-ws.get-list project.id
				model.todod-ws.get-tasks project.id
		} [
			h \div.card-content [
				bulma.title 3 project.title
			]
		]

render-project = (project) ->
	if project
		project.render!
	else
		h \div.notification.is-error [
			bulma.title 3 "Error, we did not get the project id " + project.id
		]

model.login-form = LoginForm {
	authws-url: model.authd-url
	schedule-render: -> projector.schedule-render!
	on-login: (user, token) ->
		# FIXME: May double-login if the user clicks the “login” button too much.
		if model.current-view != "login"
			return

		model.current-view := "project-list"
		model.authd-ws.token := token
		model.todod-ws.token := token
		model.todod-ws.list-lists!
		model.authd-ws.list-users!
		projector.schedule-render!
}

navbar = Navbar!

render-body = ->
	h \div#body [
		navbar.render model

		h \div#main-section [
			switch model.current-view
			when "login"
				h \div.columns [
					h \div.column
					h \div.column [
						model.login-form.render!
					]
					h \div.column
				]

			when "project-list"
				render-project-list!
			when "project"
				render-project model.viewed-project
			when "network-error"
				h \div.container [
					h \div.notification.is-danger [
						h \div.title.is-2 [ "Socket error!" ]

						h \div.content [
							h \p [ "We’re very sorry, but something got very wrong between here and the backend." ]
							h \p [ "Please check your network connection and refresh the page." ]

							h \p.small [ "The error happened on " + model.last-network-error.explicit-original-target.url ]
						]
					]
				]

			else
				h \div.notification.is-error [
					"Wait, what? Internal error!"
				]
		]

		if model.modal
			model.modal.render!
	]

document.add-event-listener 'DOMContentLoaded' ->
	projector.append document.body, render-body

