# 86af1e511043a064c69e324b9d461d613dd427735721ebf8aefebefc807d80c8 Elan 2.0.0-alpha4 valid

# Ideas for further development
# - rule for no more than 'n' same characters in succession
constant upperChars set to "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

constant lowerChars set to "abcdefghijklmnopqrstuvwxyz"

constant digitChars set to "01234567890"

constant symbolChars set to "!$%^&*"

constant length set to 32

constant upper set to true

constant lower set to true

constant digit set to true

constant symbol set to true

main
  variable all set to upperChars + lowerChars + digitChars + symbolChars
  variable password set to ""
  variable valid set to false
  while not valid
    variable pwRef set to new AsRef<of String>("")
    call populatePassword(pwRef, all)
    set password to pwRef.value()
    set valid to isValid(password, upper, lower, digit, symbol)
  end while
  call print(password)
end main

procedure populatePassword(password as AsRef<of String>, all as String)
  for i in range(1, length + 1)
    variable rnd set to randint(0, all.length() - 1)
    call password.set(password.value() + all[rnd])
  end for
end procedure

function isValid(password as String, mustHaveUpper as Boolean, mustHaveLower as Boolean, mustHaveDigit as Boolean, mustHaveSymbol as Boolean) returns Boolean
  variable validForUpper set to passesRule(mustHaveUpper, upperChars, password)
  variable validForLower set to passesRule(mustHaveLower, lowerChars, password)
  variable validForDigits set to passesRule(mustHaveDigit, digitChars, password)
  variable validForSymbols set to passesRule(mustHaveSymbol, symbolChars, password)
  return validForUpper and validForLower and validForDigits and validForSymbols
end function

test test_isValid
  assert isValid("$4De", true, true, true, true) is true
  assert isValid("$4de", true, true, true, true) is false
  assert isValid("$4de", false, true, true, true) is true
  assert isValid("eD$4", true, true, true, true) is true
  assert isValid("$4De$4De", true, true, true, true) is true
end test

function passesRule(rule as Boolean, charSet as String, password as String) returns Boolean
  return (not rule) or hasAtLeastOneFrom(charSet, password)
end function

test test_passesRule
  assert passesRule(true, "12A", "ABC") is true
  assert passesRule(false, "12A", "ABC") is true
  assert passesRule(true, "12", "ABCD") is false
  assert passesRule(false, "12", "ABCD") is true
end test

function hasAtLeastOneFrom(fromChars as String, password as String) returns Boolean
  variable hasOne set to false
  for ch in password
    set hasOne to hasOne or fromChars.contains(ch)
  end for
  return hasOne
end function

test test_hasAtLeastOneFrom
  assert hasAtLeastOneFrom("12A", "ABC") is true
  assert hasAtLeastOneFrom("C12", "ABC") is true
  assert hasAtLeastOneFrom("12", "ABCD") is false
end test
