{
"use strict";
const r = Bangle.appRect;
const midX = (r.x + r.x2 + 1) / 2;
const midY = (r.y + r.x2 + 1) / 2;
const Poly = require("poly").Poly;

  /**
   * @param
   * [{
   * x1: start x ,y1: start y,
   * x2: end x ,y2: end y,
   * Scale1: scale to start with. Sent to draw function.
   * Scale2: scale to end with.
   * draw: function(x, y, scale) draw function callback
   * }, ...]
   * @param drawFinal callback for final drawing
   */
  function MultiAnimation(objArr, drawFinal) {
    const defaultSteps = 3;
    this.stop = false;
    this.steps = 0; // number of animation steps
    this.step = 0;
    this.delayFactor = 0;
    this.done = false;
    this.objArr = objArr;
    this.drawFinal = drawFinal;
    let delayFactor = 0;
    for (let obj of this.objArr) {
      this.steps = (obj.steps < this.steps || obj.steps === undefined) ? this.steps : obj.steps; // set # of steps to obj with greatest # of steps
      obj.x1 = obj.x1 === undefined ? 1 : obj.x1;
      obj.y1 = obj.y1 === undefined ? 1 : obj.y1;
      obj.x2 = obj.x2 === undefined ? 1 : obj.x2;
      obj.y2 = obj.y2 === undefined ? 1 : obj.y2;
      obj.x = obj.x1;
      obj.y = obj.y1;
      let deltaX = (obj.x2 - obj.x1);
      let deltaY = (obj.y2 - obj.y1);
      let delta = (Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)));
      if (deltaX === 0 || deltaY === 0) obj.ratio = 0;
      else obj.ratio = ((Math.abs(deltaX) > Math.abs(deltaY)) ? deltaY / deltaX : deltaX / deltaY);
      let maxIsX = Math.abs(deltaX) > Math.abs(deltaY) ? true : false;
      let inc = maxIsX ? (deltaX / this.steps) : (deltaY / this.steps);
      if (maxIsX) {
        obj.xInc = inc;
        obj.yInc = inc * obj.ratio;
      } else {
        obj.xInc = inc * obj.ratio;
        obj.yInc = inc;
      }
      obj.scale1 = (obj.scale1 === undefined) ? 1 : obj.scale1;
      obj.scale2 = (obj.scale2 === undefined) ? 1 : obj.scale2;
      obj.scale = obj.scale1;
      obj.scaleInc = (obj.scale2 - obj.scale1) / this.steps;
      this.done = false;
      obj.delayFactor = delta / Math.max(deltaX, deltaY);
      this.delayFactor = this.delayFactor > obj.delayFactor ? this.delayFactor : obj.delayFactor;
      delete obj.delayFactor;
      this.steps = this.steps === 0 ? defaultSteps : this.steps;
    }
    this.animate = function() {
      return new Promise((resolve) => {
        this.stop = false;
        (draw = () => {
          if (!this.done) {
            g.clearRect(Bangle.appRect);;;
            for (let obj of this.objArr) {
              if (this.stop) return;
              if (obj.scale === 0) return;
              obj.draw(obj.x, obj.y, obj.scale);
              if (this.step < this.steps) {
                obj.x += obj.xInc;
                obj.y += obj.yInc;
                obj.scale += obj.scaleInc;
                this.done = false;
              } else {
                obj.x = obj.x1;
                obj.y = obj.y1;
                obj.scale = obj.scale1;
                this.done = true;
              }
            }
            if (this.stop) resolve();
            this.step++;
            this.drawTimeoutID = setTimeout(draw, 30);
          } else {
            if (this.stop) resolve();
            g.clearRect(Bangle.appRect);;;
            this.drawFinal();
            this.done = false;
            this.step = 0;
            resolve();
          }
        })(); //draw
      });
    }

    this.abort = function() {
      if (this.drawTimeoutID) clearTimeout(this.drawTimeoutID);
      this.stop = true;
      delete this.drawTimeoutID;
    }
  } //multiAnimation

  function drawEasedRect(x, y, x1, y1) {
    const a = 4;
    g.drawPoly([x + a, y, x1 - a, y, x1, y + a, x1, y1 - a, x1 - a, y1, x + a, y1, x, y1 - a, x, y + a], true);
  }

  function drawTriangle(a, r, x, y) {
    let tri = new Poly(3);
    tri.addRadialPoint(a, r, x, y);
    tri.addRadialPoint(a + 2.0944, r, x, y);
    tri.addRadialPoint(a - 2.0944, r, x, y);
    g.fillPoly(tri.getPoints(), true);
  }

  function drawRing(col) {
    col = col == undefined ? col = [0, 1, 1] : col
    let t = 20; //thickness of ring
    let x = midX;
    let y = midY;
    let r = midX;
    g.setColor.apply(g, col);
    g.fillCircle(x, y, r).setColor(g.theme.bg).fillCircle(x, y, r - t).setColor(g.theme.fg);
  }

  const Segment = function(params) {
    this.type = params.type;
    this.label = params.label;
    this.x = params.x;
    this.y = params.y;
    this.poly = params.poly;
		this.minValue = params.minValue;
		this.maxValue = params.maxValue;
    this.value = params.value;

    this.drawSegment = function(scale, x, y) {
      scale = scale === undefined ? 1 : scale;
      x = x === undefined ? midX : x;
      y = y === undefined ? midY : y;
      const fillCol = g.theme.bg;
      const olCol = g.theme.fg;
      const poly = g.transformVertices(this.poly.getPoints(), { x: x, y: y, scale: scale });
      g.setColor(fillCol).fillPoly(poly).setColor(olCol).drawPoly(poly);
      return this;
    } //drawSegment

    this.drawValue = function(scale, x, y, withBox) {
			g.setColor(g.theme.fg);
      scale = scale === undefined ? 1 : scale;
      x = x === undefined ? this.x : x;
      y = y === undefined ? this.y - Math.round(6 * scale) : y;
      const size = Math.round(30 * scale);
      let str = this.value;

      if (typeof(this.value) === "number") str = str < 10 ? "0" + str : str;
			if (typeof(this.value) === "string") str = str === 'AM' ? "AM" : "PM";

      g.setFont("Vector: " + size).setFontAlign(0, 0);

      let halfStrW = g.stringWidth("00") / 2;
      let halfStrH = g.getFontHeight() / 2;
      if (x <= halfStrW || y <= halfStrH) {
        x = halfStrW + 5;
        y = halfStrH + 2;
      }
      if (withBox) {
        drawEasedRect(x - halfStrW - 4, y - halfStrH - 2, x + halfStrW, y + (halfStrH - 2));
      }
      g.drawString(str, x, y);
      return this;
    } //drawValue

    this.drawLabel = function(scale, x, y) {
			g.setColor(g.theme.fg);
			scale = scale === undefined ? 1 : scale;
			const size = Math.round(20 * scale)
			x = x === undefined ? this.x : x;
			y = y === undefined ? this.y + Math.round(17 * scale) : y;
			g.setFont("Vector:" + size).setFontAlign(0, 0).drawString(this.label, x, y);
      return this;
    } //drawLabel
    return this;
  } // Segment

  // animation factories:
  const ToNumberPicker = function(t, segPart) {
    return new MultiAnimation([
      { draw: () => drawRing() },
      { draw: () => t.drawTime() },
      { steps: 2, x1: segPart.x, y1: segPart.y, x2: 0, y2: 0, draw: (x, y) => segPart.drawValue(1, x, y) },
    ],
      () => {
        drawRing();
        t.drawTime();
        segPart.drawValue(1.5, 0, 0, true);
      }
    );
  }

  const ToTimePicker = function(t, segPart) {
    return new MultiAnimation([
      { draw: () => drawRing() },
      { steps: 2, x1: 0, y1: 0, x2: segPart.x, y2: segPart.y, draw: (x, y) => segPart.drawValue(1, x, y) },
    ],
      () => t.draw()
    );
  }

  exports.TimePicker = function(options) {
		// if meridiem is set, this will be an alarm time picker
		// TODO maybe make the defaults as variables rather than hard coded
    if (options.meridiem !== undefined) { // meridiem: "midday", as in ante meridiem and post meridiem ie. am/pm
			this.seconds = {value:  0};
			this.meridiem = new Segment({
				type: "MER",
				x: r.x2 - 29,
				y: midY,
				label: "",
				maxValue: undefined,
				minValue: undefined,
        value: options.meridiem === undefined ? "AM" : options.meridiem
			});
		} else { // else it's a timer time picker
      this.meridiem = {value: undefined};
			this.seconds = new Segment({
				type: "S",
				x: r.x2 - 29,
				y: midY,
				label: "S",
				minValue: 0,
				maxValue: 60,
				value: options.s === undefined ? 0 : options.s
			});
			this.seconds.toNumberPicker = ToNumberPicker(this, this.seconds);
			this.seconds.toTimePicker = ToTimePicker(this, this.seconds);
		}

		this.hours = new Segment({
			type: "H",
			x: r.x + 29,
			y: midY,
			label: "H",
			minValue: options.meridiem ? 1 : 0,
			maxValue: 12,
      value: options.h === undefined ? 0 : options.h
		});

    this.minutes = new Segment({
			type: "M",
			x: midX,
			y: r.y + 29,
			label: "M",
			minValue: 0,
			maxValue: 59,
      value: options.m === undefined ? 0 : options.m
		});

    this.hours.toNumberPicker = ToNumberPicker(this, this.hours);
    this.hours.toTimePicker = ToTimePicker(this, this.hours);
    this.minutes.toNumberPicker = ToNumberPicker(this, this.minutes);
    this.minutes.toTimePicker = ToTimePicker(this, this.minutes);
    this.startFunc = options.start;
    this.cancelFunc = options.cancel;
    this.touchStart = true;
    this.firstTouch = true;
    this.segment = undefined;
    this.btid = undefined;
    this.buzzLen = undefined;
    this.buzzStrength = undefined;
    this.count = 0;

    this.setTime = function(hms) {
      this.hours.value = hms.h;
      this.minutes.value = hms.m;
      if (this.meridiem.value !== undefined) {
				this.meridiem.value = hms.meridiem;
				this.seconds.value = 0;
			} else {
				this.seconds.value = hms.s;
			}
    }

    this.resetTime = function() {
      this.hours.value = 0;
      this.minutes.value = 0;
			this.seconds.value = 0;
      if (this.meridem.value !== undefined) this.meridiem.value = "AM";

    }

    this.drawScreen = function() {
			g.setColor(0, 1, 1);
			g.fillRect({x: r.x, y: midY - 29, x2: midX - 29, y2: midY + 29, r: 18}); // 9 o'clock
			g.fillRect({x: midX - 29, y: r.y, x2: midX + 29, y2: midY - 29, r: 18}); // 12 o'clock
			g.fillRect({x: r.x2 - 58, y: midY - 29, x2: r.x2, y2: midY + 29, r: 18}); // 3 o'clock

			//g.setColor(0.25,0.25,0.25);
			g.setColor(g.theme.bg);
			g.fillRect({x: r.x + 3, y: midY - 26, x2: midX - 32, y2: midY + 26, r: 15}); // 9 o'clock
			g.fillRect({x: midX - 26, y: r.y + 3, x2: midX + 26, y2: midY - 32, r: 15}); // 12 o'clock
			g.fillRect({x: r.x2 - 54, y: midY - 26, x2: r.x2 - 3, y2: midY + 26, r: 15}); // 3 o'clock
			// g.fillRect({x: r.x + 3, y: r.y2 - 42, x2: r.x + 77, y2: r.y2 - 3, r: 15}); // bottom left
			// g.fillRect({x: r.x2 - 77, y: r.y2 - 42, x2: r.x2 - 3, y2: r.y2 - 3, r: 18}); // bottom right
		};

		this.drawButtons = function() {
			g.setColor(0, 1, 1);
      g.fillRect({x: midX - 26, y: midY - 26, x2: midX + 26, y2: midY + 26, r: 18}); // center
 			g.fillRect({x: midX - 29, y: r.y2, x2: midX + 29, y2: midY + 29, r: 18}); // 6 o'clock

      g.setColor(g.theme.bg);
      g.fillRect({x: midX - 26 + 3, y: midY - 26 + 3, x2: midX + 26 - 3, y2: midY + 26 - 3, r: 15}); // center
      g.fillRect({x: midX - 26, y: r.y2 - 3, x2: midX + 26, y2: midY + 32, r: 15}); // 6 o'clock

			g.setColor(g.theme.fg);
			drawTriangle(0, 20, midX, r.y2 - 29);
      g.setFontAlign(0, 0).setFont("Vector:35").drawString("x", midX + 2, midY - 1);
		}

    this.draw = function() {
      g.clearRect(Bangle.appRect);;;
			this.drawScreen();
      this.hours.drawValue().drawLabel();
      this.minutes.drawValue().drawLabel();
      if (this.meridiem.value !== undefined) {
        this.meridiem.drawValue();
      }	else {
        this.seconds.drawValue().drawLabel();
      }
			this.drawButtons();
      Bangle.timePickerDragHandler = this.dragHandler.bind(this);
      //setTimeout(() => {
        Bangle.setUI({mode: "custom", drag: Bangle.timePickerDragHandler});
      //}, 100);
    }

    this.drawTime = function() {
      let x = midX;
      let y = midY;
      let scale = 1;
      let size = 30 * scale;
      g.setFont("Vector:" + size);
      let wC = g.stringWidth(":");
      let w2 = g.stringWidth("00");

      let hScale = (this.segment.type === "H") ? 1.25 : scale;
      let mScale = (this.segment.type === "M") ? 1.25 : scale;
      let sScale = (this.segment.type === "S") ? 1.25 : scale;

      this.hours.drawValue(hScale, x - (w2 + wC), y, true);
      this.minutes.drawValue(mScale, x, y, true);
      if (this.meridiem.value !== undefined) {
        this.meridiem.drawValue(1, x + w2 + wC, y, true);
      } else {
        this.seconds.drawValue(sScale, x + w2 + wC, y, true);
      }
    } //drawTime

    this.selected = function(x, y) {
      x = x - midX;  // set the origin to the center of the screen
      y = y - midY;
      let s = Math.abs(x / y);  // slope
      if (y > -25 && y < 25 && x > -25 && x < 25) return { type: "C" };
      if (y < 0 && s < 1) return this.minutes;   // top half, slope is less than 1/1 i.e. within this arc: \/
      else if (y > 0 && s < 1) return { type: "B" }; // bottom half, slope less than 1/1:  /\
      else if (x < 0) return this.hours; // on left within this arc: >
      else if (x > 0) return this.meridiem.value !== undefined? this.meridiem : this.seconds; // on right within this arc: <
      else return undefined;
    } //selected

    this.dragHandler = function(d) {
      if (d.b === 0) { // released finger, show main screen
        if (this.firstTouch === true) {
          this.firstTouch = false;
          return;
        }
        if (this.segment.toNumberPicker !== undefined) {
          this.segment.toNumberPicker.abort();
          g.clearRect(Bangle.appRect);;;
          this.draw();
        }
        this.touchStart = true;  // for next time finger touches
        return;
      }
      if (this.touchStart) { // inital screen touch, show number picker
        this.segment = this.selected(d.x, d.y); // where did we touch
        if (!this.segment) return;
        else if (this.segment.type === "C") {
          Bangle.setUI();
          delete Bangle.timePickerDragHandler;

          //setTimeout(this.cancelFunc, 100);
          this.cancelFunc();

          return;
        } else if (this.segment.type === "B") { // touched start button
          Bangle.setUI();
          delete Bangle.timePickerDragHandler;
          this.startFunc({ h: this.hours.value, m: this.minutes.value, s: this.seconds.value, meridiem: this.meridiem.value});
          return;
        } else if (this.segment.type === "MER") {
					this.meridiem.value = this.meridiem.value === 'AM' ? 'PM' : 'AM';
					this.draw();
					return;
				} else {
					this.segment.toNumberPicker.animate();
					this.touchStart = false;
					return;
				}
      }
      if (!this.btid) {
        this.buzzLen = 25;
        this.buzzStrength = 0.75;
        this.btid = setTimeout(() => { this.buzzLen = 50; this.buzzStrength = 1}, 150);
      }

      const delta = Math.max(Math.abs(d.dx), Math.abs(d.dy));
      let inc = 3;
      if (delta > 3) { inc = 30; }
      else if (delta > 2) { inc = 20; }

      var dirIsCW = true; // direction is clockwise
      if (d.dx > 0 && d.y < midY) this.count += inc;
      else if (d.dx < 0 && d.y > midY) this.count += inc;
      else if (d.dy > 0 && d.x > midX) this.count += inc;
      else if (d.dy < 0 && d.x < midX) this.count += inc;
      else {
        this.count -= inc;
        dirIsCW = false; // direction is CCW
      }

      if (Math.abs(this.count) >= 60) {
        Bangle.buzz(this.buzzLen, this.buzzStrength);
        this.segment.value = dirIsCW ? this.segment.value + 1 : this.segment.value - 1;
        if (this.segment.value > this.segment.maxValue) this.segment.value = this.segment.minValue;
        else if (this.segment.value < this.segment.minValue) this.segment.value = this.segment.maxValue;
        this.count = 0;
        offset = 0;
        g.clearRect(Bangle.appRect);;;
        drawRing();
        this.drawTime();
        this.segment.drawValue(1.5, 0, 0, true);
        clearTimeout(this.btid);
        this.btid = null;
      }
    } // dragHandler
  } // TimePicker
}
