package lucee.runtime.functions.other;

import de.mkammerer.argon2.Argon2;
import de.mkammerer.argon2.Argon2Factory;
import de.mkammerer.argon2.Argon2Factory.Argon2Types;
import lucee.runtime.PageContext;
import lucee.runtime.exp.ExpressionException;
import lucee.runtime.exp.FunctionException;
import lucee.runtime.exp.PageException;
import lucee.runtime.ext.function.BIF;
import lucee.runtime.op.Caster;

public class GenerateArgon2Hash extends BIF {
	private static final long serialVersionUID = 61397352504711269L;

	@Override
	public Object invoke(PageContext pc, Object[] args) throws PageException {
		if (args.length != 5) {
			throw new FunctionException(pc, "GenerateArgon2Hash", 5, 5, args.length);
		}
		return call(pc, Caster.toString(args[0]), Caster.toString(args[1]), Caster.toByteValue(args[2]), Caster.toIntValue(args[3]), Caster.toByteValue(args[4]));
	}

	public static String call(PageContext pc, String variant, String input, double parallelismFactor, double memoryCost, double iterations) throws PageException {
		Argon2Types type;
		switch (variant.toLowerCase()) {
		case "argon2i":
			type = Argon2Types.ARGON2i;
			break;
		case "argon2d":
			type = Argon2Types.ARGON2d;
			break;
		case "argon2id":
			type = Argon2Types.ARGON2id;
			break;
		default:
			throw new FunctionException(pc, "GenerateArgon2Hash", 1, "variant", "The Variant should be ARGON2i or ARGON2d or ARGON2id", null);
		}
		Argon2 argon2 = Argon2Factory.create(type);

		if (parallelismFactor < 1 || parallelismFactor > 10) {
			throw new FunctionException(pc, "GenerateArgon2Hash", 2, "parallelismFactor", "The parallelism factor value should be between 1 and 10", null);
		}

		if (memoryCost < 8 || memoryCost > 100000) {
			throw new FunctionException(pc, "GenerateArgon2Hash", 3, "memoryCost", "The memory cost value should be between 8 and 100000", null);
		}

		if (iterations < 1 || iterations > 20) {
			throw new FunctionException(pc, "GenerateArgon2Hash", 4, "iterations", "The iterations value should be between 1 and 20", null);
		}

		int memory = Caster.toIntValue(memoryCost);
		String hash = argon2.hash(Caster.toIntValue(iterations), memory * memory, Caster.toIntValue(parallelismFactor), input.toCharArray());
		boolean success = argon2.verify(hash, input.toCharArray());

		if (!success) {
			throw new ExpressionException("Hashing failed!");
		}
		return hash;
	}
}

@@ -1047,16 +1047,184 @@
 es type;
+%0A%0A%09%09// check variant%0A%09%09if (variant == null) throw new FunctionException(pc, %22GenerateArgon2Hash%22, 1, %22variant%22, %22The Variant should be ARGON2i or ARGON2d or ARGON2id%22);
 %0A%09%09switc
@@ -1566,22 +1566,16 @@
 RGON2id%22
-, null
 );%0A%09%09%7D%0A%09
@@ -2244,16 +2244,88 @@
 yCost);%0A
+%09%09char%5B%5D carrInput = input == null ? new char%5B0%5D : input.toCharArray();%0A
 %09%09String
@@ -2430,35 +2430,25 @@
 actor), 
-input.toCharArray()
+carrInput
 );%0A%09%09boo
@@ -2482,35 +2482,25 @@
 y(hash, 
-input.toCharArray()
+carrInput
 );%0A%0A%09%09if

improve argument validation