;(function () {
	var m = (typeof global !== 'undefined')
		? (global.m || require('mithril'))
		: window.m

	if (!m) throw ("mithril-datepicker can't find Mithril.js")

	var days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
	var months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
	// var prevNextTitles = ['1 Mo', '1 Yr', '10 Yr']
	var prevNextTitles = ['', '', '']
	var weekStart = 0
	var locale = 'en-us'
	var formatOptions = null

	/***************************************
	 *
	 * actions
	 *
	 ***************************************/

	function chooseDate(props, e) {
		var box = e.target
		var selectedDate = parseInt(box.textContent)
		var dateObj = props.date
		if (box.classList.contains('other-scope')) {
			dateObj.setFullYear(dateObj.getFullYear(), dateObj.getMonth() + (selectedDate > 6 ? -1 : 1), selectedDate)
		} else {
			dateObj.setDate(selectedDate)
		}
	}

	function dismissAndCommit(props, onchange) {
		props.view = 0
		props.active = false
		if (onchange) onchange(props.date)
	}

	function prevNext(props, delta){
		var newDate = new Date(props.date)
		switch (props.view) {
			case 0:
				newDate.setMonth(newDate.getMonth() + delta)
				break
			case 1:
				newDate.setFullYear(newDate.getFullYear() + delta)
				break
			default:
				newDate.setFullYear(newDate.getFullYear() + (delta * 10))
		}
		props.date = pushToLastDay(props.date, newDate)
	}

	/***************************************
	 *
	 * utility
	 *
	 ***************************************/

	function adjustedProps(date, delta) {
		var month = date.getMonth() + delta, year = date.getFullYear()
		var over = month > 11, under = month < 0
		return {
			month: over ? 0 : under ? 11 : month,
			year: over ? year + 1 : under ? year - 1 : year
		}
	}

	function lastDateInMonth(date, delta) {
		var obj = adjustedProps(date, delta)
		if ([0, 2, 4, 6, 7, 9, 11].indexOf(obj.month) > -1) return 31 // array of 31-day props.months
		if (obj.month === 1) { // February
			if (!(obj.year % 400)) return 29
			if (!(obj.year % 100)) return 28
			return (obj.year % 4) ? 28 : 29
		}
		return 30
	}

	function pushToLastDay(oldDate, newDate) {
		if (oldDate.getDate() !== newDate.getDate()) {
			newDate.setMonth(newDate.getMonth() - 1, lastDateInMonth(newDate, -1))
		}
		return newDate
	}

	function stringsForLocale(locale) {
		var date = new Date('jan 1 2017'), _months = [], _days = [] // 1/1/2017 was month:0 and weekday:0, so perfect
		while (_days.length < 7) {
			_days.push(date.toLocaleDateString(locale, { weekday: 'long' }))
			date.setDate(date.getDate() + 1)
		}
		while (_months.length < 12) {
			_months.push(date.toLocaleDateString(locale, { month: 'long' }))
			date.setMonth(date.getMonth() + 1)
		}
		return { days: _days, months: _months }
	}

	function wrapAround(idx, array) {
		var len = array.length
		var n = idx >= len ? idx - len : idx
		return array[n]
	}

	/***************************************
	 *
	 * generators
	 *
	 ***************************************/

	function daysFromLastMonth(props){
		var month = props.date.getMonth(), year = props.date.getFullYear()
		var firstDay = (new Date(year, month, 1)).getDay() - props.weekStart
		if (firstDay < 0) firstDay += 7
		var array = []
		var lastDate = lastDateInMonth(props.date, -1)
		var offsetStart = lastDate - firstDay + 1
		for (var i=offsetStart; i<=lastDate; i++) { array.push(i) }
		return array
	}

	function daysFromThisMonth(props) {
		var max = lastDateInMonth(props.date, 0)
		var array = []
		for (var i=1; i<=max; i++) {
			array.push(i)
		}
		return array
	}

	function daysFromNextMonth(prev, these) {
		var soFar = prev.concat(these)
		var mod = soFar.length % 7
		var array = []
		if (mod > 0) {
			var n = 7 - mod
			for (var i=1; i<=n; i++) { array.push(i) }
		}
		return array
	}

	function defaultDate() {
		var now = new Date()
		now.setHours(0, 0, 0, 0)
		return now
	}

	function yearsForDecade(date) {
		var year = date.getFullYear()
        var array = []
        var start = year - (year % 10)
        for (var i=start; i<=start+10; i++) { array.push(i) }
		return array
	}

	/***************************************
	 *
	 * view helpers
	 *
	 ***************************************/

	function classForBox(a, b) { return a === b ? 'chosen' : '' }

	function displayDate(props) {
		return props.date
			.toLocaleDateString(props.locale, props.formatOptions || {
				weekday: 'short',
				month: 'short',
				day: 'numeric',
				year: 'numeric'
			})
	}

	/***************************************
	 *
	 * components
	 *
	 ***************************************/

	var Header = {
		view: function (vnode) {
			var props = vnode.attrs.props
			var date = props.date
			var theseMonths = props.months || months
			return m('.header'
				// , m('.button-bg', { class: 'v' + props.view })
				// , m('.fake-border')
				, m('.button.prev'
					, { onclick: prevNext.bind(null, props, -1) }
					, prevNextTitles[props.view]
				)
				, m('.button.segment', { onclick: function () { props.view = 0 } }, m(".number", date.getDate()))
				, m('.button.segment', { onclick: function () { props.view = 1 } }, m(".number", theseMonths[date.getMonth()].substr(0, 3)))
				, m('.button.segment', { onclick: function () { props.view = 2 } }, m(".number", date.getFullYear()))
				, m('.button.next'
					, { onclick: prevNext.bind(null, props, 1) }
					, prevNextTitles[props.view]
				)
			)
		}
	}

	var MonthView = {
		view: function (vnode) {
			var props = vnode.attrs.props
			var prevDates = daysFromLastMonth(props)
			var theseDates = daysFromThisMonth(props)
			var nextDates = daysFromNextMonth(prevDates, theseDates)
			var theseWeekdays = props.days || days
			return m('.calendar'
				, m('.weekdays'
					, theseWeekdays.map(function (_, idx) {
						var day = wrapAround(idx + props.weekStart, theseWeekdays)
						return m('.day.dummy', day.substring(0, 2))
					})
				)
				, m('.weekdays'
					, {
						onclick: function(e){
							chooseDate(props, e)
							dismissAndCommit(props, vnode.attrs.onchange)
						}
					}
					, prevDates.map(function (date) {
						return m('.button.day.other-scope', m(".number", date))
					})
					, theseDates.map(function (date) {
						return m('.button.day'
							, { class: classForBox(props.date.getDate(), date) }
							, m('.number', date)
						)
					})
					, nextDates.map(function (date) {
						return m('.button.day.other-scope', date)
					})
				)
			)

		}
	}

	var YearView = {
		view: function (vnode) {
			var props = vnode.attrs.props
			var theseMonths = props.months || months
			return m('.calendar'
				, m('.months'
					, theseMonths.map(function (month, idx) {
						return m('.button.month'
							, {
								class: classForBox(props.date.getMonth(), idx),
								onclick: function () {
									var newDate = new Date(props.date)
									newDate.setMonth(idx)
									props.date = pushToLastDay(props.date, newDate)
									props.view = 0
								}
							}
							, m('.number', month.substring(0, 3))
						)
					})
				)
			)
		}
	}

	var DecadeView = {
		view: function (vnode) {
			var props = vnode.attrs.props
			var decade = yearsForDecade(props.date)
			return m('.calendar'
				, m('.years'
                    , {
                        style: {
                            display: "flex",
                            flexWrap: "wrap"
                        }
                    }
					, decade.map(function (year) {
						return m('.button.year'
							, {
								class: classForBox(props.date.getFullYear(), year),
								onclick: function () {
									var newDate = new Date(props.date)
									newDate.setFullYear(year)
									props.date = pushToLastDay(props.date, newDate)
									props.view = 1
								}
							}
							, m('.number', year)
						)
					})
				)
			)
		}
	}

	var Editor = {
		oncreate: function (vnode) {
			requestAnimationFrame(function () { vnode.dom.classList.add('active') })
		},
		onbeforeremove: function (vnode) {
			vnode.dom.classList.remove('active')
			return new Promise(function (done) { setTimeout(done, 200) })
		},
		view: function (vnode) {
			var props = vnode.attrs.props
			return m('.editor'
                , {
                    style: {
                        display: "table-cell",
                        verticalAlign: "middle"
                    }
                }
				, m(Header, { props: props })
				, m('.sled'
					, { class: 'p' + props.view }
					, m(MonthView, { props: props, onchange: vnode.attrs.onchange })
					, m(YearView, { props: props })
					, m(DecadeView, {props: props })
				)
			)
		}
	}

	var DatePicker = {
		localize: function (loc) {
			if (loc) {
				prevNextTitles = loc.prevNextTitles || prevNextTitles
				locale = loc.locale || locale
				formatOptions = loc.formatOptions || formatOptions
				weekStart = typeof loc.weekStart === 'number'
					? loc.weekStart
					: weekStart

				var strings = stringsForLocale(locale)
				days = strings.days
				months = strings.months
			}
		},
		oninit: function (vnode) {
			var attrs = vnode.attrs
			var props = {
				date: new Date(attrs.date || defaultDate()),
				active: false,
				view: 0
			}

			;['prevNextTitles', 'locale', 'formatOptions'].forEach(function (prop) {
				props[prop] = attrs[prop] || eval(prop)
			})
			props.weekStart = typeof attrs.weekStart === 'number' ? attrs.weekStart : weekStart

			if (attrs.locale && attrs.locale !== locale) {
				var strings = stringsForLocale(props.locale)
				props.days = strings.days
				props.months = strings.months
			}

			vnode.state.props = props
		},
		view: function(vnode){
			var props = vnode.state.props
			var displayText = displayDate(props)
			return m('.mithril-date-picker-container'
				, { class: props.active ? 'active' : '' }
				, m('.mithril-date-picker'
                    , {
                        style: {
                            display: "table"
                        }
                    }
					, m('.button.current-date'
						, {
							onclick: function(){
								if (props.active) props.view = 0
								props.active = !props.active
							}
						}
						, displayText
					)
					, props.active && m('.overlay', { onclick: dismissAndCommit.bind(null, props, vnode.attrs.onchange) })
					, props.active && m(Editor, { props: props, onchange: vnode.attrs.onchange })
				)
			)
		}
	}

	if (typeof module === 'object') module.exports = DatePicker
	else if (typeof window !== 'undefined') window.DatePicker = DatePicker
	else global.DatePicker = DatePicker
})()
