exports.Scroller = function(options) {
	this.getScrollerY = function() {
		return Bangle.appRect.y + this.topBarH;
	}
	this.dragging = false;
	this.firstTouch = true;
	this.selectedX = 0;
	this.h = options.h;
	this.w = Bangle.appRect.w;

	this.c = options.c;
	this.drawFunc = options.draw;
	this.drawEmpty = options.drawEmpty;
	this.selectFunc = options.select;
	this.touchX = undefined;
	this.button1Func = options.button1;
	this.button2Func = options.button2;
	this.drawButton1 = options.drawButton1;
	this.drawButton2 = options.drawButton2;
	this.buttonW = 55;
  this.buttonsW = (this.drawButton1 !== undefined ? this.buttonW : 0) + (this.drawButton2 !== undefined ? this.buttonW : 0);
	this.touchStart = true;
	this.mode = undefined;
	this.selected = undefined;
	/**
	  minH:
		maxH;
		drawMin: function(r)
		drawMax: function(r)
		handler: function(r)
	*/
	this.topBar = options.topBar;
	this.topBarH = this.topBar.maxH;
	this.topBarIsMax = true;
	this.topBarIsMin = false;
	this.maxCountOneScreen = Math.floor((Bangle.appRect.y2 - Bangle.appRect.y - this.topBar.maxH) / this.h);
	this.y0 = Bangle.appRect.y + this.topBarH;
	this.x = 0;
	this.dy = 0;

	this.clearScroller = function() {
		g.clearRect(Bangle.appRect.x, Bangle.appRect.y + this.topBarH, Bangle.appRect.x2, Bangle.appRect.y2);
	};

	this.getMinY = function() {
		let minY = Bangle.appRect.y2 - (this.c * this.h);
		return minY > this.topBarH ? this.topBarH : minY;
	};
	this.minY = this.getMinY();

	this.dragHandler = function(d) {
		if (this.firstTouch) {
			this.firstTouch = false;
			if (d.b === 0) return;
		}
		if (d.b === 1) this.dragging = true;
		else this.dragging = false;
		if (this.mode === undefined) {
			if (this.topBarIsMax && d.y < Bangle.appRect.y + this.topBar.maxH) {
				this.topBar.handler(d, {x: Bangle.appRect.x, y: Bangle.appRect.y, x2: Bangle.appRect.x2, y2: Bangle.appRect.y + this.topBar.maxH - 1});
				return;
			}
			this.selected = Math.floor((d.y - this.y0) / this.h);
			if (this.selected > this.c - 1 || this.selected < 0) this.selected = undefined;
			if (d.x != undefined) this.touchX = d.x; // need to save the last touch position
			if (d.dy === 0 && d.dx === 0 && this.touchStart === true) {	// initial finger press, we'll check after release
				if (d.b === 0) { // finger released, must have been no dragging, call select func
					if (this.selected !== undefined) {
						const idx = this.selected;
						const bounds = this.getBounds(idx);
						this.selectFunc(idx, bounds, this.touchX);
					}
					return;
				}
			} else if (Math.abs(d.dy) > Math.abs(d.dx)) {	// drag up/down
				this.touchStart = false;
				this.selectedX = this.x;
				this.mode = "ud";
			} else if (Math.abs(d.dx) > Math.abs(d.dy) && this.selected != undefined) {	// drag left/right
				this.touchStart = false;
				this.mode = "lr";
			}
		}
		if (this.mode === "ud") this.udHandler(d);
		else if (this.mode === "lr") this.lrHandler(d);
		else if (this.mode === "button") this.buttonHandler(d);
	};

	this.udHandler = function(d) {
		this.minY = this.getMinY();
		if (d.b === 0) {	// released finger
			if (this.c > this.maxCountOneScreen) {
				if (this.topBarH > (this.topBar.maxH + this.topBar.minH) / 2) {
					this.topBarH = this.topBar.maxH;
					this.topBarIsMax = true;
					this.topBarIsMin = false;
				} else {
					this.topBarH = this.topBar.minH;
					this.topBarIsMax = false;
					this.topBarIsMin = true;
				} // make sure the top bar ends in one of the 2 states when we stop scrolling
			}
			let done = false;
			this.y0 += this.dy; // move a little extra after finger release. Feels more natural.
			if (this.y0 > this.topBarH) {
				this.y0 = this.topBarH; // overscroll top
				done = true;
			} else if (this.y0 < this.minY) {
				this.y0 = this.minY;		// overscroll bottom
				done = true;
			}
			this.drawAll();
			this.touchStart = true;	// released finger, touchStart is true for next time
			this.mode = undefined; // reset mode, we don't know if we will continue scrolling up/down
			if (done) {
				return;
			}
		}
		this.x = 0;
		this.y0 += (d.dy * 1.2); // scroll a little faster than finger is moving to compensate for lag

		if (this.c > this.maxCountOneScreen) {
			if (d.dy > 0 && this.topBarH < this.topBar.maxH) { // drag top to bottom
				this.topBarH += d.dy;
				if (this.topBarH > this.topBar.maxH) {
					this.topBarH = this.topBar.maxH;
				}
			}
			else if (d.dy < 0 && this.topBarH > this.topBar.minH) { // drag bottom to top
				this.topBarH += d.dy;
					if (this.topBarH < this.topBar.minH) {
						this.topBarH = this.topBar.minH;
					}
			}
		}

		if (this.y0 > this.topBarH + 30) this.y0 = this.topBarH + 30;		// max overscroll top
		else if (this.y0 < this.minY-30) this.y0 = this.minY-30;		// max overscroll bottom
		this.clearScroller();
		this.drawAll();
	};

	this.lrHandler = function(d) {
		if (d.b === 0) { // released finger
			if (this.buttonsW === 0) return;
			if (this.selectedX <= -(this.buttonsW) + 20) { // left swipe buttons width less 20px
				this.selectedX = -(this.buttonsW);
				this.drawLR(this.selected);
				this.mode = "button";
			} else {
				this.selectedX = this.x;
				this.mode = undefined;
				this.drawAll();
			}
			this.touchStart = true;
			return;
		}
		if (d.dx > 0 && this.selectedX >= 0) return; // ignore swipe right
		else {
			this.selectedX += d.dx;
			this.drawLR(this.selected);
		}
	};

	this.buttonHandler = function(d) {
		if (Math.abs(d.dx > 1)) {
			this.mode = "lr";
			return;
		}
		if (this.touchStart && d.b === 0) {
			this.mode = undefined;
			this.selectedX = this.x;
			let b1x1, b1x2, b2x1, b2x2;
			if (this.drawButton2 === undefined) {
				b1x1 = this.x + this.w - this.buttonW;
				b1x2 = this.x + this.w;
			} else {
				b1x1 = this.x + this.w - this.buttonsW;
				b1x2 = this.x + this.w - this.buttonW;
			}
			b2x1 = this.x + this.w - this.buttonW;
			if (d.x > b1x1 && d.x < b1x2) { // button 1: delete
				this.removingSelection = this.selected;
				this.c--;
				this.minY = this.getMinY();
				this.animateDelete(this.selected);
				this.button1Func(this.selected);
			} else if (d.x > b2x1) {
				this.button2Func(this.selected);
			}
			return;
		}
	};

	this.getBounds = function(idx) {
		let x = this.x;
		let y = this.y0 + (idx * this.h);
		let x2 = x + this.w - 1;
		let y2 = y + this.h - 1;
		return {x: x, y: y, x2: x2, y2: y2, w: this.w, h: this.h};
	}

	this.draw = function(idx) {
		this.drawFunc(idx, this.getBounds(idx), this.dragging);
	};

	this.scrollToCenter = function(idx) {
		this.idxMidY = this.getScrollerY() + ((Bangle.appRect.h - this.h) / 2) - (idx * this.h);
		this.dir = this.idxMidY > this.y0 ? 1 : -1;

	  const scroll = function(idx) {
			let done = false;
			this.y0 += 30 * this.dir;
			if (Math.abs(this.y0) - Math.abs(this.idxMidY) <= 30) {
				this.y0 = this.idxMidY;
				done = true;
			}
			this.drawAll();
			if (done) {
				delete this.idxMidY;
				delete this.dir;
				delete Bangle.scroll;
				return;
			} else {
			  setTimeout(() => Bangle.scroll(idx), 30);
				return;
			}
		}
		Bangle.scroll = scroll.bind(this, idx);
		Bangle.scroll(idx);
	};

	this.drawTopBar = function() {
		if (this.c <= this.maxCountOneScreen) {
					this.topBarH = this.topBar.maxH;
					this.topBarIsMax = true;
					this.topBarIsMin = false;
				}
		g.setClipRect(Bangle.appRect.x, Bangle.appRect.y, Bangle.appRect.x2, Bangle.appRect.y + this.topBarH - 1);
		if (this.topBarH < this.topBar.maxH) {
			this.topBar.drawMin({x: Bangle.appRect.x, y: Bangle.appRect.y, x2: Bangle.appRect.x2, y2: Bangle.appRect.y + this.topBarH - 1})
		} else {
			this.topBar.drawMax({x: Bangle.appRect.x, y: Bangle.appRect.y, x2: Bangle.appRect.x2, y2: Bangle.appRect.y + this.topBarH - 1})
		}
	};

	this.drawAll = function() {
		this.drawTopBar();
		g.setClipRect(Bangle.appRect.x, this.getScrollerY(), Bangle.appRect.x2, Bangle.appRect.y2);
		this.clearScroller();
		if (this.c === 0) {
			this.drawEmpty({x: Bangle.appRect.x, y: this.getScrollerY(), x2: Bangle.appRect.x2, y2: Bangle.appRect.y2});
			return;
		}
		let x = this.x;
		let y = this.y0;
		let x2 = x + this.w - 1;
		let y2 = y + this.h - 1;
		const startIdx = this.y0 < 0 ? Math.floor(-this.y0 / this.h) : 0; // How many are fully off screen? We won't draw them.
		const count = Math.ceil(Bangle.appRect.h / this.h);	// How many we should draw
		let drawn = 0;
		for (let i = 0; i < this.c; i++) {
			if (i >= startIdx && drawn <= count) {
				if (i === this.removingSelection) {
					g.clearRect(x, y, x2, y + this.removingH - 1);
					y += this.removingH;
					y2 += this.removingH;
				}
				if ( (this.mode === "lr"  || this.mode === "button") && i === this.selected ) {
					this.drawLR(this.selected);
				}
				else {
					this.drawFunc(i, {
						x: x, y: y, x2: x2, y2: y2, w: this.w, h: this.h
					}, this.dragging);
				}
				drawn++;
			}
			y += this.h;
			y2 += this.h;
		}
	};

	this.drawLR = function(selected) {
		// const idx = selected;
		// const x = this.x;
		const y = this.y0 + (this.h * selected);
		const x2 = this.x + this.w - 1;
		const y2 = y + this.h - 1;
		// const w = this.buttonW;
		// const h = this.h;

		g.clearRect(this.x, y, x2, y2);
		this.drawMenu(selected,
									{ x: x2 - (2 * this.buttonW), y: y, x2: x2 - this.buttonW - 1, y2: y2, w: this.buttonW, h: this.h },
									{ x: x2 - this.buttonW, y: y, x2: x2, y2: y2, w: this.buttonW, h: this.h });

		this.drawFunc(this.selected, {
			x: this.selectedX, y: y, x2: this.selectedX + this.w - 1, y2: y2, h: this.h, w: this.w
		});
	};

	this.animateDelete = function(selected) {
		Bangle.animateDelete = this.animateDelete.bind(this);
		this.removingSelection = selected;

		this.minY = this.getMinY();
		this.removingH = this.removingH === undefined ? this.h : this.removingH - 30;
		if (this.removingH < 1) this.removingH = 1;
		if (selected === this.c || this.y0 < this.minY) {
			if (this.y0 < Bangle.appRect.y + this.topBarH) this.y0 += this.removingH;
			if (this.y0 > Bangle.appRect.y + this.topBarH) this.y0 -= this.removingH;
		}
		this.drawAll();
		if (this.removingH > 1) {
			setTimeout(() => Bangle.animateDelete(selected), 30);
		} else {
			if (this.y0 < this.minY) {
				this.y0 = this.minY;
			}
			this.removingSelection = undefined;
			this.removingH = undefined;
			delete Bangle.animateDelete;
			this.drawAll();
		}
	};

	this.drawMenu = function(_idx, r1, r2) {
		const col = g.getColor();
		if (this.drawButton2 !== undefined) this.drawButton2(r2);
		else r1 = r2;
		if (this.drawButton1 !== undefined) this.drawButton1(r1);
		g.setColor(col);
	};

	this.remove = function() {
		Bangle.setUI();
		g.reset();
	};

	this.reload = function() {
		this.minY = this.getMinY();
		Bangle.setUI();
		this.firstTouch = true;
		Bangle.dragHandler = this.dragHandler.bind(this);
			Bangle.setUI({mode: "custom", drag: Bangle.dragHandler,
				btn: (_n) => Bangle.showClock()
			});
	};
} // Scroller
