/* eslint-disable */

/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

/**
 * An inlined enum containing useful character codes (to be used with String.charCodeAt).
 * Please leave the const keyword such that it gets inlined when compiled to JavaScript!
 */
export const enum CharCode {
  Null = 0,
  /**
   * The `\b` character.
   */
  Backspace = 8,
  /**
   * The `\t` character.
   */
  Tab = 9,
  /**
   * The `\n` character.
   */
  LineFeed = 10,
  /**
   * The `\r` character.
   */
  CarriageReturn = 13,
  Space = 32,
  /**
   * The `!` character.
   */
  ExclamationMark = 33,
  /**
   * The `"` character.
   */
  DoubleQuote = 34,
  /**
   * The `#` character.
   */
  Hash = 35,
  /**
   * The `$` character.
   */
  DollarSign = 36,
  /**
   * The `%` character.
   */
  PercentSign = 37,
  /**
   * The `&` character.
   */
  Ampersand = 38,
  /**
   * The `'` character.
   */
  SingleQuote = 39,
  /**
   * The `(` character.
   */
  OpenParen = 40,
  /**
   * The `)` character.
   */
  CloseParen = 41,
  /**
   * The `*` character.
   */
  Asterisk = 42,
  /**
   * The `+` character.
   */
  Plus = 43,
  /**
   * The `,` character.
   */
  Comma = 44,
  /**
   * The `-` character.
   */
  Dash = 45,
  /**
   * The `.` character.
   */
  Period = 46,
  /**
   * The `/` character.
   */
  Slash = 47,

  Digit0 = 48,
  Digit1 = 49,
  Digit2 = 50,
  Digit3 = 51,
  Digit4 = 52,
  Digit5 = 53,
  Digit6 = 54,
  Digit7 = 55,
  Digit8 = 56,
  Digit9 = 57,

  /**
   * The `:` character.
   */
  Colon = 58,
  /**
   * The `;` character.
   */
  Semicolon = 59,
  /**
   * The `<` character.
   */
  LessThan = 60,
  /**
   * The `=` character.
   */
  Equals = 61,
  /**
   * The `>` character.
   */
  GreaterThan = 62,
  /**
   * The `?` character.
   */
  QuestionMark = 63,
  /**
   * The `@` character.
   */
  AtSign = 64,

  A = 65,
  B = 66,
  C = 67,
  D = 68,
  E = 69,
  F = 70,
  G = 71,
  H = 72,
  I = 73,
  J = 74,
  K = 75,
  L = 76,
  M = 77,
  N = 78,
  O = 79,
  P = 80,
  Q = 81,
  R = 82,
  S = 83,
  T = 84,
  U = 85,
  V = 86,
  W = 87,
  X = 88,
  Y = 89,
  Z = 90,

  /**
   * The `[` character.
   */
  OpenSquareBracket = 91,
  /**
   * The `\` character.
   */
  Backslash = 92,
  /**
   * The `]` character.
   */
  CloseSquareBracket = 93,
  /**
   * The `^` character.
   */
  Caret = 94,
  /**
   * The `_` character.
   */
  Underline = 95,
  /**
   * The ``(`)`` character.
   */
  BackTick = 96,

  a = 97,
  b = 98,
  c = 99,
  d = 100,
  e = 101,
  f = 102,
  g = 103,
  h = 104,
  i = 105,
  j = 106,
  k = 107,
  l = 108,
  m = 109,
  n = 110,
  o = 111,
  p = 112,
  q = 113,
  r = 114,
  s = 115,
  t = 116,
  u = 117,
  v = 118,
  w = 119,
  x = 120,
  y = 121,
  z = 122,

  /**
   * The `{` character.
   */
  OpenCurlyBrace = 123,
  /**
   * The `|` character.
   */
  Pipe = 124,
  /**
   * The `}` character.
   */
  CloseCurlyBrace = 125,
  /**
   * The `~` character.
   */
  Tilde = 126,

  U_Combining_Grave_Accent = 0x0300, //	U+0300	Combining Grave Accent
  U_Combining_Acute_Accent = 0x0301, //	U+0301	Combining Acute Accent
  U_Combining_Circumflex_Accent = 0x0302, //	U+0302	Combining Circumflex Accent
  U_Combining_Tilde = 0x0303, //	U+0303	Combining Tilde
  U_Combining_Macron = 0x0304, //	U+0304	Combining Macron
  U_Combining_Overline = 0x0305, //	U+0305	Combining Overline
  U_Combining_Breve = 0x0306, //	U+0306	Combining Breve
  U_Combining_Dot_Above = 0x0307, //	U+0307	Combining Dot Above
  U_Combining_Diaeresis = 0x0308, //	U+0308	Combining Diaeresis
  U_Combining_Hook_Above = 0x0309, //	U+0309	Combining Hook Above
  U_Combining_Ring_Above = 0x030a, //	U+030A	Combining Ring Above
  U_Combining_Double_Acute_Accent = 0x030b, //	U+030B	Combining Double Acute Accent
  U_Combining_Caron = 0x030c, //	U+030C	Combining Caron
  U_Combining_Vertical_Line_Above = 0x030d, //	U+030D	Combining Vertical Line Above
  U_Combining_Double_Vertical_Line_Above = 0x030e, //	U+030E	Combining Double Vertical Line Above
  U_Combining_Double_Grave_Accent = 0x030f, //	U+030F	Combining Double Grave Accent
  U_Combining_Candrabindu = 0x0310, //	U+0310	Combining Candrabindu
  U_Combining_Inverted_Breve = 0x0311, //	U+0311	Combining Inverted Breve
  U_Combining_Turned_Comma_Above = 0x0312, //	U+0312	Combining Turned Comma Above
  U_Combining_Comma_Above = 0x0313, //	U+0313	Combining Comma Above
  U_Combining_Reversed_Comma_Above = 0x0314, //	U+0314	Combining Reversed Comma Above
  U_Combining_Comma_Above_Right = 0x0315, //	U+0315	Combining Comma Above Right
  U_Combining_Grave_Accent_Below = 0x0316, //	U+0316	Combining Grave Accent Below
  U_Combining_Acute_Accent_Below = 0x0317, //	U+0317	Combining Acute Accent Below
  U_Combining_Left_Tack_Below = 0x0318, //	U+0318	Combining Left Tack Below
  U_Combining_Right_Tack_Below = 0x0319, //	U+0319	Combining Right Tack Below
  U_Combining_Left_Angle_Above = 0x031a, //	U+031A	Combining Left Angle Above
  U_Combining_Horn = 0x031b, //	U+031B	Combining Horn
  U_Combining_Left_Half_Ring_Below = 0x031c, //	U+031C	Combining Left Half Ring Below
  U_Combining_Up_Tack_Below = 0x031d, //	U+031D	Combining Up Tack Below
  U_Combining_Down_Tack_Below = 0x031e, //	U+031E	Combining Down Tack Below
  U_Combining_Plus_Sign_Below = 0x031f, //	U+031F	Combining Plus Sign Below
  U_Combining_Minus_Sign_Below = 0x0320, //	U+0320	Combining Minus Sign Below
  U_Combining_Palatalized_Hook_Below = 0x0321, //	U+0321	Combining Palatalized Hook Below
  U_Combining_Retroflex_Hook_Below = 0x0322, //	U+0322	Combining Retroflex Hook Below
  U_Combining_Dot_Below = 0x0323, //	U+0323	Combining Dot Below
  U_Combining_Diaeresis_Below = 0x0324, //	U+0324	Combining Diaeresis Below
  U_Combining_Ring_Below = 0x0325, //	U+0325	Combining Ring Below
  U_Combining_Comma_Below = 0x0326, //	U+0326	Combining Comma Below
  U_Combining_Cedilla = 0x0327, //	U+0327	Combining Cedilla
  U_Combining_Ogonek = 0x0328, //	U+0328	Combining Ogonek
  U_Combining_Vertical_Line_Below = 0x0329, //	U+0329	Combining Vertical Line Below
  U_Combining_Bridge_Below = 0x032a, //	U+032A	Combining Bridge Below
  U_Combining_Inverted_Double_Arch_Below = 0x032b, //	U+032B	Combining Inverted Double Arch Below
  U_Combining_Caron_Below = 0x032c, //	U+032C	Combining Caron Below
  U_Combining_Circumflex_Accent_Below = 0x032d, //	U+032D	Combining Circumflex Accent Below
  U_Combining_Breve_Below = 0x032e, //	U+032E	Combining Breve Below
  U_Combining_Inverted_Breve_Below = 0x032f, //	U+032F	Combining Inverted Breve Below
  U_Combining_Tilde_Below = 0x0330, //	U+0330	Combining Tilde Below
  U_Combining_Macron_Below = 0x0331, //	U+0331	Combining Macron Below
  U_Combining_Low_Line = 0x0332, //	U+0332	Combining Low Line
  U_Combining_Double_Low_Line = 0x0333, //	U+0333	Combining Double Low Line
  U_Combining_Tilde_Overlay = 0x0334, //	U+0334	Combining Tilde Overlay
  U_Combining_Short_Stroke_Overlay = 0x0335, //	U+0335	Combining Short Stroke Overlay
  U_Combining_Long_Stroke_Overlay = 0x0336, //	U+0336	Combining Long Stroke Overlay
  U_Combining_Short_Solidus_Overlay = 0x0337, //	U+0337	Combining Short Solidus Overlay
  U_Combining_Long_Solidus_Overlay = 0x0338, //	U+0338	Combining Long Solidus Overlay
  U_Combining_Right_Half_Ring_Below = 0x0339, //	U+0339	Combining Right Half Ring Below
  U_Combining_Inverted_Bridge_Below = 0x033a, //	U+033A	Combining Inverted Bridge Below
  U_Combining_Square_Below = 0x033b, //	U+033B	Combining Square Below
  U_Combining_Seagull_Below = 0x033c, //	U+033C	Combining Seagull Below
  U_Combining_X_Above = 0x033d, //	U+033D	Combining X Above
  U_Combining_Vertical_Tilde = 0x033e, //	U+033E	Combining Vertical Tilde
  U_Combining_Double_Overline = 0x033f, //	U+033F	Combining Double Overline
  U_Combining_Grave_Tone_Mark = 0x0340, //	U+0340	Combining Grave Tone Mark
  U_Combining_Acute_Tone_Mark = 0x0341, //	U+0341	Combining Acute Tone Mark
  U_Combining_Greek_Perispomeni = 0x0342, //	U+0342	Combining Greek Perispomeni
  U_Combining_Greek_Koronis = 0x0343, //	U+0343	Combining Greek Koronis
  U_Combining_Greek_Dialytika_Tonos = 0x0344, //	U+0344	Combining Greek Dialytika Tonos
  U_Combining_Greek_Ypogegrammeni = 0x0345, //	U+0345	Combining Greek Ypogegrammeni
  U_Combining_Bridge_Above = 0x0346, //	U+0346	Combining Bridge Above
  U_Combining_Equals_Sign_Below = 0x0347, //	U+0347	Combining Equals Sign Below
  U_Combining_Double_Vertical_Line_Below = 0x0348, //	U+0348	Combining Double Vertical Line Below
  U_Combining_Left_Angle_Below = 0x0349, //	U+0349	Combining Left Angle Below
  U_Combining_Not_Tilde_Above = 0x034a, //	U+034A	Combining Not Tilde Above
  U_Combining_Homothetic_Above = 0x034b, //	U+034B	Combining Homothetic Above
  U_Combining_Almost_Equal_To_Above = 0x034c, //	U+034C	Combining Almost Equal To Above
  U_Combining_Left_Right_Arrow_Below = 0x034d, //	U+034D	Combining Left Right Arrow Below
  U_Combining_Upwards_Arrow_Below = 0x034e, //	U+034E	Combining Upwards Arrow Below
  U_Combining_Grapheme_Joiner = 0x034f, //	U+034F	Combining Grapheme Joiner
  U_Combining_Right_Arrowhead_Above = 0x0350, //	U+0350	Combining Right Arrowhead Above
  U_Combining_Left_Half_Ring_Above = 0x0351, //	U+0351	Combining Left Half Ring Above
  U_Combining_Fermata = 0x0352, //	U+0352	Combining Fermata
  U_Combining_X_Below = 0x0353, //	U+0353	Combining X Below
  U_Combining_Left_Arrowhead_Below = 0x0354, //	U+0354	Combining Left Arrowhead Below
  U_Combining_Right_Arrowhead_Below = 0x0355, //	U+0355	Combining Right Arrowhead Below
  U_Combining_Right_Arrowhead_And_Up_Arrowhead_Below = 0x0356, //	U+0356	Combining Right Arrowhead And Up Arrowhead Below
  U_Combining_Right_Half_Ring_Above = 0x0357, //	U+0357	Combining Right Half Ring Above
  U_Combining_Dot_Above_Right = 0x0358, //	U+0358	Combining Dot Above Right
  U_Combining_Asterisk_Below = 0x0359, //	U+0359	Combining Asterisk Below
  U_Combining_Double_Ring_Below = 0x035a, //	U+035A	Combining Double Ring Below
  U_Combining_Zigzag_Above = 0x035b, //	U+035B	Combining Zigzag Above
  U_Combining_Double_Breve_Below = 0x035c, //	U+035C	Combining Double Breve Below
  U_Combining_Double_Breve = 0x035d, //	U+035D	Combining Double Breve
  U_Combining_Double_Macron = 0x035e, //	U+035E	Combining Double Macron
  U_Combining_Double_Macron_Below = 0x035f, //	U+035F	Combining Double Macron Below
  U_Combining_Double_Tilde = 0x0360, //	U+0360	Combining Double Tilde
  U_Combining_Double_Inverted_Breve = 0x0361, //	U+0361	Combining Double Inverted Breve
  U_Combining_Double_Rightwards_Arrow_Below = 0x0362, //	U+0362	Combining Double Rightwards Arrow Below
  U_Combining_Latin_Small_Letter_A = 0x0363, //	U+0363	Combining Latin Small Letter A
  U_Combining_Latin_Small_Letter_E = 0x0364, //	U+0364	Combining Latin Small Letter E
  U_Combining_Latin_Small_Letter_I = 0x0365, //	U+0365	Combining Latin Small Letter I
  U_Combining_Latin_Small_Letter_O = 0x0366, //	U+0366	Combining Latin Small Letter O
  U_Combining_Latin_Small_Letter_U = 0x0367, //	U+0367	Combining Latin Small Letter U
  U_Combining_Latin_Small_Letter_C = 0x0368, //	U+0368	Combining Latin Small Letter C
  U_Combining_Latin_Small_Letter_D = 0x0369, //	U+0369	Combining Latin Small Letter D
  U_Combining_Latin_Small_Letter_H = 0x036a, //	U+036A	Combining Latin Small Letter H
  U_Combining_Latin_Small_Letter_M = 0x036b, //	U+036B	Combining Latin Small Letter M
  U_Combining_Latin_Small_Letter_R = 0x036c, //	U+036C	Combining Latin Small Letter R
  U_Combining_Latin_Small_Letter_T = 0x036d, //	U+036D	Combining Latin Small Letter T
  U_Combining_Latin_Small_Letter_V = 0x036e, //	U+036E	Combining Latin Small Letter V
  U_Combining_Latin_Small_Letter_X = 0x036f, //	U+036F	Combining Latin Small Letter X

  /**
   * Unicode Character 'LINE SEPARATOR' (U+2028)
   * http://www.fileformat.info/info/unicode/char/2028/index.htm
   */
  LINE_SEPARATOR = 0x2028,
  /**
   * Unicode Character 'PARAGRAPH SEPARATOR' (U+2029)
   * http://www.fileformat.info/info/unicode/char/2029/index.htm
   */
  PARAGRAPH_SEPARATOR = 0x2029,
  /**
   * Unicode Character 'NEXT LINE' (U+0085)
   * http://www.fileformat.info/info/unicode/char/0085/index.htm
   */
  NEXT_LINE = 0x0085,

  // http://www.fileformat.info/info/unicode/category/Sk/list.htm
  U_CIRCUMFLEX = 0x005e, // U+005E	CIRCUMFLEX
  U_GRAVE_ACCENT = 0x0060, // U+0060	GRAVE ACCENT
  U_DIAERESIS = 0x00a8, // U+00A8	DIAERESIS
  U_MACRON = 0x00af, // U+00AF	MACRON
  U_ACUTE_ACCENT = 0x00b4, // U+00B4	ACUTE ACCENT
  U_CEDILLA = 0x00b8, // U+00B8	CEDILLA
  U_MODIFIER_LETTER_LEFT_ARROWHEAD = 0x02c2, // U+02C2	MODIFIER LETTER LEFT ARROWHEAD
  U_MODIFIER_LETTER_RIGHT_ARROWHEAD = 0x02c3, // U+02C3	MODIFIER LETTER RIGHT ARROWHEAD
  U_MODIFIER_LETTER_UP_ARROWHEAD = 0x02c4, // U+02C4	MODIFIER LETTER UP ARROWHEAD
  U_MODIFIER_LETTER_DOWN_ARROWHEAD = 0x02c5, // U+02C5	MODIFIER LETTER DOWN ARROWHEAD
  U_MODIFIER_LETTER_CENTRED_RIGHT_HALF_RING = 0x02d2, // U+02D2	MODIFIER LETTER CENTRED RIGHT HALF RING
  U_MODIFIER_LETTER_CENTRED_LEFT_HALF_RING = 0x02d3, // U+02D3	MODIFIER LETTER CENTRED LEFT HALF RING
  U_MODIFIER_LETTER_UP_TACK = 0x02d4, // U+02D4	MODIFIER LETTER UP TACK
  U_MODIFIER_LETTER_DOWN_TACK = 0x02d5, // U+02D5	MODIFIER LETTER DOWN TACK
  U_MODIFIER_LETTER_PLUS_SIGN = 0x02d6, // U+02D6	MODIFIER LETTER PLUS SIGN
  U_MODIFIER_LETTER_MINUS_SIGN = 0x02d7, // U+02D7	MODIFIER LETTER MINUS SIGN
  U_BREVE = 0x02d8, // U+02D8	BREVE
  U_DOT_ABOVE = 0x02d9, // U+02D9	DOT ABOVE
  U_RING_ABOVE = 0x02da, // U+02DA	RING ABOVE
  U_OGONEK = 0x02db, // U+02DB	OGONEK
  U_SMALL_TILDE = 0x02dc, // U+02DC	SMALL TILDE
  U_DOUBLE_ACUTE_ACCENT = 0x02dd, // U+02DD	DOUBLE ACUTE ACCENT
  U_MODIFIER_LETTER_RHOTIC_HOOK = 0x02de, // U+02DE	MODIFIER LETTER RHOTIC HOOK
  U_MODIFIER_LETTER_CROSS_ACCENT = 0x02df, // U+02DF	MODIFIER LETTER CROSS ACCENT
  U_MODIFIER_LETTER_EXTRA_HIGH_TONE_BAR = 0x02e5, // U+02E5	MODIFIER LETTER EXTRA-HIGH TONE BAR
  U_MODIFIER_LETTER_HIGH_TONE_BAR = 0x02e6, // U+02E6	MODIFIER LETTER HIGH TONE BAR
  U_MODIFIER_LETTER_MID_TONE_BAR = 0x02e7, // U+02E7	MODIFIER LETTER MID TONE BAR
  U_MODIFIER_LETTER_LOW_TONE_BAR = 0x02e8, // U+02E8	MODIFIER LETTER LOW TONE BAR
  U_MODIFIER_LETTER_EXTRA_LOW_TONE_BAR = 0x02e9, // U+02E9	MODIFIER LETTER EXTRA-LOW TONE BAR
  U_MODIFIER_LETTER_YIN_DEPARTING_TONE_MARK = 0x02ea, // U+02EA	MODIFIER LETTER YIN DEPARTING TONE MARK
  U_MODIFIER_LETTER_YANG_DEPARTING_TONE_MARK = 0x02eb, // U+02EB	MODIFIER LETTER YANG DEPARTING TONE MARK
  U_MODIFIER_LETTER_UNASPIRATED = 0x02ed, // U+02ED	MODIFIER LETTER UNASPIRATED
  U_MODIFIER_LETTER_LOW_DOWN_ARROWHEAD = 0x02ef, // U+02EF	MODIFIER LETTER LOW DOWN ARROWHEAD
  U_MODIFIER_LETTER_LOW_UP_ARROWHEAD = 0x02f0, // U+02F0	MODIFIER LETTER LOW UP ARROWHEAD
  U_MODIFIER_LETTER_LOW_LEFT_ARROWHEAD = 0x02f1, // U+02F1	MODIFIER LETTER LOW LEFT ARROWHEAD
  U_MODIFIER_LETTER_LOW_RIGHT_ARROWHEAD = 0x02f2, // U+02F2	MODIFIER LETTER LOW RIGHT ARROWHEAD
  U_MODIFIER_LETTER_LOW_RING = 0x02f3, // U+02F3	MODIFIER LETTER LOW RING
  U_MODIFIER_LETTER_MIDDLE_GRAVE_ACCENT = 0x02f4, // U+02F4	MODIFIER LETTER MIDDLE GRAVE ACCENT
  U_MODIFIER_LETTER_MIDDLE_DOUBLE_GRAVE_ACCENT = 0x02f5, // U+02F5	MODIFIER LETTER MIDDLE DOUBLE GRAVE ACCENT
  U_MODIFIER_LETTER_MIDDLE_DOUBLE_ACUTE_ACCENT = 0x02f6, // U+02F6	MODIFIER LETTER MIDDLE DOUBLE ACUTE ACCENT
  U_MODIFIER_LETTER_LOW_TILDE = 0x02f7, // U+02F7	MODIFIER LETTER LOW TILDE
  U_MODIFIER_LETTER_RAISED_COLON = 0x02f8, // U+02F8	MODIFIER LETTER RAISED COLON
  U_MODIFIER_LETTER_BEGIN_HIGH_TONE = 0x02f9, // U+02F9	MODIFIER LETTER BEGIN HIGH TONE
  U_MODIFIER_LETTER_END_HIGH_TONE = 0x02fa, // U+02FA	MODIFIER LETTER END HIGH TONE
  U_MODIFIER_LETTER_BEGIN_LOW_TONE = 0x02fb, // U+02FB	MODIFIER LETTER BEGIN LOW TONE
  U_MODIFIER_LETTER_END_LOW_TONE = 0x02fc, // U+02FC	MODIFIER LETTER END LOW TONE
  U_MODIFIER_LETTER_SHELF = 0x02fd, // U+02FD	MODIFIER LETTER SHELF
  U_MODIFIER_LETTER_OPEN_SHELF = 0x02fe, // U+02FE	MODIFIER LETTER OPEN SHELF
  U_MODIFIER_LETTER_LOW_LEFT_ARROW = 0x02ff, // U+02FF	MODIFIER LETTER LOW LEFT ARROW
  U_GREEK_LOWER_NUMERAL_SIGN = 0x0375, // U+0375	GREEK LOWER NUMERAL SIGN
  U_GREEK_TONOS = 0x0384, // U+0384	GREEK TONOS
  U_GREEK_DIALYTIKA_TONOS = 0x0385, // U+0385	GREEK DIALYTIKA TONOS
  U_GREEK_KORONIS = 0x1fbd, // U+1FBD	GREEK KORONIS
  U_GREEK_PSILI = 0x1fbf, // U+1FBF	GREEK PSILI
  U_GREEK_PERISPOMENI = 0x1fc0, // U+1FC0	GREEK PERISPOMENI
  U_GREEK_DIALYTIKA_AND_PERISPOMENI = 0x1fc1, // U+1FC1	GREEK DIALYTIKA AND PERISPOMENI
  U_GREEK_PSILI_AND_VARIA = 0x1fcd, // U+1FCD	GREEK PSILI AND VARIA
  U_GREEK_PSILI_AND_OXIA = 0x1fce, // U+1FCE	GREEK PSILI AND OXIA
  U_GREEK_PSILI_AND_PERISPOMENI = 0x1fcf, // U+1FCF	GREEK PSILI AND PERISPOMENI
  U_GREEK_DASIA_AND_VARIA = 0x1fdd, // U+1FDD	GREEK DASIA AND VARIA
  U_GREEK_DASIA_AND_OXIA = 0x1fde, // U+1FDE	GREEK DASIA AND OXIA
  U_GREEK_DASIA_AND_PERISPOMENI = 0x1fdf, // U+1FDF	GREEK DASIA AND PERISPOMENI
  U_GREEK_DIALYTIKA_AND_VARIA = 0x1fed, // U+1FED	GREEK DIALYTIKA AND VARIA
  U_GREEK_DIALYTIKA_AND_OXIA = 0x1fee, // U+1FEE	GREEK DIALYTIKA AND OXIA
  U_GREEK_VARIA = 0x1fef, // U+1FEF	GREEK VARIA
  U_GREEK_OXIA = 0x1ffd, // U+1FFD	GREEK OXIA
  U_GREEK_DASIA = 0x1ffe, // U+1FFE	GREEK DASIA

  U_IDEOGRAPHIC_FULL_STOP = 0x3002, // U+3002	IDEOGRAPHIC FULL STOP
  U_LEFT_CORNER_BRACKET = 0x300c, // U+300C	LEFT CORNER BRACKET
  U_RIGHT_CORNER_BRACKET = 0x300d, // U+300D	RIGHT CORNER BRACKET
  U_LEFT_BLACK_LENTICULAR_BRACKET = 0x3010, // U+3010	LEFT BLACK LENTICULAR BRACKET
  U_RIGHT_BLACK_LENTICULAR_BRACKET = 0x3011, // U+3011	RIGHT BLACK LENTICULAR BRACKET

  U_OVERLINE = 0x203e, // Unicode Character 'OVERLINE'

  /**
   * UTF-8 BOM
   * Unicode Character 'ZERO WIDTH NO-BREAK SPACE' (U+FEFF)
   * http://www.fileformat.info/info/unicode/char/feff/index.htm
   */
  UTF8_BOM = 65279,

  U_FULLWIDTH_SEMICOLON = 0xff1b, // U+FF1B	FULLWIDTH SEMICOLON
  U_FULLWIDTH_COMMA = 0xff0c, // U+FF0C	FULLWIDTH COMMA
}

function roundFloat(number: number, decimalPoints: number): number {
  const decimal = Math.pow(10, decimalPoints);
  return Math.round(number * decimal) / decimal;
}

export class RGBA {
  _rgbaBrand: void = undefined;

  /**
   * Red: integer in [0-255]
   */
  readonly r: number;

  /**
   * Green: integer in [0-255]
   */
  readonly g: number;

  /**
   * Blue: integer in [0-255]
   */
  readonly b: number;

  /**
   * Alpha: float in [0-1]
   */
  readonly a: number;

  constructor(r: number, g: number, b: number, a: number = 1) {
    this.r = Math.min(255, Math.max(0, r)) | 0;
    this.g = Math.min(255, Math.max(0, g)) | 0;
    this.b = Math.min(255, Math.max(0, b)) | 0;
    this.a = roundFloat(Math.max(Math.min(1, a), 0), 3);
  }

  static equals(a: RGBA, b: RGBA): boolean {
    return a.r === b.r && a.g === b.g && a.b === b.b && a.a === b.a;
  }
}

export class HSLA {
  _hslaBrand: void = undefined;

  /**
   * Hue: integer in [0, 360]
   */
  readonly h: number;

  /**
   * Saturation: float in [0, 1]
   */
  readonly s: number;

  /**
   * Luminosity: float in [0, 1]
   */
  readonly l: number;

  /**
   * Alpha: float in [0, 1]
   */
  readonly a: number;

  constructor(h: number, s: number, l: number, a: number) {
    this.h = Math.max(Math.min(360, h), 0) | 0;
    this.s = roundFloat(Math.max(Math.min(1, s), 0), 3);
    this.l = roundFloat(Math.max(Math.min(1, l), 0), 3);
    this.a = roundFloat(Math.max(Math.min(1, a), 0), 3);
  }

  static equals(a: HSLA, b: HSLA): boolean {
    return a.h === b.h && a.s === b.s && a.l === b.l && a.a === b.a;
  }

  /**
   * Converts an RGB color value to HSL. Conversion formula
   * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
   * Assumes r, g, and b are contained in the set [0, 255] and
   * returns h in the set [0, 360], s, and l in the set [0, 1].
   */
  static fromRGBA(rgba: RGBA): HSLA {
    const r = rgba.r / 255;
    const g = rgba.g / 255;
    const b = rgba.b / 255;
    const a = rgba.a;

    const max = Math.max(r, g, b);
    const min = Math.min(r, g, b);
    let h = 0;
    let s = 0;
    const l = (min + max) / 2;
    const chroma = max - min;

    if (chroma > 0) {
      s = Math.min(l <= 0.5 ? chroma / (2 * l) : chroma / (2 - 2 * l), 1);

      switch (max) {
        case r:
          h = (g - b) / chroma + (g < b ? 6 : 0);
          break;
        case g:
          h = (b - r) / chroma + 2;
          break;
        case b:
          h = (r - g) / chroma + 4;
          break;
      }

      h *= 60;
      h = Math.round(h);
    }
    return new HSLA(h, s, l, a);
  }

  private static _hue2rgb(p: number, q: number, t: number): number {
    if (t < 0) {
      t += 1;
    }
    if (t > 1) {
      t -= 1;
    }
    if (t < 1 / 6) {
      return p + (q - p) * 6 * t;
    }
    if (t < 1 / 2) {
      return q;
    }
    if (t < 2 / 3) {
      return p + (q - p) * (2 / 3 - t) * 6;
    }
    return p;
  }

  /**
   * Converts an HSL color value to RGB. Conversion formula
   * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
   * Assumes h in the set [0, 360] s, and l are contained in the set [0, 1] and
   * returns r, g, and b in the set [0, 255].
   */
  static toRGBA(hsla: HSLA): RGBA {
    const h = hsla.h / 360;
    const { s, l, a } = hsla;
    let r: number, g: number, b: number;

    if (s === 0) {
      r = g = b = l; // achromatic
    } else {
      const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
      const p = 2 * l - q;
      r = HSLA._hue2rgb(p, q, h + 1 / 3);
      g = HSLA._hue2rgb(p, q, h);
      b = HSLA._hue2rgb(p, q, h - 1 / 3);
    }

    return new RGBA(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255), a);
  }
}

export class HSVA {
  _hsvaBrand: void = undefined;

  /**
   * Hue: integer in [0, 360]
   */
  readonly h: number;

  /**
   * Saturation: float in [0, 1]
   */
  readonly s: number;

  /**
   * Value: float in [0, 1]
   */
  readonly v: number;

  /**
   * Alpha: float in [0, 1]
   */
  readonly a: number;

  constructor(h: number, s: number, v: number, a: number) {
    this.h = Math.max(Math.min(360, h), 0) | 0;
    this.s = roundFloat(Math.max(Math.min(1, s), 0), 3);
    this.v = roundFloat(Math.max(Math.min(1, v), 0), 3);
    this.a = roundFloat(Math.max(Math.min(1, a), 0), 3);
  }

  static equals(a: HSVA, b: HSVA): boolean {
    return a.h === b.h && a.s === b.s && a.v === b.v && a.a === b.a;
  }

  // from http://www.rapidtables.com/convert/color/rgb-to-hsv.htm
  static fromRGBA(rgba: RGBA): HSVA {
    const r = rgba.r / 255;
    const g = rgba.g / 255;
    const b = rgba.b / 255;
    const cmax = Math.max(r, g, b);
    const cmin = Math.min(r, g, b);
    const delta = cmax - cmin;
    const s = cmax === 0 ? 0 : delta / cmax;
    let m: number;

    if (delta === 0) {
      m = 0;
    } else if (cmax === r) {
      m = ((((g - b) / delta) % 6) + 6) % 6;
    } else if (cmax === g) {
      m = (b - r) / delta + 2;
    } else {
      m = (r - g) / delta + 4;
    }

    return new HSVA(Math.round(m * 60), s, cmax, rgba.a);
  }

  // from http://www.rapidtables.com/convert/color/hsv-to-rgb.htm
  static toRGBA(hsva: HSVA): RGBA {
    const { h, s, v, a } = hsva;
    const c = v * s;
    const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
    const m = v - c;
    let [r, g, b] = [0, 0, 0];

    if (h < 60) {
      r = c;
      g = x;
    } else if (h < 120) {
      r = x;
      g = c;
    } else if (h < 180) {
      g = c;
      b = x;
    } else if (h < 240) {
      g = x;
      b = c;
    } else if (h < 300) {
      r = x;
      b = c;
    } else if (h <= 360) {
      r = c;
      b = x;
    }

    r = Math.round((r + m) * 255);
    g = Math.round((g + m) * 255);
    b = Math.round((b + m) * 255);

    return new RGBA(r, g, b, a);
  }
}

export class Color {
  static fromHex(hex: string): Color {
    return Color.Format.CSS.parseHex(hex) || Color.red;
  }

  readonly rgba: RGBA;
  private _hsla?: HSLA;
  get hsla(): HSLA {
    if (this._hsla) {
      return this._hsla;
    } else {
      return HSLA.fromRGBA(this.rgba);
    }
  }

  private _hsva?: HSVA;
  get hsva(): HSVA {
    if (this._hsva) {
      return this._hsva;
    }
    return HSVA.fromRGBA(this.rgba);
  }

  constructor(arg: RGBA | HSLA | HSVA) {
    if (!arg) {
      throw new Error('Color needs a value');
    } else if (arg instanceof RGBA) {
      this.rgba = arg;
    } else if (arg instanceof HSLA) {
      this._hsla = arg;
      this.rgba = HSLA.toRGBA(arg);
    } else if (arg instanceof HSVA) {
      this._hsva = arg;
      this.rgba = HSVA.toRGBA(arg);
    } else {
      throw new Error('Invalid color ctor argument');
    }
  }

  equals(other: Color | null): boolean {
    return (
      !!other &&
      RGBA.equals(this.rgba, other.rgba) &&
      HSLA.equals(this.hsla, other.hsla) &&
      HSVA.equals(this.hsva, other.hsva)
    );
  }

  /**
   * http://www.w3.org/TR/WCAG20/#relativeluminancedef
   * Returns the number in the set [0, 1]. O => Darkest Black. 1 => Lightest white.
   */
  getRelativeLuminance(): number {
    const R = Color._relativeLuminanceForComponent(this.rgba.r);
    const G = Color._relativeLuminanceForComponent(this.rgba.g);
    const B = Color._relativeLuminanceForComponent(this.rgba.b);
    const luminance = 0.2126 * R + 0.7152 * G + 0.0722 * B;

    return roundFloat(luminance, 4);
  }

  private static _relativeLuminanceForComponent(color: number): number {
    const c = color / 255;
    return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
  }

  /**
   * http://www.w3.org/TR/WCAG20/#contrast-ratiodef
   * Returns the contrast ration number in the set [1, 21].
   */
  getContrastRatio(another: Color): number {
    const lum1 = this.getRelativeLuminance();
    const lum2 = another.getRelativeLuminance();
    return lum1 > lum2 ? (lum1 + 0.05) / (lum2 + 0.05) : (lum2 + 0.05) / (lum1 + 0.05);
  }

  /**
   *	http://24ways.org/2010/calculating-color-contrast
   *  Return 'true' if darker color otherwise 'false'
   */
  isDarker(): boolean {
    const yiq = (this.rgba.r * 299 + this.rgba.g * 587 + this.rgba.b * 114) / 1000;
    return yiq < 128;
  }

  /**
   *	http://24ways.org/2010/calculating-color-contrast
   *  Return 'true' if lighter color otherwise 'false'
   */
  isLighter(): boolean {
    const yiq = (this.rgba.r * 299 + this.rgba.g * 587 + this.rgba.b * 114) / 1000;
    return yiq >= 128;
  }

  isLighterThan(another: Color): boolean {
    const lum1 = this.getRelativeLuminance();
    const lum2 = another.getRelativeLuminance();
    return lum1 > lum2;
  }

  isDarkerThan(another: Color): boolean {
    const lum1 = this.getRelativeLuminance();
    const lum2 = another.getRelativeLuminance();
    return lum1 < lum2;
  }

  lighten(factor: number): Color {
    return new Color(new HSLA(this.hsla.h, this.hsla.s, this.hsla.l + this.hsla.l * factor, this.hsla.a));
  }

  darken(factor: number): Color {
    return new Color(new HSLA(this.hsla.h, this.hsla.s, this.hsla.l - this.hsla.l * factor, this.hsla.a));
  }

  transparent(factor: number): Color {
    const { r, g, b, a } = this.rgba;
    return new Color(new RGBA(r, g, b, a * factor));
  }

  isTransparent(): boolean {
    return this.rgba.a === 0;
  }

  isOpaque(): boolean {
    return this.rgba.a === 1;
  }

  opposite(): Color {
    return new Color(new RGBA(255 - this.rgba.r, 255 - this.rgba.g, 255 - this.rgba.b, this.rgba.a));
  }

  blend(c: Color): Color {
    const rgba = c.rgba;

    // Convert to 0..1 opacity
    const thisA = this.rgba.a;
    const colorA = rgba.a;

    const a = thisA + colorA * (1 - thisA);
    if (a < 1e-6) {
      return Color.transparent;
    }

    const r = (this.rgba.r * thisA) / a + (rgba.r * colorA * (1 - thisA)) / a;
    const g = (this.rgba.g * thisA) / a + (rgba.g * colorA * (1 - thisA)) / a;
    const b = (this.rgba.b * thisA) / a + (rgba.b * colorA * (1 - thisA)) / a;

    return new Color(new RGBA(r, g, b, a));
  }

  makeOpaque(opaqueBackground: Color): Color {
    if (this.isOpaque() || opaqueBackground.rgba.a !== 1) {
      // only allow to blend onto a non-opaque color onto a opaque color
      return this;
    }

    const { r, g, b, a } = this.rgba;

    // https://stackoverflow.com/questions/12228548/finding-equivalent-color-with-opacity
    return new Color(
      new RGBA(
        opaqueBackground.rgba.r - a * (opaqueBackground.rgba.r - r),
        opaqueBackground.rgba.g - a * (opaqueBackground.rgba.g - g),
        opaqueBackground.rgba.b - a * (opaqueBackground.rgba.b - b),
        1,
      ),
    );
  }

  flatten(...backgrounds: Color[]): Color {
    const background = backgrounds.reduceRight((accumulator, color) => {
      return Color._flatten(color, accumulator);
    });
    return Color._flatten(this, background);
  }

  private static _flatten(foreground: Color, background: Color) {
    const backgroundAlpha = 1 - foreground.rgba.a;
    return new Color(
      new RGBA(
        backgroundAlpha * background.rgba.r + foreground.rgba.a * foreground.rgba.r,
        backgroundAlpha * background.rgba.g + foreground.rgba.a * foreground.rgba.g,
        backgroundAlpha * background.rgba.b + foreground.rgba.a * foreground.rgba.b,
      ),
    );
  }

  private _toString?: string;
  toString(): string {
    if (!this._toString) {
      this._toString = Color.Format.CSS.format(this);
    }
    return this._toString;
  }

  static getLighterColor(of: Color, relative: Color, factor?: number): Color {
    if (of.isLighterThan(relative)) {
      return of;
    }
    factor = factor ? factor : 0.5;
    const lum1 = of.getRelativeLuminance();
    const lum2 = relative.getRelativeLuminance();
    factor = (factor * (lum2 - lum1)) / lum2;
    return of.lighten(factor);
  }

  static getDarkerColor(of: Color, relative: Color, factor?: number): Color {
    if (of.isDarkerThan(relative)) {
      return of;
    }
    factor = factor ? factor : 0.5;
    const lum1 = of.getRelativeLuminance();
    const lum2 = relative.getRelativeLuminance();
    factor = (factor * (lum1 - lum2)) / lum1;
    return of.darken(factor);
  }

  static readonly white = new Color(new RGBA(255, 255, 255, 1));
  static readonly black = new Color(new RGBA(0, 0, 0, 1));
  static readonly red = new Color(new RGBA(255, 0, 0, 1));
  static readonly blue = new Color(new RGBA(0, 0, 255, 1));
  static readonly green = new Color(new RGBA(0, 255, 0, 1));
  static readonly cyan = new Color(new RGBA(0, 255, 255, 1));
  static readonly lightgrey = new Color(new RGBA(211, 211, 211, 1));
  static readonly transparent = new Color(new RGBA(0, 0, 0, 0));
}

export namespace Color {
  export namespace Format {
    export namespace CSS {
      export function formatRGB(color: Color): string {
        if (color.rgba.a === 1) {
          return `rgb(${color.rgba.r}, ${color.rgba.g}, ${color.rgba.b})`;
        }

        return Color.Format.CSS.formatRGBA(color);
      }

      export function formatRGBA(color: Color): string {
        return `rgba(${color.rgba.r}, ${color.rgba.g}, ${color.rgba.b}, ${+color.rgba.a.toFixed(2)})`;
      }

      export function formatHSL(color: Color): string {
        if (color.hsla.a === 1) {
          return `hsl(${color.hsla.h}, ${(color.hsla.s * 100).toFixed(2)}%, ${(color.hsla.l * 100).toFixed(2)}%)`;
        }

        return Color.Format.CSS.formatHSLA(color);
      }

      export function formatHSLA(color: Color): string {
        return `hsla(${color.hsla.h}, ${(color.hsla.s * 100).toFixed(2)}%, ${(color.hsla.l * 100).toFixed(
          2,
        )}%, ${color.hsla.a.toFixed(2)})`;
      }

      function _toTwoDigitHex(n: number): string {
        const r = n.toString(16);
        return r.length !== 2 ? '0' + r : r;
      }

      /**
       * Formats the color as #RRGGBB
       */
      export function formatHex(color: Color): string {
        return `#${_toTwoDigitHex(color.rgba.r)}${_toTwoDigitHex(color.rgba.g)}${_toTwoDigitHex(color.rgba.b)}`;
      }

      /**
       * Formats the color as #RRGGBBAA
       * If 'compact' is set, colors without transparancy will be printed as #RRGGBB
       */
      export function formatHexA(color: Color, compact = false): string {
        if (compact && color.rgba.a === 1) {
          return Color.Format.CSS.formatHex(color);
        }

        return `#${_toTwoDigitHex(color.rgba.r)}${_toTwoDigitHex(color.rgba.g)}${_toTwoDigitHex(
          color.rgba.b,
        )}${_toTwoDigitHex(Math.round(color.rgba.a * 255))}`;
      }

      /**
       * The default format will use HEX if opaque and RGBA otherwise.
       */
      export function format(color: Color): string {
        if (color.isOpaque()) {
          return Color.Format.CSS.formatHex(color);
        }

        return Color.Format.CSS.formatRGBA(color);
      }

      /**
       * Converts an Hex color value to a Color.
       * returns r, g, and b are contained in the set [0, 255]
       * @param hex string (#RGB, #RGBA, #RRGGBB or #RRGGBBAA).
       */
      export function parseHex(hex: string): Color | null {
        const length = hex.length;

        if (length === 0) {
          // Invalid color
          return null;
        }

        if (hex.charCodeAt(0) !== CharCode.Hash) {
          // Does not begin with a #
          return null;
        }

        if (length === 7) {
          // #RRGGBB format
          const r = 16 * _parseHexDigit(hex.charCodeAt(1)) + _parseHexDigit(hex.charCodeAt(2));
          const g = 16 * _parseHexDigit(hex.charCodeAt(3)) + _parseHexDigit(hex.charCodeAt(4));
          const b = 16 * _parseHexDigit(hex.charCodeAt(5)) + _parseHexDigit(hex.charCodeAt(6));
          return new Color(new RGBA(r, g, b, 1));
        }

        if (length === 9) {
          // #RRGGBBAA format
          const r = 16 * _parseHexDigit(hex.charCodeAt(1)) + _parseHexDigit(hex.charCodeAt(2));
          const g = 16 * _parseHexDigit(hex.charCodeAt(3)) + _parseHexDigit(hex.charCodeAt(4));
          const b = 16 * _parseHexDigit(hex.charCodeAt(5)) + _parseHexDigit(hex.charCodeAt(6));
          const a = 16 * _parseHexDigit(hex.charCodeAt(7)) + _parseHexDigit(hex.charCodeAt(8));
          return new Color(new RGBA(r, g, b, a / 255));
        }

        if (length === 4) {
          // #RGB format
          const r = _parseHexDigit(hex.charCodeAt(1));
          const g = _parseHexDigit(hex.charCodeAt(2));
          const b = _parseHexDigit(hex.charCodeAt(3));
          return new Color(new RGBA(16 * r + r, 16 * g + g, 16 * b + b));
        }

        if (length === 5) {
          // #RGBA format
          const r = _parseHexDigit(hex.charCodeAt(1));
          const g = _parseHexDigit(hex.charCodeAt(2));
          const b = _parseHexDigit(hex.charCodeAt(3));
          const a = _parseHexDigit(hex.charCodeAt(4));
          return new Color(new RGBA(16 * r + r, 16 * g + g, 16 * b + b, (16 * a + a) / 255));
        }

        // Invalid color
        return null;
      }

      function _parseHexDigit(charCode: CharCode): number {
        switch (charCode) {
          case CharCode.Digit0:
            return 0;
          case CharCode.Digit1:
            return 1;
          case CharCode.Digit2:
            return 2;
          case CharCode.Digit3:
            return 3;
          case CharCode.Digit4:
            return 4;
          case CharCode.Digit5:
            return 5;
          case CharCode.Digit6:
            return 6;
          case CharCode.Digit7:
            return 7;
          case CharCode.Digit8:
            return 8;
          case CharCode.Digit9:
            return 9;
          case CharCode.a:
            return 10;
          case CharCode.A:
            return 10;
          case CharCode.b:
            return 11;
          case CharCode.B:
            return 11;
          case CharCode.c:
            return 12;
          case CharCode.C:
            return 12;
          case CharCode.d:
            return 13;
          case CharCode.D:
            return 13;
          case CharCode.e:
            return 14;
          case CharCode.E:
            return 14;
          case CharCode.f:
            return 15;
          case CharCode.F:
            return 15;
        }
        return 0;
      }
    }
  }
}
