import { URIRegExps } from './uri';
import { merge, subexp } from './util';

export function buildExps(isIRI:boolean):URIRegExps {
	const
		ALPHA$$ = '[A-Za-z]',
		CR$ = '[\\x0D]',
		DIGIT$$ = '[0-9]',
		DQUOTE$$ = '[\\x22]',
		HEXDIG$$ = merge(DIGIT$$, '[A-Fa-f]'),  // case-insensitive
		LF$$ = '[\\x0A]',
		SP$$ = '[\\x20]',
		PCT_ENCODED$ = subexp(subexp('%[EFef]' + HEXDIG$$ + '%' + HEXDIG$$ + HEXDIG$$ + '%' + HEXDIG$$ + HEXDIG$$) + '|' + subexp('%[89A-Fa-f]' + HEXDIG$$ + '%' + HEXDIG$$ + HEXDIG$$) + '|' + subexp('%' + HEXDIG$$ + HEXDIG$$)),  // expanded
		GEN_DELIMS$$ = '[\\:\\/\\?\\#\\[\\]\\@]',
		SUB_DELIMS$$ = '[\\!\\$\\&\\\'\\(\\)\\*\\+\\,\\;\\=]',
		RESERVED$$ = merge(GEN_DELIMS$$, SUB_DELIMS$$),
		UCSCHAR$$ = isIRI ? '[\\xA0-\\u200D\\u2010-\\u2029\\u202F-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]' : '[]',  // subset, excludes bidi control characters
		IPRIVATE$$ = isIRI ? '[\\uE000-\\uF8FF]' : '[]',  // subset
		UNRESERVED$$ = merge(ALPHA$$, DIGIT$$, '[\\-\\.\\_\\~]', UCSCHAR$$),
		SCHEME$ = subexp(ALPHA$$ + merge(ALPHA$$, DIGIT$$, '[\\+\\-\\.]') + '*'),
		USERINFO$ = subexp(subexp(PCT_ENCODED$ + '|' + merge(UNRESERVED$$, SUB_DELIMS$$, '[\\:]')) + '*'),
		DEC_OCTET$ = subexp(subexp('25[0-5]') + '|' + subexp('2[0-4]' + DIGIT$$) + '|' + subexp('1' + DIGIT$$ + DIGIT$$) + '|' + subexp('[1-9]' + DIGIT$$) + '|' + DIGIT$$),
		DEC_OCTET_RELAXED$ = subexp(subexp('25[0-5]') + '|' + subexp('2[0-4]' + DIGIT$$) + '|' + subexp('1' + DIGIT$$ + DIGIT$$) + '|' + subexp('0?[1-9]' + DIGIT$$) + '|0?0?' + DIGIT$$),  // relaxed parsing rules
		IPV4ADDRESS$ = subexp(DEC_OCTET_RELAXED$ + '\\.' + DEC_OCTET_RELAXED$ + '\\.' + DEC_OCTET_RELAXED$ + '\\.' + DEC_OCTET_RELAXED$),
		H16$ = subexp(HEXDIG$$ + '{1,4}'),
		LS32$ = subexp(subexp(H16$ + '\\:' + H16$) + '|' + IPV4ADDRESS$),
		IPV6ADDRESS1$ = subexp(                                                            subexp(H16$ + '\\:') + '{6}' + LS32$), //                           6( h16 ":" ) ls32
		IPV6ADDRESS2$ = subexp(                                                 '\\:\\:' + subexp(H16$ + '\\:') + '{5}' + LS32$), //                      "::" 5( h16 ":" ) ls32
		IPV6ADDRESS3$ = subexp(subexp(                                 H16$) + '?\\:\\:' + subexp(H16$ + '\\:') + '{4}' + LS32$), // [               h16 ] "::" 4( h16 ":" ) ls32
		IPV6ADDRESS4$ = subexp(subexp(subexp(H16$ + '\\:') + '{0,1}' + H16$) + '?\\:\\:' + subexp(H16$ + '\\:') + '{3}' + LS32$), // [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
		IPV6ADDRESS5$ = subexp(subexp(subexp(H16$ + '\\:') + '{0,2}' + H16$) + '?\\:\\:' + subexp(H16$ + '\\:') + '{2}' + LS32$), // [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
		IPV6ADDRESS6$ = subexp(subexp(subexp(H16$ + '\\:') + '{0,3}' + H16$) + '?\\:\\:' +        H16$ + '\\:'          + LS32$), // [ *3( h16 ":" ) h16 ] "::"    h16 ":"   ls32
		IPV6ADDRESS7$ = subexp(subexp(subexp(H16$ + '\\:') + '{0,4}' + H16$) + '?\\:\\:'                                + LS32$), // [ *4( h16 ":" ) h16 ] "::"              ls32
		IPV6ADDRESS8$ = subexp(subexp(subexp(H16$ + '\\:') + '{0,5}' + H16$) + '?\\:\\:'                                + H16$ ), // [ *5( h16 ":" ) h16 ] "::"              h16
		IPV6ADDRESS9$ = subexp(subexp(subexp(H16$ + '\\:') + '{0,6}' + H16$) + '?\\:\\:'                                       ), // [ *6( h16 ":" ) h16 ] "::"
		IPV6ADDRESS$ = subexp([IPV6ADDRESS1$, IPV6ADDRESS2$, IPV6ADDRESS3$, IPV6ADDRESS4$, IPV6ADDRESS5$, IPV6ADDRESS6$, IPV6ADDRESS7$, IPV6ADDRESS8$, IPV6ADDRESS9$].join('|')),
		ZONEID$ = subexp(subexp(UNRESERVED$$ + '|' + PCT_ENCODED$) + '+'),  // RFC 6874
		IPV6ADDRZ$ = subexp(IPV6ADDRESS$ + '\\%25' + ZONEID$),  // RFC 6874
		IPV6ADDRZ_RELAXED$ = subexp(IPV6ADDRESS$ + subexp('\\%25|\\%(?!' + HEXDIG$$ + '{2})') + ZONEID$),  // RFC 6874, with relaxed parsing rules
		IPVFUTURE$ = subexp('[vV]' + HEXDIG$$ + '+\\.' + merge(UNRESERVED$$, SUB_DELIMS$$, '[\\:]') + '+'),
		IP_LITERAL$ = subexp('\\[' + subexp(IPV6ADDRZ_RELAXED$ + '|' + IPV6ADDRESS$ + '|' + IPVFUTURE$) + '\\]'),  // RFC 6874
		REG_NAME$ = subexp(subexp(PCT_ENCODED$ + '|' + merge(UNRESERVED$$, SUB_DELIMS$$)) + '*'),
		HOST$ = subexp(IP_LITERAL$ + '|' + IPV4ADDRESS$ + '(?!' + REG_NAME$ + ')' + '|' + REG_NAME$),
		PORT$ = subexp(DIGIT$$ + '*'),
		AUTHORITY$ = subexp(subexp(USERINFO$ + '@') + '?' + HOST$ + subexp('\\:' + PORT$) + '?'),
		PCHAR$ = subexp(PCT_ENCODED$ + '|' + merge(UNRESERVED$$, SUB_DELIMS$$, '[\\:\\@]')),
		SEGMENT$ = subexp(PCHAR$ + '*'),
		SEGMENT_NZ$ = subexp(PCHAR$ + '+'),
		SEGMENT_NZ_NC$ = subexp(subexp(PCT_ENCODED$ + '|' + merge(UNRESERVED$$, SUB_DELIMS$$, '[\\@]')) + '+'),
		PATH_ABEMPTY$ = subexp(subexp('\\/' + SEGMENT$) + '*'),
		PATH_ABSOLUTE$ = subexp('\\/' + subexp(SEGMENT_NZ$ + PATH_ABEMPTY$) + '?'),  // simplified
		PATH_NOSCHEME$ = subexp(SEGMENT_NZ_NC$ + PATH_ABEMPTY$),  // simplified
		PATH_ROOTLESS$ = subexp(SEGMENT_NZ$ + PATH_ABEMPTY$),  // simplified
		PATH_EMPTY$ = '(?!' + PCHAR$ + ')',
		PATH$ = subexp(PATH_ABEMPTY$ + '|' + PATH_ABSOLUTE$ + '|' + PATH_NOSCHEME$ + '|' + PATH_ROOTLESS$ + '|' + PATH_EMPTY$),
		QUERY$ = subexp(subexp(PCHAR$ + '|' + merge('[\\/\\?]', IPRIVATE$$)) + '*'),
		FRAGMENT$ = subexp(subexp(PCHAR$ + '|[\\/\\?]') + '*'),
		HIER_PART$ = subexp(subexp('\\/\\/' + AUTHORITY$ + PATH_ABEMPTY$) + '|' + PATH_ABSOLUTE$ + '|' + PATH_ROOTLESS$ + '|' + PATH_EMPTY$),
		URI$ = subexp(SCHEME$ + '\\:' + HIER_PART$ + subexp('\\?' + QUERY$) + '?' + subexp('\\#' + FRAGMENT$) + '?'),
		RELATIVE_PART$ = subexp(subexp('\\/\\/' + AUTHORITY$ + PATH_ABEMPTY$) + '|' + PATH_ABSOLUTE$ + '|' + PATH_NOSCHEME$ + '|' + PATH_EMPTY$),
		RELATIVE$ = subexp(RELATIVE_PART$ + subexp('\\?' + QUERY$) + '?' + subexp('\\#' + FRAGMENT$) + '?'),
		URI_REFERENCE$ = subexp(URI$ + '|' + RELATIVE$),
		ABSOLUTE_URI$ = subexp(SCHEME$ + '\\:' + HIER_PART$ + subexp('\\?' + QUERY$) + '?'),

		GENERIC_REF$ = '^(' + SCHEME$ + ')\\:' + subexp(subexp('\\/\\/(' + subexp('(' + USERINFO$ + ')@') + '?(' + HOST$ + ')' + subexp('\\:(' + PORT$ + ')') + '?)') + '?(' + PATH_ABEMPTY$ + '|' + PATH_ABSOLUTE$ + '|' + PATH_ROOTLESS$ + '|' + PATH_EMPTY$ + ')') + subexp('\\?(' + QUERY$ + ')') + '?' + subexp('\\#(' + FRAGMENT$ + ')') + '?$',
		RELATIVE_REF$ = '^(){0}' + subexp(subexp('\\/\\/(' + subexp('(' + USERINFO$ + ')@') + '?(' + HOST$ + ')' + subexp('\\:(' + PORT$ + ')') + '?)') + '?(' + PATH_ABEMPTY$ + '|' + PATH_ABSOLUTE$ + '|' + PATH_NOSCHEME$ + '|' + PATH_EMPTY$ + ')') + subexp('\\?(' + QUERY$ + ')') + '?' + subexp('\\#(' + FRAGMENT$ + ')') + '?$',
		ABSOLUTE_REF$ = '^(' + SCHEME$ + ')\\:' + subexp(subexp('\\/\\/(' + subexp('(' + USERINFO$ + ')@') + '?(' + HOST$ + ')' + subexp('\\:(' + PORT$ + ')') + '?)') + '?(' + PATH_ABEMPTY$ + '|' + PATH_ABSOLUTE$ + '|' + PATH_ROOTLESS$ + '|' + PATH_EMPTY$ + ')') + subexp('\\?(' + QUERY$ + ')') + '?$',
		SAMEDOC_REF$ = '^' + subexp('\\#(' + FRAGMENT$ + ')') + '?$',
		AUTHORITY_REF$ = '^' + subexp('(' + USERINFO$ + ')@') + '?(' + HOST$ + ')' + subexp('\\:(' + PORT$ + ')') + '?$'
	;

	return {
		NOT_SCHEME : new RegExp(merge('[^]', ALPHA$$, DIGIT$$, '[\\+\\-\\.]'), 'g'),
		NOT_USERINFO : new RegExp(merge('[^\\%\\:]', UNRESERVED$$, SUB_DELIMS$$), 'g'),
		NOT_HOST : new RegExp(merge('[^\\%\\[\\]\\:]', UNRESERVED$$, SUB_DELIMS$$), 'g'),
		NOT_PATH : new RegExp(merge('[^\\%\\/\\:\\@]', UNRESERVED$$, SUB_DELIMS$$), 'g'),
		NOT_PATH_NOSCHEME : new RegExp(merge('[^\\%\\/\\@]', UNRESERVED$$, SUB_DELIMS$$), 'g'),
		NOT_QUERY : new RegExp(merge('[^\\%]', UNRESERVED$$, SUB_DELIMS$$, '[\\:\\@\\/\\?]', IPRIVATE$$), 'g'),
		NOT_FRAGMENT : new RegExp(merge('[^\\%]', UNRESERVED$$, SUB_DELIMS$$, '[\\:\\@\\/\\?]'), 'g'),
		ESCAPE : new RegExp(merge('[^]', UNRESERVED$$, SUB_DELIMS$$), 'g'),
		UNRESERVED : new RegExp(UNRESERVED$$, 'g'),
		OTHER_CHARS : new RegExp(merge('[^\\%]', UNRESERVED$$, RESERVED$$), 'g'),
		PCT_ENCODED : new RegExp(PCT_ENCODED$, 'g'),
		IPV4ADDRESS : new RegExp('^(' + IPV4ADDRESS$ + ')$'),
		IPV6ADDRESS : new RegExp('^\\[?(' + IPV6ADDRESS$ + ')' + subexp(subexp('\\%25|\\%(?!' + HEXDIG$$ + '{2})') + '(' + ZONEID$ + ')') + '?\\]?$')  // RFC 6874, with relaxed parsing rules
	};
}

export default buildExps(false);
