// 31 july 2015
import Cocoa

// leave a whole lot of space around the alignment rect, just to be safe
// TODO fine tune this
// TODO de-duplicate this from spinbox.m
class tBoxContainer : NSView {
	override var alignmentRectInsets: NSEdgeInsets {
		get {
			return NSEdgeInsetsMake(50, 50, 50, 50)
		}
	}
}

struct tBoxChild {
	var c: tControl
	var stretchy: Bool
}

// the swift bridge isn't perfect; it won't recognize these properly
// thanks to Eridius in freenode/#swift-lang
let myNSLayoutPriorityRequired: NSLayoutPriority = 1000
let myNSLayoutPriorityDefaultHigh: NSLayoutPriority = 750
let myNSLayoutPriorityDragThatCanResizeWindow: NSLayoutPriority = 510
let myNSLayoutPriorityWindowSizeStayPut: NSLayoutPriority = 500
let myNSLayoutPriorityDragThatCannotResizeWindow: NSLayoutPriority = 490
let myNSLayoutPriorityDefaultLow: NSLayoutPriority = 250
let myNSLayoutPriorityFittingSizeCompression: NSLayoutPriority = 50

class tBox : tControl {
	private var v: NSView
	private var children: [tBoxChild]
	private var vertical: Bool
	private var parent: tControl?
	private var spaced: Bool

	// TODO rename to padded
	init(vertical: Bool, spaced: Bool) {
		self.v = tBoxContainer(frame: NSZeroRect)
		self.v.translatesAutoresizingMaskIntoConstraints = false
		self.children = []
		self.vertical = vertical
		self.parent = nil
		self.spaced = spaced
	}

	func tAddControl(c: tControl, stretchy: Bool) {
		c.tSetParent(self, addToView: self.v)
		self.children.append(tBoxChild(
			c:			c,
			stretchy:		stretchy))
		self.tRelayout()
	}

	func tSetParent(p: tControl, addToView v: NSView) {
		self.parent = p
		v.addSubview(self.v)
	}

	// TODO make the other dimension not hug (as an experiment)
	func tFillAutoLayout(inout p: tAutoLayoutParams) {
		var hasStretchy = false
		if self.children.count != 0 {
			hasStretchy = self.actualLayoutWork()
		}
		p.view = self.v
		p.attachLeft = true
		p.attachTop = true
		// don't attach to the end if there weren't any stretchy controls
		if self.vertical {
			p.attachRight = true
			p.attachBottom = hasStretchy
		} else {
			p.attachRight = hasStretchy
			p.attachBottom = true
		}
	}

	func actualLayoutWork() -> Bool {
		var orientation: NSLayoutConstraintOrientation
		// TODO don't use Int
		var i, n: Int
		var nStretchy: Int

		self.v.removeConstraints(self.v.constraints)

		orientation = NSLayoutConstraintOrientation.Horizontal
		if self.vertical {
			orientation = NSLayoutConstraintOrientation.Vertical
		}

		var views = [String: NSView]()
		n = 0
		var predicates = [String]()
		var pp = tAutoLayoutParams()
		for child in self.children {
			var priority: NSLayoutPriority

			pp.nonStretchyWidthPredicate = ""
			pp.nonStretchyHeightPredicate = ""
			// this also resets the hugging priority
			// TODO do this when adding and removing controls instead
			child.c.tFillAutoLayout(&pp)
			priority = myNSLayoutPriorityDefaultHigh			// forcibly hug; avoid stretching out
			if child.stretchy {
				priority = myNSLayoutPriorityDefaultLow		// do not forcibly hug; freely stretch out
			}
			if self.vertical {
				predicates.append(pp.nonStretchyHeightPredicate)
			} else {
				predicates.append(pp.nonStretchyWidthPredicate)
			}
			pp.view?.setContentHuggingPriority(priority, forOrientation:orientation)
			views[tAutoLayoutKey(n)] = pp.view
			n++
		}

		// first string the views together
		var constraint = "H:|"
		if self.vertical {
			constraint = "V:|"
		}
		var firstStretchy = true
		// swift can't tell that nStretchy isn't used until firstStretchy becomes false
		nStretchy = 0
		for i in 0..<n {
			if self.spaced && i != 0 {
				constraint += "-"
			}
			constraint += "[" + tAutoLayoutKey(i)
			if self.children[i].stretchy {
				if firstStretchy {
					firstStretchy = false
					nStretchy = i
				} else {
					constraint += "(==" + tAutoLayoutKey(nStretchy) + ")"
				}
			} else {
				constraint += predicates[i]
			}
			constraint += "]"
		}
		constraint += "|"
		self.v.addConstraints(mkconstraints(constraint, views))
		// TODO do not release constraint; it's autoreleased?

		// next make the views span the full other dimension
		// TODO make all of these the same width/height
		for i in 0..<n {
			constraint = "V:|["
			if self.vertical {
				constraint = "H:|["
			}
			constraint += tAutoLayoutKey(i) + "]|"
			self.v.addConstraints(mkconstraints(constraint, views))
			// TODO do not release constraint; it's autoreleased?
		}

		// the caller needs to know if a control was stretchy
		// firstStretchy is false if there was one
		return !firstStretchy
	}

	func tRelayout() {
		if self.parent != nil {
			self.parent?.tRelayout()
		}
	}
}