less = Npm.require('less')
autoprefixer = Npm.require('less-plugin-autoprefix')
crypto = Npm.require('crypto')

logger = new Logger 'rocketchat:theme',
	methods:
		stop_rendering:
			type: 'info'


WebApp.rawConnectHandlers.use (req, res, next) ->
	path = req.url.split("?")[0]
	prefix = __meteor_runtime_config__.ROOT_URL_PATH_PREFIX || ''
	if (path == "#{prefix}/__cordova/theme.css" || path == "#{prefix}/theme.css")
		css = RocketChat.theme.getCss()
		hash = crypto.createHash('sha1').update(css).digest('hex')
		res.setHeader('Content-Type', 'text/css; charset=UTF-8')
		res.setHeader('ETag', '"' + hash + '"')
		res.write(css)
		res.end()
	else
		next()

calculateClientHash = WebAppHashing.calculateClientHash
WebAppHashing.calculateClientHash = (manifest, includeFilter, runtimeConfigOverride) ->
	css = RocketChat.theme.getCss()

	if css.trim() isnt ''
		hash = crypto.createHash('sha1').update(css).digest('hex')

		themeManifestItem = _.find manifest, (item) -> return item.path is 'app/theme.css'
		if not themeManifestItem?
			themeManifestItem = {}
			manifest.push themeManifestItem

		themeManifestItem.path = 'app/theme.css'
		themeManifestItem.type = 'css'
		themeManifestItem.cacheable = true
		themeManifestItem.where = 'client'
		themeManifestItem.url = "/theme.css?#{hash}"
		themeManifestItem.size = css.length
		themeManifestItem.hash = hash

	calculateClientHash.call this, manifest, includeFilter, runtimeConfigOverride

RocketChat.theme = new class
	variables: {}
	packageCallbacks: []
	files: [
		'server/lesshat.less'
		'server/colors.less'
	]

	constructor: ->
		@customCSS = ''

		RocketChat.settings.add 'css', ''
		RocketChat.settings.addGroup 'Layout'

		RocketChat.settings.onload 'css', Meteor.bindEnvironment (key, value, initialLoad) =>
			if not initialLoad
				Meteor.startup ->
					process.emit('message', {refresh: 'client'})

		@compileDelayed = _.debounce Meteor.bindEnvironment(@compile.bind(@)), 100

		Meteor.startup =>
			RocketChat.settings.onAfterInitialLoad =>

				RocketChat.settings.get '*', Meteor.bindEnvironment (key, value, initialLoad) =>
					if key is 'theme-custom-css'
						if value?
							@customCSS = value
					else if /^theme-.+/.test(key) is true
						name = key.replace /^theme-[a-z]+-/, ''
						if @variables[name]?
							@variables[name].value = value
					else
						return

					@compileDelayed()

	compile: ->
		content = [
			@getVariablesAsLess()
		]

		content.push Assets.getText file for file in @files

		for packageCallback in @packageCallbacks
			result = packageCallback()
			if _.isString result
				content.push result

		content.push @customCSS

		content = content.join '\n'

		options =
			compress: true
			plugins: [
				new autoprefixer()
			]

		start = Date.now()
		less.render content, options, (err, data) ->
			logger.stop_rendering Date.now() - start
			if err?
				return console.log err

			RocketChat.settings.updateById 'css', data.css

			Meteor.startup ->
				Meteor.setTimeout ->
					process.emit('message', {refresh: 'client'})
				, 200

	addVariable: (type, name, value, section, persist=true, editor, allowedTypes) ->
		@variables[name] =
			type: type
			value: value

		if persist is true
			config =
				group: 'Layout'
				type: type
				editor: editor or type
				section: section
				public: false
				allowedTypes: allowedTypes

			RocketChat.settings.add "theme-#{type}-#{name}", value, config

	addPublicColor: (name, value, section, editor='color') ->
		@addVariable 'color', name, value, section, true, editor, ['color', 'expression']

	addPublicFont: (name, value) ->
		@addVariable 'font', name, value, 'Fonts', true

	getVariablesAsObject: ->
		obj = {}
		for name, variable of @variables
			obj[name] = variable.value

		return obj

	getVariablesAsLess: ->
		items = []
		for name, variable of @variables
			items.push "@#{name}: #{variable.value};"

		return items.join '\n'

	addPackageAsset: (cb) ->
		@packageCallbacks.push cb
		@compileDelayed()

	getCss: ->
		return RocketChat.settings.get('css') or ''
