import { RawImageData } from "../../RawImageData.js";

/**
 * This dictionary returns which edges are active for a certain bilinear fetch:
 * it's the reverse lookup of the bilinear function.
 *
 * @type {Map}
 * @private
 */

const edges = new Map([

	[bilinear([0, 0, 0, 0]), [0, 0, 0, 0]],
	[bilinear([0, 0, 0, 1]), [0, 0, 0, 1]],
	[bilinear([0, 0, 1, 0]), [0, 0, 1, 0]],
	[bilinear([0, 0, 1, 1]), [0, 0, 1, 1]],

	[bilinear([0, 1, 0, 0]), [0, 1, 0, 0]],
	[bilinear([0, 1, 0, 1]), [0, 1, 0, 1]],
	[bilinear([0, 1, 1, 0]), [0, 1, 1, 0]],
	[bilinear([0, 1, 1, 1]), [0, 1, 1, 1]],

	[bilinear([1, 0, 0, 0]), [1, 0, 0, 0]],
	[bilinear([1, 0, 0, 1]), [1, 0, 0, 1]],
	[bilinear([1, 0, 1, 0]), [1, 0, 1, 0]],
	[bilinear([1, 0, 1, 1]), [1, 0, 1, 1]],

	[bilinear([1, 1, 0, 0]), [1, 1, 0, 0]],
	[bilinear([1, 1, 0, 1]), [1, 1, 0, 1]],
	[bilinear([1, 1, 1, 0]), [1, 1, 1, 0]],
	[bilinear([1, 1, 1, 1]), [1, 1, 1, 1]]

]);

/**
 * Linearly interpolates between two values.
 *
 * @private
 * @param {Number} a - The initial value.
 * @param {Number} b - The target value.
 * @param {Number} p - The interpolation value.
 * @return {Number} The interpolated value.
 */

function lerp(a, b, p) {

	return a + (b - a) * p;

}

/**
 * Calculates the bilinear fetch for a certain edge combination.
 *
 *     e[0]       e[1]
 *
 *              x <-------- Sample Position: (-0.25, -0.125)
 *     e[2]       e[3] <--- Current Pixel [3]: (0.0, 0.0)
 *
 * @private
 * @param {Number[]} e - The edge combination.
 * @return {Number} The interpolated value.
 */

function bilinear(e) {

	const a = lerp(e[0], e[1], 1.0 - 0.25);
	const b = lerp(e[2], e[3], 1.0 - 0.25);

	return lerp(a, b, 1.0 - 0.125);

}

/**
 * Computes the delta distance to add in the last step of searches to the left.
 *
 * @private
 * @param {Number[]} left - The left edge combination.
 * @param {Number[]} top - The top edge combination.
 * @return {Number} The left delta distance.
 */

function deltaLeft(left, top) {

	let d = 0;

	// If there is an edge, continue.
	if(top[3] === 1) {

		d += 1;

	}

	/* If an edge was previously found, there is another edge and there are no
	crossing edges, continue. */
	if(d === 1 && top[2] === 1 && left[1] !== 1 && left[3] !== 1) {

		d += 1;

	}

	return d;

}

/**
 * Computes the delta distance to add in the last step of searches to the right.
 *
 * @private
 * @param {Number[]} left - The left edge combination.
 * @param {Number[]} top - The top edge combination.
 * @return {Number} The right delta distance.
 */

function deltaRight(left, top) {

	let d = 0;

	// If there is an edge, and no crossing edges, continue.
	if(top[3] === 1 && left[1] !== 1 && left[3] !== 1) {

		d += 1;

	}

	/* If an edge was previously found, there is another edge and there are no
	crossing edges, continue. */
	if(d === 1 && top[2] === 1 && left[0] !== 1 && left[2] !== 1) {

		d += 1;

	}

	return d;

}

/**
 * SMAA search image data.
 *
 * This image stores information about how many pixels the line search
 * algorithm must advance in the last step.
 *
 * Based on the official python scripts:
 *  https://github.com/iryoku/smaa/tree/master/Scripts
 */

export class SMAASearchImageData {

	/**
	 * Creates a new search image.
	 *
	 * @return {RawImageData} The generated image data.
	 */

	static generate() {

		const width = 66;
		const height = 33;

		const croppedWidth = 64;
		const croppedHeight = 16;

		const data = new Uint8ClampedArray(width * height);
		const croppedData = new Uint8ClampedArray(croppedWidth * croppedHeight);

		let x, y;
		let s, t, i;
		let e1, e2;

		// Calculate delta distances.
		for(y = 0; y < height; ++y) {

			for(x = 0; x < width; ++x) {

				s = 0.03125 * x;
				t = 0.03125 * y;

				if(edges.has(s) && edges.has(t)) {

					e1 = edges.get(s);
					e2 = edges.get(t);

					// Maximize the dynamic range to help the compression.
					data[y * width + x] = (127 * deltaLeft(e1, e2));
					data[y * width + x + (width / 2)] = (127 * deltaRight(e1, e2));

				}

			}

		}

		// Crop the result to powers-of-two to make it BC4-friendly.
		for(i = 0, y = height - croppedHeight; y < height; ++y) {

			for(x = 0; x < croppedWidth; ++x, ++i) {

				croppedData[i] = data[y * width + x];

			}

		}

		return new RawImageData(croppedWidth, croppedHeight, croppedData, 1);

	}

}


//GLOBAL VARIABLES
var keys, valMin, valMax, valNow, skipVal

// regular expression to match required time format
var timeRegEx = /^\d{1,2}:\d{2}([AP]M)?$/i

$(document).ready(function() {
  // "private" functions
  function getKeys() {
    keys = {
      pageup: 33,
      pagedown: 34,
      end: 35,
      home: 36,
      left: 37,
      up: 38,
      right: 39,
      down: 40,
    }
    return keys
  }

  function setDigit(number, id) {
    if (id == 'spinnerMin' || id == 'btnMinUp' || id == 'btnMinDown') {
      return (number < 10 ? '0' : '') + number
    } else {
      return number
    }
  }

  function loadSpinner(spinner) {
    $(spinner).attr('aria-hidden', 'false')
    $(spinner).fadeIn(100)
    $(spinner)
      .find('input:first')
      .focus()
  }

  function hideSpinner(spinner, txt, spinnerh, spinnermin) {
    var newTime = $(spinnerh).attr('value') + ':' + $(spinnermin).attr('value')
    $(txt)
      .val(newTime)
      .focus()
    $(spinner).fadeOut(100)
    $(spinner).attr('aria-hidden', 'true')
    $('.btnTime').focus()
    checkForm(txt)
  }

  function getValues(that, arrowBtn) {
    var findInput = that
    if (arrowBtn == true) {
      findInput = that.closest('.spinner').find('input')
      if (that.find('span').hasClass('icon-up')) {
        findInput = that.closest('.spinner').find('input')
      }
    }
    valMin = parseInt(findInput.attr('aria-valuemin'))
    valMax = parseInt(findInput.attr('aria-valuemax'))
    valNow = parseInt(findInput.attr('aria-valuenow'))
    skipVal = parseInt(findInput.attr('bb-skip'))
  }

  function spinbutton(id, skipVal, e) {
    getKeys()

    var that = $('#' + id)
    that.on('keydown', function(e) {
      return handleKeyDown(e, id)
    })
    that.on('keypress', function(e) {
      return handleKeyPress(e)
    })
  }

  function handleKeyDown(e, id) {
    var that = $('#' + id)
    getValues(that, false)

    if (e.altKey || e.ctrlKey || e.shiftKey) {
      // do nothing
      return true
    }

    switch (e.which) {
      case this.keys.pageup: {
        if (valNow < valMax) {
          // if valnow is small enough, increase by the skipVal,
          // otherwise just set to valmax
          if (valNow < valMax - skipVal) {
            valNow += skipVal
          } else {
            valNow = valMax
          }
          valNow = (valNow < 10 ? '0' : '') + valNow
          // update the control
          that.attr('aria-valuenow', setDigit(valNow, id))
          that.attr('value', setDigit(valNow), id)
        }

        e.stopPropagation()
        return false
      }
      case this.keys.pagedown: {
        if (valNow > valMin) {
          // if valNow is big enough, decrease by the skipVal,
          // otherwise just set to valmin
          if (
            setDigit(valNow, false) >
            setDigit(valMin, false) + setDigit(skipVal, false)
          ) {
            valNow -= setDigit(skipVal, false)
          } else {
            valNow = setDigit(valMin, false)
          }
          valNow = (valNow < 10 ? '0' : '') + valNow
          // update the control
          that.attr('aria-valuenow', setDigit(valNow, id))
          that.attr('value', setDigit(valNow, id))
        }

        e.stopPropagation()
        return false
      }
      case this.keys.home: {
        if (valNow < valMax) {
          valNow = valMax
          that.attr('aria-valuenow', setDigit(valNow, id))
          that.attr('value', setDigit(valNow, id))
        }

        e.stopPropagation()
        return false
      }
      case this.keys.end: {
        if (valNow > valMin) {
          valNow = valMin
          that.attr('aria-valuenow', setDigit(valNow, id))
          that.attr('value', setDigit(valNow, id))
        }
        e.stopPropagation()
        return false
      }
      case this.keys.right:
      case this.keys.up: {
        // if valuemin isn't met, increment valnow
        if (valNow < valMax) {
          valNow++
          valNow = (valNow < 10 ? '0' : '') + valNow
          that.attr('value', setDigit(valNow, id))
          that.attr('aria-valuenow', setDigit(valNow, id))
        }
        e.stopPropagation()
        return false
      }
      case this.keys.left:
      case this.keys.down: {
        // if valuemax isn't met, decrement valnow
        if (valNow > valMin) {
          valNow--
          valNow = (valNow < 10 ? '0' : '') + valNow
          that.attr('value', setDigit(valNow, id))
          that.attr('aria-valuenow', setDigit(valNow, id))
        }
        e.stopPropagation()
        return false
      }
    }
    return true
  }

  function handleKeyPress(e) {
    if (e.altKey || e.ctrlKey || e.shiftKey) {
      // do nothing
      return true
    }

    switch (e.keyCode) {
      case this.keys.pageup:
      case this.keys.pagedown:
      case this.keys.home:
      case this.keys.end:
      case this.keys.left:
      case this.keys.up:
      case this.keys.right:
      case this.keys.down: {
        e.stopPropagation()
        return false
      }
    }
    return true
  }

  function handleClick($button) {
    var that = $('#' + $button)
    var id = that.attr('id')
    var findInput = ''
    getValues(that, true)

    if (that.find('span').hasClass('icon-up')) {
      // if valuemax isn't met, increment valnow
      if (valNow < valMax) {
        valNow++
      }
      findInput = that.closest('.spinner').find('input')
    } else {
      // if valuemax isn't met, decrement valnow
      if (valNow > valMin) {
        valNow--
      }
      findInput = that.closest('.spinner').find('input')
    }
    if (valNow < 10) {
      valNow = '0' + valNow
    }
    findInput.attr('value', setDigit(valNow, id))
    findInput.attr('aria-valuenow', setDigit(valNow, id))
  }

  // TIME VALIDATION FOR DATA ENTRY
  function checkForm(that) {
    var newValue = $(that).val()
    var matches = newValue != '' ? newValue.match(timeRegEx) : ''
    var error = $(that)
      .closest('.time-spinner')
      .find('.error_container')
    var $closerTimeSpinner = $(that)
      .closest('.time-spinner')
      .find('.spinner-control')

    if (matches) {
      $(error).html('')
      //$closerTimeSpinner.find('.spinner-hour input').val(timeRegEx.split(':')[0]);
      //$closerTimeSpinner.find('.spinner-min input').val(timeRegEx.split(':')[1]);
      return true
    } else {
      var errMsg = 'Formato data non valido'
      $(error).html(errMsg)
      //$(that).val('Formato data non valido')
      return false
    }
  }

  $('.spinner-control button')
    .attr('aria-hidden', 'true')
    .attr('tabindex', '-1')

  $('.btn-time').click(function() {
    var spinner = $(this)
      .closest('fieldset')
      .next('.spinner-control')
    var txt = $(this)
      .closest('.calendar-input-container')
      .find('.txtTime')
    var spinnerh = $(spinner).find('.spinnerHour')
    var spinnermin = $(spinner).find('.spinnerMin')
    if ($(spinner).attr('aria-hidden') == 'true') {
      loadSpinner(spinner, txt)
    } else {
      hideSpinner(spinner, txt, spinnerh, spinnermin)
    }
  })

  //Hour and Minute
  $('.spinnerHour, .spinnerMin').each(function(e) {
    return spinbutton($(this).attr('id'), $(this).attr('bb-skip'), e)
  })

  $(
    '.btnHourUp, .btnHourDown, .btnMinUp, .btnMinDown',
    '.spinner-control'
  ).click(function() {
    handleClick($(this).attr('id'))
  })

  $('.spinner-control *').on('keydown', function(e) {
    if (e.which == 13) {
      var spinnerh = $(this)
        .closest('.time-spinner')
        .find('.spinnerHour')
        .attr('value')
      var spinnermin = $(this)
        .closest('.time-spinner')
        .find('.spinnerMin')
        .attr('value')

      var spinner = $(this).closest('.time-spinner')
      var txt = $(this)
        .closest('.time-spinner')
        .find('.txtTime')
      var newTime = spinnerh + ':' + spinnermin

      $(txt)
        .attr('value', newTime)
        .focus()
      $('.spinner-control').attr('aria-hidden', 'true')
      $('.spinner-control').fadeOut(100)
      return false
    } else if (e.which == 27) {
      hideSpinner(spinner, txt, spinnerh, spinnermin)
      return false
    }
  })

  //Direct Time Entry
  $('.txtTime').on('keydown', function(event) {
    if (e.which == 13) {
      return checkForm($(this))
    }
  })

  $('.spinner-control, .btn-time').on('click', function(event) {
    event.stopPropagation()
  })

  $('.form-control.txtTime').on('blur', function() {
    checkForm(this)
  })
  $('input.spinnerHour, input.spinnerMin').on('keypress', function(event) {
    return false
  })

  // click out to close
  $(window).click(function() {
    var spinner = $('.spinner-control[aria-hidden="false"]')
    var spinnerh = $(spinner).find('.spinnerHour')
    var spinnermin = $(spinner).find('.spinnerMin')
    var txt = $(spinner)
      .closest('.time-spinner')
      .find('.txtTime')
    if ($(spinner).length > 0) {
      hideSpinner(spinner, txt, spinnerh, spinnermin)
    }
  })
})