-- local led_pin = 1 --GPIO 5
local led_pin = 2 --nodeMCU LED
local override_pin = 4 --GPIO 4

local HEARTBEAT_times = {40, 200, 40, 900} --format is alternating on and off times in ms
local TRIPLEBLINK_times = {100, 20, 100, 20, 100, 20, 100} --format is alternating off and on times in ms
local led_pwm_frequency = 500 --units: hz
local led_max_brightness = 1023
local current_pattern
local pattern_queue = {}
local HEARTBEAT_index = 1
local TRIPLEBLINK_index = 1

--pattern definitions
local patterns = {
  STOPPED = 0,
  HEARTBEAT = 1,
  FADEIN = 2,
  FADEOUT = 3,
  TRIPLEBLINK = 4
}

local patterns_initial_pwm_duty = {
  [0] = 0,
  0,
  0,
  led_max_brightness,
  led_max_brightness
}

local pattern_intervals = {
  [0] = 0,
  HEARTBEAT_times,
  1,
  1,
  TRIPLEBLINK_times
}

--dependencies
local TIMER = TIMERS.led
local debug_message = debug_message
local gpio = gpio
local pwm = pwm
local tmr = tmr
local table = table

local print = print
module(...)

function testcall()
  do_pattern(patterns.FADEOUT)
end

function init()
  debug_message('led.init')

  --handle timer, take exclusive control
  tmr.unregister(TIMER)
  current_pattern = patterns.STOPPED

  --init pins
  gpio.mode(override_pin, gpio.OUTPUT)
  pwm.setup(led_pin, led_pwm_frequency, 0)
  pwm.start(led_pin)
  pwm.stop(led_pin)
end

function stop()
  tmr.unregister(TIMER)
  --deassert control
  gpio.write(override_pin, gpio.LOW)
  current_pattern = patterns.STOPPED
  pattern_queue = {}
end

function do_pattern(next_pattern)
  debug_message('led.do_pattern: ' .. next_pattern)

  if(next_pattern == current_pattern) then
    debug_message('led.do_pattern: next same as current')
    return
  end
  if current_pattern ~= patterns.STOPPED then
    debug_message('led.do_pattern: current not STOPPED')
    --put next_pattern in queue
    table.insert(pattern_queue, next_pattern)
  else
    debug_message('current, next: ' .. current_pattern .. ', ' .. next_pattern)
    --start next_pattern!
    current_pattern = next_pattern
    -- override_enable()
    gpio.write(override_pin, gpio.HIGH)

    pattern(
      patterns_initial_pwm_duty[next_pattern],
      pattern_intervals[next_pattern],
      next_pattern_funcs[next_pattern]
    )
  end

  --rewrite queue:
  --when queueing new pattern, if transition (or flag) not nil then do immediately
  --otherwise enqueue and let end_function or similar get next from queue

  --STOP should instead a pattern like all others, renamed as OFF

  --transition functions transition(t) --> t+1
  --if t+1 is nil, end pattern

end

--TODO: intervals not used (still using global state)
function pattern(initial_pwm_duty, intervals, transition)
  debug_message('led.pattern')
  pwm.setduty(led_pin, initial_pwm_duty)
  local pwm_duty = initial_pwm_duty

  --looping timer instead of alarm_single, can deregister on nil
  transition()
end

function FADEIN_update()
  local current_brightness = pwm.getduty(led_pin)
  current_brightness = current_brightness + 1
  if(current_brightness > led_max_brightness) then
    current_brightness = led_max_brightness
  end
  pwm.setduty(led_pin, current_brightness)

  if current_brightness < led_max_brightness then
    tmr.alarm(TIMER, 2, tmr.ALARM_SINGLE, FADEIN_update)
  else
    end_pattern()
  end
end

function FADEOUT_update()
  local current_brightness = pwm.getduty(led_pin)
  current_brightness = current_brightness - 1
  if(current_brightness < 0) then
    current_brightness = 0
  end
  pwm.setduty(led_pin, current_brightness)

  if current_brightness > 0 then
    tmr.alarm(TIMER, 2, tmr.ALARM_SINGLE, FADEOUT_update)
  else
    end_pattern()
  end
end

function HEARTBEAT_update()
  local current_brightness = pwm.getduty(led_pin)
  current_brightness = led_max_brightness - current_brightness
  pwm.setduty(led_pin, current_brightness)
  tmr.alarm(TIMER, HEARTBEAT_times[HEARTBEAT_index], tmr.ALARM_SINGLE, HEARTBEAT_update)
  HEARTBEAT_index = HEARTBEAT_index + 1
  if HEARTBEAT_index > table.getn(HEARTBEAT_times) then
    HEARTBEAT_index = 1
  end
  --pattern does not end
end

function TRIPLEBLINK_update()
  local current_brightness = pwm.getduty(led_pin)
  current_brightness = led_max_brightness - current_brightness
  pwm.setduty(led_pin, current_brightness)
  tmr.alarm(TIMER, TRIPLEBLINK_times[TRIPLEBLINK_index], tmr.ALARM_SINGLE, TRIPLEBLINK_update)
  TRIPLEBLINK_index = TRIPLEBLINK_index + 1
  if TRIPLEBLINK_index > table.getn(TRIPLEBLINK_times) then
    end_pattern()
  end
end

next_pattern_funcs = {
  HEARTBEAT_update,
  FADEIN_update,
  FADEOUT_update,
  TRIPLEBLINK_update
}

function end_pattern()
  if table.getn(pattern_queue) == 0 then
    stop()
  else
    p = pattern_queue[1]
    table.remove(pattern_queue, 1)
    current_pattern = patterns.STOPPED
    tmr.unregister(TIMER)
    do_pattern(p)
  end
end

init(TIMER)
