psCandy.psm1

[Flags()] enum Styles {
  Normal = 1
  Underline = 2
  Bold = 4
  Reversed = 8
  Strike = 16
  Italic = 32
  Reverse = 64
}

enum Align {
  Left
  Center
  Right
}

class Theme {
  static [void] Init() {
    [Theme]::Load("$PSScriptRoot\theme.json")
  }

  static [void] Load(
    [string]$ThemeName
  ) {
    $theme = Get-Content -Path $ThemeName | ConvertFrom-Json -AsHashtable
    $theme.GetEnumerator() | ForEach-Object {
      $key = $_.Name
      $value = $_.Value
      Set-Item -Path "Env:$key" -Value $value
    }
  }
}

$script:esc = $([char]0x1b)

$BorderTypes = @{
  "Normal"    = @{
    "Top"          = "─"
    "Bottom"       = "─"
    "Left"         = "│"
    "Right"        = "│"
    "TopLeft"      = "┌"
    "TopRight"     = "┐"
    "BottomLeft"   = "└"
    "BottomRight"  = "┘"
    "MiddleLeft"   = "├"
    "MiddleRight"  = "┤"
    "Middle"       = "┼"
    "MiddleTop"    = "┬"
    "MiddleBottom" = "┴"
  }
  "Rounded"   = @{
    "Top"          = "─"
    "Bottom"       = "─"
    "Left"         = "│"
    "Right"        = "│"
    "TopLeft"      = "╭"
    "TopRight"     = "╮"
    "BottomLeft"   = "╰"
    "BottomRight"  = "╯"
    "MiddleLeft"   = "├"
    "MiddleRight"  = "┤"
    "Middle"       = "┼"
    "MiddleTop"    = "┬"
    "MiddleBottom" = "┴"
  }
  "Block"     = @{
    "Top"          = "█"
    "Bottom"       = "█"
    "Left"         = "█"
    "Right"        = "█"
    "TopLeft"      = "█"
    "TopRight"     = "█"
    "BottomLeft"   = "█"
    "BottomRight"  = "█"
    "MiddleLeft"   = "█"
    "MiddleRight"  = "█"
    "Middle"       = "█"
    "MiddleTop"    = "█"
    "MiddleBottom" = "█"
  }
  "OuterHalf" = @{
    "Top"         = "▀"
    "Bottom"      = "▄"
    "Left"        = "▌"
    "Right"       = "▐"
    "TopLeft"     = "▛"
    "TopRight"    = "▜"
    "BottomLeft"  = "▙"
    "BottomRight" = "▟"
  }
  "InnerHalf" = @{
    "Top"         = "▄"
    "Bottom"      = "▀"
    "Left"        = "▐"
    "Right"       = "▌"
    "TopLeft"     = "▗"
    "TopRight"    = "▖"
    "BottomLeft"  = "▝"
    "BottomRight" = "▘"
  }
  "Thick"     = @{
    "Top"          = "━"
    "Bottom"       = "━"
    "Left"         = "┃"
    "Right"        = "┃"
    "TopLeft"      = "┏"
    "TopRight"     = "┓"
    "BottomLeft"   = "┗"
    "BottomRight"  = "┛"
    "MiddleLeft"   = "┣"
    "MiddleRight"  = "┫"
    "Middle"       = "╋"
    "MiddleTop"    = "┳"
    "MiddleBottom" = "┻"
  }
  "Double"    = @{
    "Top"          = "═"
    "Bottom"       = "═"
    "Left"         = "║"
    "Right"        = "║"
    "TopLeft"      = "╔"
    "TopRight"     = "╗"
    "BottomLeft"   = "╚"
    "BottomRight"  = "╝"
    "MiddleLeft"   = "╠"
    "MiddleRight"  = "╣"
    "Middle"       = "╬"
    "MiddleTop"    = "╦"
    "MiddleBottom" = "╩"
  }
  "Hidden"    = @{
    "Top"          = " "
    "Bottom"       = " "
    "Left"         = " "
    "Right"        = " "
    "TopLeft"      = " "
    "TopRight"     = " "
    "BottomLeft"   = " "
    "BottomRight"  = " "
    "MiddleLeft"   = " "
    "MiddleRight"  = " "
    "Middle"       = " "
    "MiddleTop"    = " "
    "MiddleBottom" = " "
  }
}

class candyString {
  static [int] GetDisplayWidth([string] $Str) {
    $width = 0
    $Str = [regex]::Replace($Str, "\e\[[\d;]*m", '')
    $length = $Str.Length

    $emojiRegex = [regex]::new("(\p{Cs}|\p{So}|\p{Sk}|\p{Sc}|\p{Sm})")

    for ($i = 0; $i -lt $length; $i++) {
      $char = $Str[$i]
      $charCode = [int][char]$char

      if ($emojiRegex.IsMatch($char)) {
        $width += 1
      }
      elseif ($charCode -ge 0x1100 -and (
          $charCode -le 0x115F -or # Hangul Jamo init. consonants
          $charCode -eq 0x2329 -or # LEFT-POINTING ANGLE BRACKET
          $charCode -eq 0x232A -or # RIGHT-POINTING ANGLE BRACKET
            ($charCode -ge 0x2E80 -and $charCode -le 0xA4CF -and $charCode -ne 0x303F) -or # CJK ... Yi
            ($charCode -ge 0xAC00 -and $charCode -le 0xD7A3) -or # Hangul Syllables
            ($charCode -ge 0xF900 -and $charCode -le 0xFAFF) -or # CJK Compatibility Ideographs
            ($charCode -ge 0xFE10 -and $charCode -le 0xFE19) -or # Vertical forms
            ($charCode -ge 0xFE30 -and $charCode -le 0xFE6F) -or # CJK Compatibility Forms
            ($charCode -ge 0xFF00 -and $charCode -le 0xFF60) -or # Fullwidth Forms
            ($charCode -ge 0xFFE0 -and $charCode -le 0xFFE6) -or # Halfwidth and Fullwidth Forms
            ($charCode -ge 0x1F300 -and $charCode -le 0x1F64F) -or # Emoticons
            ($charCode -ge 0x1F680 -and $charCode -le 0x1F6FF) -or # Transport and Map Symbols
            ($charCode -ge 0x1F700 -and $charCode -le 0x1F77F) -or # Alchemical Symbols
            ($charCode -ge 0x1F780 -and $charCode -le 0x1F7FF) -or # Geometric Shapes Extended
            ($charCode -ge 0x1F800 -and $charCode -le 0x1F8FF) -or # Supplemental Arrows-C
            ($charCode -ge 0x1F900 -and $charCode -le 0x1F9FF) -or # Supplemental Symbols and Pictographs
            ($charCode -ge 0x1FA00 -and $charCode -le 0x1FA6F) -or # Chess Symbols
            ($charCode -ge 0x1FA70 -and $charCode -le 0x1FAFF) -or # Symbols and Pictographs Extended-A
            ($charCode -ge 0x20000 -and $charCode -le 0x2FFFD) -or # CJK Unified Ideographs Extension B-D
            ($charCode -ge 0x30000 -and $charCode -le 0x3FFFD)     # CJK Unified Ideographs Extension E-F
        )) {
        $width += 2
      }
      else {
        $width += 1
      }
    }
    return $width
  }

  static [int] GetStringFullLength ([string]$InputString) {
    $length = 0
    foreach ($char in $InputString.ToCharArray()) {
        [int]$utf16CodeUnit = [System.BitConverter]::GetBytes($char)[0]
        if ($utf16CodeUnit -lt 0) {
            $length += 2
        } else {
            $length++
        }
    }
    return $length
  }

  static [int] GetDisplayLength([string] $Str) {
    $Str = [regex]::Replace($Str, "\e\[[\d;]*m", '')
    return $str.Length
  }

  static [string] TruncateString(
    [string]$InputString,
    [int]$MaxWidth
  ) {
    $ellipsis = "."
    $ellipsisWidth = 1  # Precomputed width of the ellipsis

    $currentWidth = [candyString]::GetDisplayWidth($InputString)
    if ($currentWidth -le $MaxWidth) {
      return $InputString
    }

    $truncatedString = New-Object System.Text.StringBuilder
    $currentWidth = 0

    foreach ($char in $InputString.ToCharArray()) {
      $charWidth = if ([System.Text.Encoding]::UTF8.GetByteCount($char) -gt 1) { 2 } else { 1 }
        
      if ($currentWidth + $charWidth + $ellipsisWidth -gt $MaxWidth) {
        break
      }

      $truncatedString.Append($char) | Out-Null
      $currentWidth += $charWidth
    }

    return $truncatedString.ToString() + $ellipsis
  }

  static [String] PadString (
    [string]$InputString,
    [int]$TotalWidth,
    [string]$PadCharacter = " ",
    [Align]$PadDirection = [Align]::Left # Options: Left, Right, Both
  ) {
    $InputString = [candystring]::TruncateString($InputString, $TotalWidth)
    $padLength = $TotalWidth 

    if ($padLength -le 0) {
      return $InputString
    }

    
    switch ($PadDirection) {
      Right {
        $displayWidth = [candyString]::GetDisplayWidth($InputString)
        $diff = $TotalWidth - $displayWidth
        if ($diff -gt 0) {
          $InputString = ($PadCharacter * $diff) + $InputString
        }
      }
      Left {
        $displayWidth = [candyString]::GetDisplayWidth($InputString)
        $diff = $TotalWidth - $displayWidth
        if ($diff -gt 0) {
          $InputString = $InputString + ($PadCharacter * $diff) 
        }
      }
      Center {
        # $leftPad = $rightPad = $PadCharacter
        $width = [candyString]::GetDisplayWidth($InputString)
        $left = [math]::floor(($TotalWidth - $width) / 2)
        $right = $TotalWidth - $width - $left
        $leftPad = $PadCharacter * $left
        $rightPad = $PadCharacter * $right
        $InputString = $leftPad + $InputString + $rightPad
      }
      default {
        throw "Invalid PadDirection specified. Use 'Left', 'Right', or 'Both'."
      }
    }
    return $InputString
  }
}

class candyColor {
  [int]$r
  [int]$g
  [int]$b

  static [candyColor] tocolor([string]$color) {
    $methodInfo = [colors].GetMethod($color, [System.Reflection.BindingFlags]::Static -bor [System.Reflection.BindingFlags]::Public)
    if ($null -eq $methodInfo) {
      return [colors]::White()
    }
    return $methodinfo.Invoke($null, $null)
  }

  static [string] colorList() {
    $result = @()
    [Colors] | Get-Member -Static | Where-Object { $_.Definition -match 'candyColor' } | ForEach-Object { 
      $result += $_.Name
    }
    return $result -join "|"
  }
}

class Colors {
  static [candyColor] AliceBlue () {
    return @{R = 240
      G        = 248
      B        = 255
    }
  }
  static [candyColor] AntiqueWhite () {
    return @{R = 250
      G        = 235
      B        = 215
    }
  }
  static [candyColor] Aqua () {
    return @{R = 0
      G        = 255
      B        = 255
    }
  }
  static [candyColor] Aquamarine () {
    return @{R = 127
      G        = 255
      B        = 212
    }
  }
  static [candyColor] Azure () {
    return @{R = 240
      G        = 255
      B        = 255
    }
  }
  static [candyColor] Beige () {
    return @{R = 245
      G        = 245
      B        = 220
    }
  }
  static [candyColor] Bisque () {
    return @{R = 255
      G        = 228
      B        = 196
    }
  }
  static [candyColor] Black () {
    return @{R = 0
      G        = 0
      B        = 0
    }
  }
  static [candyColor] BlanchedAlmond () {
    return @{R = 255
      G        = 235
      B        = 205
    }
  }
  static [candyColor] Blue () {
    return @{R = 0
      G        = 0
      B        = 255
    }
  }
  static [candyColor] BlueViolet () {
    return @{R = 138
      G        = 43
      B        = 226
    }
  }
  static [candyColor] Brown () {
    return @{R = 165
      G        = 42
      B        = 42
    }
  }
  static [candyColor] BurlyWood () {
    return @{R = 222
      G        = 184
      B        = 135
    }
  }
  static [candyColor] CadetBlue () {
    return @{R = 95
      G        = 158
      B        = 160
    }
  }
  static [candyColor] Chartreuse () {
    return @{R = 127
      G        = 255
      B        = 0
    }
  }
  static [candyColor] Chocolate () {
    return @{R = 210
      G        = 105
      B        = 30
    }
  }
  static [candyColor] Coral () {
    return @{R = 255
      G        = 127
      B        = 80
    }
  }
  static [candyColor] CornflowerBlue () {
    return @{R = 100
      G        = 149
      B        = 237
    }
  }
  static [candyColor] Cornsilk () {
    return @{R = 255
      G        = 248
      B        = 220
    }
  }
  static [candyColor] Crimson () {
    return @{R = 220
      G        = 20
      B        = 60
    }
  }
  static [candyColor] Cyan () {
    return @{R = 0
      G        = 255
      B        = 255
    }
  }
  static [candyColor] DarkBlue () {
    return @{R = 0
      G        = 0
      B        = 139
    }
  }
  static [candyColor] DarkCyan () {
    return @{R = 0
      G        = 139
      B        = 139
    }
  }
  static [candyColor] DarkGoldenrod () {
    return @{R = 184
      G        = 134
      B        = 11
    }
  }
  static [candyColor] DarkGray () {
    return @{R = 169
      G        = 169
      B        = 169
    }
  }
  static [candyColor] DarkGreen () {
    return @{R = 0
      G        = 100
      B        = 0
    }
  }
  static [candyColor] DarkKhaki () {
    return @{R = 189
      G        = 183
      B        = 107
    }
  }
  static [candyColor] DarkMagenta () {
    return @{R = 139
      G        = 0
      B        = 139
    }
  }
  static [candyColor] DarkOliveGreen () {
    return @{R = 85
      G        = 107
      B        = 47
    }
  }
  static [candyColor] DarkOrange () {
    return @{R = 255
      G        = 140
      B        = 0
    }
  }
  static [candyColor] DarkOrchid () {
    return @{R = 153
      G        = 50
      B        = 204
    }
  }
  static [candyColor] DarkRed () {
    return @{R = 139
      G        = 0
      B        = 0
    }
  }
  static [candyColor] DarkSalmon () {
    return @{R = 233
      G        = 150
      B        = 122
    }
  }
  static [candyColor] DarkSeaGreen () {
    return @{R = 143
      G        = 188
      B        = 143
    }
  }
  static [candyColor] DarkSlateBlue () {
    return @{R = 72
      G        = 61
      B        = 139
    }
  }
  static [candyColor] DarkSlateGray () {
    return @{R = 47
      G        = 79
      B        = 79
    }
  }
  static [candyColor] DarkTurquoise () {
    return @{R = 0
      G        = 206
      B        = 209
    }
  }
  static [candyColor] DarkViolet () {
    return @{R = 148
      G        = 0
      B        = 211
    }
  }
  static [candyColor] DeepPink () {
    return @{R = 255
      G        = 20
      B        = 147
    }
  }
  static [candyColor] DeepSkyBlue () {
    return @{R = 0
      G        = 191
      B        = 255
    }
  }
  static [candyColor] DimGray () {
    return @{R = 105
      G        = 105
      B        = 105
    }
  }
  static [candyColor] DodgerBlue () {
    return @{R = 30
      G        = 144
      B        = 255
    }
  }
  static [candyColor] Empty () {
    return @{R = 0
      G        = 0
      B        = 0
    }
  }
  static [candyColor] Firebrick () {
    return @{R = 178
      G        = 34
      B        = 34
    }
  }
  static [candyColor] FloralWhite () {
    return @{R = 255
      G        = 250
      B        = 240
    }
  }
  static [candyColor] ForestGreen () {
    return @{R = 34
      G        = 139
      B        = 34
    }
  }
  static [candyColor] Fuchsia () {
    return @{R = 255
      G        = 0
      B        = 255
    }
  }
  static [candyColor] Gainsboro () {
    return @{R = 220
      G        = 220
      B        = 220
    }
  }
  static [candyColor] GhostWhite () {
    return @{R = 248
      G        = 248
      B        = 255
    }
  }
  static [candyColor] Gold () {
    return @{R = 255
      G        = 215
      B        = 0
    }
  }
  static [candyColor] Goldenrod () {
    return @{R = 218
      G        = 165
      B        = 32
    }
  }
  static [candyColor] Gray () {
    return @{R = 128
      G        = 128
      B        = 128
    }
  }
  static [candyColor] Green () {
    return @{R = 0
      G        = 128
      B        = 0
    }
  }
  static [candyColor] GreenYellow () {
    return @{R = 173
      G        = 255
      B        = 47
    }
  }
  static [candyColor] Honeydew () {
    return @{R = 240
      G        = 255
      B        = 240
    }
  }
  static [candyColor] HotPink () {
    return @{R = 255
      G        = 105
      B        = 180
    }
  }
  static [candyColor] IndianRed () {
    return @{R = 205
      G        = 92
      B        = 92
    }
  }
  static [candyColor] Indigo () {
    return @{R = 75
      G        = 0
      B        = 130
    }
  }
  static [candyColor] Ivory () {
    return @{R = 255
      G        = 255
      B        = 240
    }
  }
  static [candyColor] Khaki () {
    return @{R = 240
      G        = 230
      B        = 140
    }
  }
  static [candyColor] Lavender () {
    return @{R = 230
      G        = 230
      B        = 250
    }
  }
  static [candyColor] LavenderBlush () {
    return @{R = 255
      G        = 240
      B        = 245
    }
  }
  static [candyColor] LawnGreen () {
    return @{R = 124
      G        = 252
      B        = 0
    }
  }
  static [candyColor] LemonChiffon () {
    return @{R = 255
      G        = 250
      B        = 205
    }
  }
  static [candyColor] LightBlue () {
    return @{R = 173
      G        = 216
      B        = 230
    }
  }
  static [candyColor] LightCoral () {
    return @{R = 240
      G        = 128
      B        = 128
    }
  }
  static [candyColor] LightCyan () {
    return @{R = 224
      G        = 255
      B        = 255
    }
  }
  static [candyColor] LightGoldenrodYellow () {
    return @{R = 250
      G        = 250
      B        = 210
    }
  }
  static [candyColor] LightGray () {
    return @{R = 211
      G        = 211
      B        = 211
    }
  }
  static [candyColor] LightGreen () {
    return @{R = 144
      G        = 238
      B        = 144
    }
  }
  static [candyColor] LightPink () {
    return @{R = 255
      G        = 182
      B        = 193
    }
  }
  static [candyColor] LightSalmon () {
    return @{R = 255
      G        = 160
      B        = 122
    }
  }
  static [candyColor] LightSeaGreen () {
    return @{R = 32
      G        = 178
      B        = 170
    }
  }
  static [candyColor] LightSkyBlue () {
    return @{R = 135
      G        = 206
      B        = 250
    }
  }
  static [candyColor] LightSlateGray () {
    return @{R = 119
      G        = 136
      B        = 153
    }
  }
  static [candyColor] LightSteelBlue () {
    return @{R = 176
      G        = 196
      B        = 222
    }
  }
  static [candyColor] LightYellow () {
    return @{R = 255
      G        = 255
      B        = 224
    }
  }
  static [candyColor] Lime () {
    return @{R = 0
      G        = 255
      B        = 0
    }
  }
  static [candyColor] LimeGreen () {
    return @{R = 50
      G        = 205
      B        = 50
    }
  }
  static [candyColor] Linen () {
    return @{R = 250
      G        = 240
      B        = 230
    }
  }
  static [candyColor] Magenta () {
    return @{R = 255
      G        = 0
      B        = 255
    }
  }
  static [candyColor] Maroon () {
    return @{R = 128
      G        = 0
      B        = 0
    }
  }
  static [candyColor] MediumAquamarine () {
    return @{R = 102
      G        = 205
      B        = 170
    }
  }
  static [candyColor] MediumBlue () {
    return @{R = 0
      G        = 0
      B        = 205
    }
  }
  static [candyColor] MediumOrchid () {
    return @{R = 186
      G        = 85
      B        = 211
    }
  }
  static [candyColor] MediumPurple () {
    return @{R = 147
      G        = 112
      B        = 219
    }
  }
  static [candyColor] MediumSeaGreen () {
    return @{R = 60
      G        = 179
      B        = 113
    }
  }
  static [candyColor] MediumSlateBlue () {
    return @{R = 123
      G        = 104
      B        = 238
    }
  }
  static [candyColor] MediumSpringGreen () {
    return @{R = 0
      G        = 250
      B        = 154
    }
  }
  static [candyColor] MediumTurquoise () {
    return @{R = 72
      G        = 209
      B        = 204
    }
  }
  static [candyColor] MediumVioletRed () {
    return @{R = 199
      G        = 21
      B        = 133
    }
  }
  static [candyColor] MidnightBlue () {
    return @{R = 25
      G        = 25
      B        = 112
    }
  }
  static [candyColor] MintCream () {
    return @{R = 245
      G        = 255
      B        = 250
    }
  }
  static [candyColor] MistyRose () {
    return @{R = 255
      G        = 228
      B        = 225
    }
  }
  static [candyColor] Moccasin () {
    return @{R = 255
      G        = 228
      B        = 181
    }
  }
  static [candyColor] NavajoWhite () {
    return @{R = 255
      G        = 222
      B        = 173
    }
  }
  static [candyColor] Navy () {
    return @{R = 0
      G        = 0
      B        = 128
    }
  }
  static [candyColor] OldLace () {
    return @{R = 253
      G        = 245
      B        = 230
    }
  }
  static [candyColor] Olive () {
    return @{R = 128
      G        = 128
      B        = 0
    }
  }
  static [candyColor] OliveDrab () {
    return @{R = 107
      G        = 142
      B        = 35
    }
  }
  static [candyColor] Orange () {
    return @{R = 255
      G        = 165
      B        = 0
    }
  }
  static [candyColor] OrangeRed () {
    return @{R = 255
      G        = 69
      B        = 0
    }
  }
  static [candyColor] Orchid () {
    return @{R = 218
      G        = 112
      B        = 214
    }
  }
  static [candyColor] PaleGoldenrod () {
    return @{R = 238
      G        = 232
      B        = 170
    }
  }
  static [candyColor] PaleGreen () {
    return @{R = 152
      G        = 251
      B        = 152
    }
  }
  static [candyColor] PaleTurquoise () {
    return @{R = 175
      G        = 238
      B        = 238
    }
  }
  static [candyColor] PaleVioletRed () {
    return @{R = 219
      G        = 112
      B        = 147
    }
  }
  static [candyColor] PapayaWhip () {
    return @{R = 255
      G        = 239
      B        = 213
    }
  }
  static [candyColor] PeachPuff () {
    return @{R = 255
      G        = 218
      B        = 185
    }
  }
  static [candyColor] Peru () {
    return @{R = 205
      G        = 133
      B        = 63
    }
  }
  static [candyColor] Pink () {
    return @{R = 255
      G        = 192
      B        = 203
    }
  }
  static [candyColor] Plum () {
    return @{R = 221
      G        = 160
      B        = 221
    }
  }
  static [candyColor] PowderBlue () {
    return @{R = 176
      G        = 224
      B        = 230
    }
  }
  static [candyColor] Purple () {
    return @{R = 128
      G        = 0
      B        = 128
    }
  }
  static [candyColor] RebeccaPurple () {
    return @{R = 102
      G        = 51
      B        = 153
    }
  }
  static [candyColor] Red () {
    return @{R = 255
      G        = 0
      B        = 0
    }
  }
  static [candyColor] RosyBrown () {
    return @{R = 188
      G        = 143
      B        = 143
    }
  }
  static [candyColor] RoyalBlue () {
    return @{R = 65
      G        = 105
      B        = 225
    }
  }
  static [candyColor] SaddleBrown () {
    return @{R = 139
      G        = 69
      B        = 19
    }
  }
  static [candyColor] Salmon () {
    return @{R = 250
      G        = 128
      B        = 114
    }
  }
  static [candyColor] SandyBrown () {
    return @{R = 244
      G        = 164
      B        = 96
    }
  }
  static [candyColor] SeaGreen () {
    return @{R = 46
      G        = 139
      B        = 87
    }
  }
  static [candyColor] SeaShell () {
    return @{R = 255
      G        = 245
      B        = 238
    }
  }
  static [candyColor] Sienna () {
    return @{R = 160
      G        = 82
      B        = 45
    }
  }
  static [candyColor] Silver () {
    return @{R = 192
      G        = 192
      B        = 192
    }
  }
  static [candyColor] SkyBlue () {
    return @{R = 135
      G        = 206
      B        = 235
    }
  }
  static [candyColor] SlateBlue () {
    return @{R = 106
      G        = 90
      B        = 205
    }
  }
  static [candyColor] SlateGray () {
    return @{R = 112
      G        = 128
      B        = 144
    }
  }
  static [candyColor] Snow () {
    return @{R = 255
      G        = 250
      B        = 250
    }
  }
  static [candyColor] SpringGreen () {
    return @{R = 0
      G        = 255
      B        = 127
    }
  }
  static [candyColor] SteelBlue () {
    return @{R = 70
      G        = 130
      B        = 180
    }
  }
  static [candyColor] Tan () {
    return @{R = 210
      G        = 180
      B        = 140
    }
  }
  static [candyColor] Teal () {
    return @{R = 0
      G        = 128
      B        = 128
    }
  }
  static [candyColor] Thistle () {
    return @{R = 216
      G        = 191
      B        = 216
    }
  }
  static [candyColor] Tomato () {
    return @{R = 255
      G        = 99
      B        = 71
    }
  }
  static [candyColor] Transparent () {
    return @{R = 255
      G        = 255
      B        = 255
    }
  }
  static [candyColor] Turquoise () {
    return @{R = 64
      G        = 224
      B        = 208
    }
  }
  static [candyColor] Violet () {
    return @{R = 238
      G        = 130
      B        = 238
    }
  }
  static [candyColor] Wheat () {
    return @{R = 245
      G        = 222
      B        = 179
    }
  }
  static [candyColor] White () {
    return @{R = 255
      G        = 255
      B        = 255
    }
  }
  static [candyColor] WhiteSmoke () {
    return @{R = 245
      G        = 245
      B        = 245
    }
  }
  static [candyColor] Yellow () {
    return @{R = 255
      G        = 255
      B        = 0
    }
  }
  static [candyColor] YellowGreen () {
    return @{R = 154
      G        = 205
      B        = 50
    }
  }

}
class _color {
  
}

class Color {
  [candyColor]$Foreground = $null
  [candyColor]$Background = $null
  [Styles]$style
  static [string] Pick8() {
    [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
    [console]::CursorVisible = $false
    [console]::Clear()
    Write-Candy -Text "Pick a color <Green>(8bits)</Green>" -fullscreen -Border "Rounded" -Align Center
    $y = $global:host.UI.RawUI.CursorPosition.Y
    $Cell = ">000<" # Lenght 5

    $w = $global:host.UI.RawUI.WindowSize.Width - 2
    $CellsperRows = [math]::Floor($w / ($Cell.Length))
    $index = 0
    $back = -1
    $result = -1
    while ($true) {
      [console]::SetCursorPosition(0, $y)
      $Color = 0
      $buffer = ""
      while ($Color -lt 256) {
        $clsString = $color.ToString().PadLeft(3, " ")
        if ($index -eq $color) {
          $clsString = "<R>" + $clsString + "</R>"
        }
    
        $clsString = " " + $clsString + " "
        $buffer += [Color]::Applycolor16($clsString, $color, $back)
        if (($color + 1) % $CellsperRows -eq 0) {
          $buffer += "`n"
        }
        $Color++
      }
      Write-Candy $buffer
      if ($back -ne -1) {
        Write-Candy -Text "Color is <$($index)>[$($back)]$index[/$($back)]</$($index)> " -fullscreen -Border "Rounded"
      }
      else {
        Write-Candy -Text "Color is <$($index)>$index</$($index)> " -fullscreen -Border "Rounded"
      }
  
      if ($global:Host.UI.RawUI.KeyAvailable) {
        [System.Management.Automation.Host.KeyInfo]$key = $($global:host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown'))
        if ($key.VirtualKeyCode -eq 39) {
          $index++
          if ($index -gt 255) {
            $index = 0
          }
        }
        if ($key.VirtualKeyCode -eq 37) {
          $index--
          if ($index -lt 0) {
            $index = 255
          }
        } 
        if ($key.VirtualKeyCode -eq 40) {
          $index += $CellsperRows
          if ($index -gt 255) {
            $index = 0
          }
        }
        if ($key.VirtualKeyCode -eq 38) {
          $index -= $CellsperRows
          if ($index -lt 0) {
            $index = 255
          }
        } 
        if ($key.VirtualKeyCode -eq 66) {
          if ($back -eq -1) {
            $back = $index
          }
          else {
            $back = -1
          }
      
        }
    
        if ($key.VirtualKeyCode -eq 13) {
          $result = $index
          break
        }
        if ($key.VirtualKeyCode -eq 27) {
          break
        }
        if ($key.VirtualKeyCode -eq 32) {
          $index = 0
        }
      }
    }
    [console]::CursorVisible = $true
    return $result
  }
  static [String] Pick () {
    [Console]::Clear()
    Write-Candy "🎨 <Green>Pick</Green> <Blue>A</Blue> <Red>Color</Red>" -Width ($global:Host.UI.RawUI.BufferSize.Width - 2) -Border "Rounded" -Align Center
    [System.Console]::SetCursorPosition(0, 3)
    $items = [System.Collections.Generic.List[ListItem]]::new()
    
    $colors = [candyColor]::colorList() -split "\|"
    $colors | ForEach-Object {
      $colorName = "<$($_)>$($_)</$($_)>"
      $items.Add([ListItem]::new($colorName, $_))
    }
    $List = [List]::new($items)
    $List.SetLimit($true)
    $list.SetHeight(($global:Host.UI.RawUI.BufferSize.Height - 9))
    $c = $List.Display()
    if ($c) {
      return $c.Value
    }
    return [String]::Empty
  }


  static [string] color16 (
    [string]$Text,
    [int]$ForegroundColor = -1,
    [int]$BackgroundColor = -1,
    [switch]$Underline,
    [switch]$Strike
  ) {
    
  
    $fore = ""
    $back = ""
    $Under = ""
    $Stri = ""
    if ($ForegroundColor -ne -1) {
      $fore = "$script:esc[38;5;$($ForegroundColor)m"
    }
    if ( $BackgroundColor -ne -1 ) {
      $back = "$script:esc[48;5;$($BackgroundColor)m"
    }
    if ($Underline) {
      $under = "$script:esc[4m"
    }
    if ($Strike) {
      $stri = "$script:esc[9m"
    }
    $close = "$script:esc[0m"
    $result = "$under$stri$fore$back$Text$close"
    return $result
  }

  

  static [string] Applycolor16 (
    [string]$Text,
    [int]$ForegroundColor = -1,
    [int]$BackgroundColor = -1
  ) {
    
    $fore = ""
    $back = ""
    $close = ""
    if ($ForegroundColor -ne -1) {
      $fore = "$script:esc[38;5;$($ForegroundColor)m"
      $close = "$script:esc[39m"
    }
    if ( $BackgroundColor -ne -1 ) {
      $back = "$script:esc[48;5;$($BackgroundColor)m"
      $close = "$script:esc[49m"
    }
    
    $result = "$fore$back$Text$close"
    return $result
  }

  static [string]ApplyColorRGB(
    [string]$text,
    [string]$foregroundColor = "",
    [string]$backgroundColor = ""
  ) {
    $result = ""
    
    $Fore = ""
    $Back = ""
    $close = ""
    if ("" -ne $ForegroundColor) {
      $col = [candycolor]::tocolor($foregroundcolor)
      $fore = "$script:esc[38;2;$($col.R);$($col.G);$($col.B)m"
      $close = "$script:esc[39m"
    }
    
    if ("" -ne $BackgroundColor) {
      $col = [candycolor]::tocolor($backgroundcolor) 
      $back = "$script:esc[48;2;$($col.R);$($col.G);$($col.B)m"
      $close = "$script:esc[49m"
    }
    
    $result = "$fore$back$Text$close"
    return $result
  }

  static [string] colorRGB (
    [string]$Text,
    [candyColor]$Foreground,
    [candyColor]$Background,
    [switch]$Underline,
    [switch]$Strike
  ) {
    
    $Fore = ""
    $Back = ""
    $Under = ""
    $Stri = ""
    
    if ($null -ne $Foreground) {
      $fore = "$script:esc[38;2;$($Foreground.R);$($Foreground.G);$($Foreground.B)m"
    }
    if ($null -ne $Background) {
      $back = "$script:esc[48;2;$($Background.R);$($Background.G);$($Background.B)m"
    }
    if ($Underline) {
      $under = "$script:esc[4m"
    }
    if ($Strike) {
      $stri = "$script:esc[9m"
    }
    $close = "$script:esc[0m"
    $result = "$under$stri$fore$back$Text$close"
    return $result
  }

  color (
    [candyColor]$Foreground,
    [candyColor]$Background = $null
  ) {
    $this.Foreground = $Foreground
    $this.Background = $Background
  }

  color (
    [candyColor]$Foreground
  ) {
    $this.Foreground = $Foreground
  }   
  
  [string]ApplyColor(
    [string]$text
  ) {
    $result = ""
    
    $Fore = ""
    $Back = ""
    $sty = ""
    $close = ""
    if ($null -ne $this.Foreground) {
      $fore = "$script:esc[38;2;$($this.Foreground.R);$($this.Foreground.G);$($this.Foreground.B)m"
      $close = "$script:esc[39m"
    }
    
    if ($null -ne $this.Background) {
      $back = "$script:esc[48;2;$($this.Background.R);$($this.Background.G);$($this.Background.B)m"
      $close = "$script:esc[49m"
    }
    if ( ($this.style -band [Styles]::Underline) -eq [Styles]::Underline ) {
      $sty = [string]::concat($sty, "$script:esc[4m")
    }
    
    if (($this.style -band [styles]::Strike) -eq [Styles]::Strike) {
      $sty = [string]::concat($sty, "$script:esc[9m")
    }

    if (($this.style -band [styles]::Italic) -eq [Styles]::Italic) {
      $sty = [string]::concat($sty, "$script:esc[3m")
    }

    if (($this.style -band [styles]::Bold) -eq [Styles]::Bold) {
      $sty = [string]::concat($sty, "$script:esc[1m")
    }

    
    $result = "$sty$fore$back$Text$close"
    return $result
  }

  [string]ApplyStyle(
    [string]$text
  ) {
    $result = ""
    
    $sty = ""
    $endsty = ""
    if ( ($this.style -band [Styles]::Underline) -eq [Styles]::Underline ) {
      $sty = [string]::concat($sty, "$script:esc[4m")
      $endsty = [string]::concat($sty, "$script:esc[24m")
    }
    
    
    if (($this.style -band [styles]::Strike) -eq [Styles]::Strike) {
      $sty = [string]::concat($sty, "$script:esc[9m")
      $endsty = [string]::concat($sty, "$script:esc[24m")
    }
    

    if (($this.style -band [styles]::Italic) -eq [Styles]::Italic) {
      $sty = [string]::concat($sty, "$script:esc[3m")
      $endsty = [string]::concat($sty, "$script:esc[24m")
    }
    

    if (($this.style -band [styles]::Bold) -eq [Styles]::Bold) {
      $sty = [string]::concat($sty, "$script:esc[1m")
      $endsty = [string]::concat($sty, "$script:esc[24m")
    }
    
    $result = "$sty$Text$endsty"
    return $result
  }

  static [string]endStyle(
    [string]$text) {
    $result = ""
    $close = "$script:esc[0m"
    $result = "$Text$close"
    return $result
  }
  
  [string]render (
    [string]$text
  ) {
    
    $Fore = ""
    $Back = ""
    $sty = ""
    
    if ($null -ne $this.Foreground) {
      $fore = "$script:esc[38;2;$($this.Foreground.R);$($this.Foreground.G);$($this.Foreground.B)m"
    }
    
    if ($null -ne $this.Background) {
      $back = "$script:esc[48;2;$($this.Background.R);$($this.Background.G);$($this.Background.B)m"
    }
    if ( ($this.style -band [Styles]::Underline) -eq [Styles]::Underline ) {
      $sty = [string]::concat($sty, "$script:esc[4m")
    }
    
    if (($this.style -band [styles]::Strike) -eq [Styles]::Strike) {
      $sty = [string]::concat($sty, "$script:esc[9m")
    }

    if (($this.style -band [styles]::Italic) -eq [Styles]::Italic) {
      $sty = [string]::concat($sty, "$script:esc[3m")
    }

    if (($this.style -band [styles]::Bold) -eq [Styles]::Bold) {
      $sty = [string]::concat($sty, "$script:esc[1m")
    }

    $close = "$script:esc[0m"
    $result = "$sty$fore$back$Text$close"
    return $result
  }

  [string]render (
    [string]$text,
    [Styles]$style
  ) {
    $oldStyle = $this.style
    $this.style = $style

    $result = $this.render($text)

    $this.style = $oldStyle
    return $result
  }
}


class Option {
  [String]$text
  [PSCustomObject]$value
  [bool]$selected = $false

  Option(
    [String]$text,
    [PSCustomObject]$value
  ) {
    $this.text = $text
    $this.value = $value
  }
  Option(
    [String]$text,
    [PSCustomObject]$value,
    [bool]$selected
  ) {
    $this.text = $text
    $this.value = $value
    $this.selected = $selected
  }
}
class Border {
  static [hashtable] GetBorder(
    [string]$type = "Normal"
  ) {
    return $script:BorderTypes[$type]
  }

  static [string[]] GetBorderTypes() {
    $result = @()
    $script:BorderTypes.Keys | ForEach-Object {
      $result += $_
    }
    return $result
  }
}

class Spinner {
  [hashtable]$Spinner
  [System.Collections.Hashtable]$statedata
  $runspace
  [powershell]$session
  [Int32]$X = $Host.UI.RawUI.CursorPosition.X
  [Int32]$Y = $Host.UI.RawUI.CursorPosition.Y
  [bool]$running = $false
  [Int32]$width = $Host.UI.RawUI.BufferSize.Width
  [candyColor]$SpinColor = [Colors]::MediumOrchid()
  [bool]$CuirsorState = [System.Console]::CursorVisible

  $Spinners = @{
    "Circle" = @{
      "Frames" = @("◜", "◠", "◝", "◞", "◡", "◟")
      "Sleep"  = 50
    }
    "Dots"   = @{
      "Frames" = @("⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷", "⣿")
      "Sleep"  = 50
    }
    "Line"   = @{
      "Frames" = @("▰▱▱▱▱▱▱", "▰▰▱▱▱▱▱", "▰▰▰▱▱▱▱", "▰▰▰▰▱▱▱", "▰▰▰▰▰▱▱", "▰▰▰▰▰▰▱", "▰▰▰▰▰▰▰", "▰▱▱▱▱▱▱")
      "Sleep"  = 50
    }
    "Square" = @{
      "Frames" = @("⣾⣿", "⣽⣿", "⣻⣿", "⢿⣿", "⡿⣿", "⣟⣿", "⣯⣿", "⣷⣿", "⣿⣾", "⣿⣽", "⣿⣻", "⣿⢿", "⣿⡿", "⣿⣟", "⣿⣯", "⣿⣷")
      "Sleep"  = 50
    }
    "Bubble" = @{
      "Frames" = @("......", "o.....", "Oo....", "oOo...", ".oOo..", "..oOo.", "...oOo", "....oO", ".....o", "....oO", "...oOo", "..oOo.", ".oOo..", "oOo...", "Oo....", "o.....", "......")
      "Sleep"  = 50
    }
    "Arrow"  = @{
      "Frames" = @("≻ ", " ≻ ", " ≻ ", " ≻ ", " ≻", " ≺", " ≺ ", " ≺ ", " ≺ ", "≺ ")
      "Sleep"  = 50
    }
    "Pulse"  = @{
      "Frames" = @("◾", "◾", "◼️", "◼️", "⬛", "⬛", "◼️", "◼️")
      "Sleep"  = 50
    }
  }

  Spinner(
    [string]$type = "Dots"
  ) {
    $this.Spinner = $this.Spinners[$type]
  }

  Spinner(
    [string]$type = "Dots",
    [int]$X,
    [int]$Y
  ) {
    $this.Spinner = $this.Spinners[$type]
    $this.X = $X
    $this.Y = $Y
  }

  [void] Start(
    [string]$label = "Loading..."
  ) {
    $this.running = $true
    $this.statedata = [System.Collections.Hashtable]::Synchronized([System.Collections.Hashtable]::new())
    $this.runspace = [runspacefactory]::CreateRunspace()
    $this.statedata.offset = ($this.Spinner.Frames | Measure-Object -Property Length -Maximum).Maximum
    $ThemedFrames = @()
    $color = [Color]::new($this.SpinColor)
    $this.Spinner.Frames | ForEach-Object {
      $ThemedFrames += $color.render($_)
    }
    $this.statedata.Frames = $ThemedFrames
    $this.statedata.Sleep = $this.Spinner.Sleep
    $this.statedata.label = $label
    $this.statedata.oldLabel = $label
    $this.statedata.X = $this.X
    $this.statedata.Y = $this.Y
    $this.runspace.Open()
    $this.Runspace.SessionStateProxy.SetVariable("StateData", $this.StateData)
    $sb = {
      [System.Console]::OutputEncoding = [System.Text.Encoding]::UTF8
      [system.Console]::CursorVisible = $false
      $X = $StateData.X
      $Y = $StateData.Y
    
      $Frames = $statedata.Frames
      $i = 0
      while ($true) {
        [System.Console]::setcursorposition($X, $Y)
        $text = $Frames[$i]    
        [system.console]::write($text)
        [System.Console]::setcursorposition(($X + $statedata.offset) + 1, $Y)
        [system.console]::write((" " * 80))
        [System.Console]::setcursorposition(($X + $statedata.offset) + 1, $Y)
        [system.console]::write($statedata.label)
        $i = ($i + 1) % $Frames.Length
        Start-Sleep -Milliseconds $Statedata.Sleep
      }
    }
    $this.session = [powershell]::create()
    $null = $this.session.AddScript($sb)
    $this.session.Runspace = $this.runspace
    $null = $this.session.BeginInvoke()
  }

  [void] SetLabel(
    [string]$label
  ) {
    $this.statedata.label = $label
  }

  [void] SetColor(
    [candyColor]$color
  ) {
    $this.SpinColor = $color
  }

  [void] Stop() {
    if ($this.running -eq $true) {
      [System.Console]::setcursorposition(0, $this.Y)
      [system.console]::write("".PadLeft($this.Width, " "))
      $this.running = $false
      $this.session.Stop()
      $this.runspace.Close()
      $this.runspace.Dispose()
      [System.Console]::setcursorposition($this.X, $this.Y)
      [system.Console]::CursorVisible = $this.CuirsorState
    } 
  }
}

class ListItem {
  [string]$text
  [PSCustomObject]$value
  [string]$Icon
  [int]$IconWidth = 0
  [bool]$selected = $false
  [bool]$checked = $false
  [bool]$chained = $true
  [bool]$header = $false
  
  ListItem(
    [string]$text,
    [PSCustomObject]$value,
    [string]$Icon
  ) {
    $this.text = $text
    $this.value = $value
    $this.IconWidth = [candyString]::GetStringFullLength((Build-Candy -Text $Icon))
    $this.Icon = $Icon
  }

  ListItem(
    [string]$text,
    [PSCustomObject]$value
  ) {
    $this.text = $text
    $this.value = $value
    $this.Icon = ""
    $this.IconWidth = 0
  }
}


class List {
  [System.Collections.Generic.List[ListItem]]$items
  [int]$pages = 1
  [int]$page = 1
  [int]$height = 0
  [int]$index = 0
  [int]$width = $Host.UI.RawUI.BufferSize.Width - 3
  [string]$filter = ""
  [string]$blanks = " "
  [int]$linelen = 0
  [char]$selector = ">"
  [Color]$SearchColor = [Color]::new([Colors]::BlueViolet())
  [Color]$HeaderColor = [Color]::new([Colors]::BlueViolet())
  [Color]$SelectedColor = [Color]::new([Colors]::Green())
  [Color]$FilterColor = [Color]::new([Colors]::Orange())
  [Color]$NoFilterColor = [Color]::new([Colors]::Green())
  [string]$checked = "▣"
  [string]$unchecked = "▢"
  [bool]$limit = $false
  [bool]$border = $false
  [bool]$fullscreen = $true
  [string]$header = ""
  [int]$headoffset = 0
  [int]$nbToDraw = 0
  [string]$title = ""
  [hashtable]$borderType = [Border]::GetBorder("Rounded")
  [int]$Y = $global:Host.UI.RawUI.CursorPosition.Y
  
  # TODO: Rendre paramétrable le style de sélection

  [void] Theme() {
    [theme]::init()
    $this.SearchColor = [Color]::new([candyColor]::tocolor($env:LIST_SEARCHCOLOR))
    $this.SelectedColor = [Color]::new([candyColor]::tocolor($env:LIST_SELECTEDCOLOR))
    $this.HeaderColor = [Color]::new([candyColor]::tocolor($env:LIST_HEADERCOLOR))
    $this.FilterColor = [Color]::new([candyColor]::tocolor($env:LIST_FILTERCOLOR))
    $this.NoFilterColor = [Color]::new([candyColor]::tocolor($env:LIST_NOFILTERCOLOR))
    $this.SelectedColor.style = [Enum]::Parse([Styles], $env:LIST_SELECTED_STYLE)
    $this.checked = $env:LIST_CHECKED
    $this.unchecked = $env:LIST_UNCHECKED
  }

  List (
    [System.Collections.Generic.List[ListItem]]$items
  ) {
    $this.items = $items
    # fixing the items icon size
    $IconWidth = ($items | ForEach-Object {
      $_.IconWidth
    } | Measure-Object -Maximum).Maximum

    $this.items | ForEach-Object {
      if ($_.Icon.Trim() -eq "") {
        $_.Icon = " "
      } 
      $_.IconWidth = $IconWidth
    }
    $this.height = ($global:Host.UI.RawUI.BufferSize.Height - 6) - $this.Y
    $this.Theme()
  }

  [Void] DrawTitle(
    [string]$title,
    [boolean]$border = $true
  ) {
    [console]::setcursorposition(0, $this.Y)
    Write-Candy -Text $title -Border "Rounded" -Width ($this.width) -Align Center
    $this.Y = $global:Host.UI.RawUI.CursorPosition.Y
    $this.height = $this.height - 3
  }

  [Void] DrawFooter() {
    $footerOffset = (2 + $this.y)
    if ($this.border) {
      $footerOffset = (2 + $this.Y)
    }
    [console]::setcursorposition(0, $this.height + $footerOffset)
    [Console]::Write((" " * $this.width))
    [console]::setcursorposition(0, $this.height + $footerOffset)
    # $footer = "◖"
    if ($this.pages -gt 1) {
      $footer += "<B>Page</B> $($this.page)/$($this.pages)"
    }
    
    if ($this.filter -and ($this.filter -ne "")) {
      $filtertext = "<Orange>$($this.filter)</Orange>"
    }
    else {
      $filtertext = ("<Green>None</Green>")
    }
    $footer += " ⋮ [<I>Filter:</I> $($filtertext)]"
    # [console]::WriteLine($footer)
    Write-Candy -Text $footer -Border "Rounded" -Width ($this.width) 
  }

  [void] setTitle(
    [string]$title
  ) {
    $this.title = $title
  }
  [void] SetHeight(
    [int]$height
  ) {
    $this.height = $height
    $this.blanks = (" " * $global:Host.UI.RawUI.BufferSize.Width) * ($this.height + 2)
  }

  [void] SetWidth(
    [int]$Width
  ) {
    $this.width = $width
    $this.blanks = (" " * $global:Host.UI.RawUI.BufferSize.Width) * ($this.height + 2)
  }

  [void]  SetLimit(
    [Bool]$limit
  ) {
    $this.limit = $limit
  }

  [void] SetItems([System.Collections.Generic.List[ListItem]]$items) {
    $this.items = $items
  }


  [void] SetHeader([string]$header) {
    $this.header = $header
  }

  [String] MakeBufer(
    [System.Collections.Generic.List[ListItem]]$items
  ) {

    function makeFilteredItem {
      param (
        [string]$text
      )
      $filter = (Build-candy $text) -split '\e'
      $result = $filter | ForEach-Object {
        if ([regex]::IsMatch($_, $this.filter)) {
          $currentIndex = 0
          $color = [Regex]::Match($_, "\[38[\d;]*m")
          $Filtermatches = [regex]::Matches($_, $this.filter)
          $buffer = ""
          foreach ($match in $Filtermatches) {
            if ($match.Index -gt $currentIndex) {
              $buffer = [string]::concat($buffer, $_.Substring($currentIndex, $match.Index - $currentIndex))
            }
            $innerText = Build-Candy -text "[White]<Blue>$($match.Groups[0].Value)</Blue>[/White]"
            if ($color.Success) {
              $innerText = [string]::concat($innerText, "$($script:esc)$($color.Value)")
            }
            else {
              $innerText = [string]::concat($innerText, "$($script:esc)[39m")
            }
            $buffer = [string]::concat($buffer, $innerText)
            $currentIndex = $match.Index + $match.Length
          }
        
          if ($currentIndex -lt $_.Length) {
            $buffer = [string]::concat($buffer, $_.Substring($currentIndex))
          }
          $buffer
        }
        else {
          $_
        }
      }
      
      $text = $result -join $($script:esc)
      return $text
    }

    $i = 0
    $script:offset = 0
    if ($this.limit) {
      $baseoffset = 3
    }
    else {
      $baseoffset = 3
    }
    $offset = $baseoffset
    try {
      if ($items) {       
        $buffer = $items | ForEach-Object {
          $offset = $baseoffset
          # $checkmark = ""
          $icon = $_.Icon
          # if ($_.Icon -ne "") {
            $offset += $_.IconWidth
          # }
          # else {
          # $icon = " "
          # $offset += 3
          # }
          $text = $_.text
          
          if ($this.filter -and ($this.filter -ne "")) {
            $text = makeFilteredItem $text
          } 
          if ($this.limit) {
            $checkmark = ""
          }
          else {
            if ($_.checked) {
              $checkmark = $this.checked
            }
            else {
              $checkmark = $this.unchecked
            }
          }
          $text = Build-Candy $text
          $fill = $this.linelen - $offset
          $text = [candyString]::PadString($text, $fill, " ", [Align]::Left)
          $text = "$checkmark $icon $text"
          if ($this.index -eq $i) {
            $text = "<U>$($this.selector) $($text)</U>"
          }
          else {
            $text = " $($text)"
          }
          
          Build-Candy $text
          
          $i++
        } | Out-String
      }
      else {
        $buffer = "Too much filter ? 😊"
      }
      while ($i -lt ($this.nbToDraw)) {
        $buffer += [candyString]::PadString("", ($this.linelen + 2), " ", [Align]::Left) + "`n"
        $i++
      }
    }
    catch {
      Write-Host "An error occurred:"
      Write-Host $PSScriptRoot
      Write-Host $_
      Exit(1)
    }
    if ($this.border) {
      while ($i -lt $this.height) {
        $buffer += $this.borderType.Left + "".PadRight(($this.linelen + 4 + $script:offset), " ") + $this.borderType.Right + "`n"
        $i++
      }
      $buffer = $this.borderType.TopLeft + "".PadLeft(($this.linelen + 4 + $script:offset), $this.borderType.Top) + $this.borderType.TopRight + "`n" + $buffer
      $buffer = $buffer + $this.borderType.BottomLeft + "".PadLeft(($this.linelen + 4 + $script:offset), $this.borderType.Bottom) + $this.borderType.BottomRight
    }
    $this.headoffset = $offset
    return $buffer
  }

  [System.Collections.Generic.List[PSObject]] Display() {
    $result = @()
    $this.pages = [math]::Ceiling($this.items.Count / $this.height)
    [System.Collections.Generic.List[ListItem]]$VisibleItems = @()
    $stop = $false
    [console]::CursorVisible = $false
    $redraw = $true
    $search = $false
    $continue = $false
    if ($this.fullscreen) {
      $this.linelen = $this.width 
      if (-not $this.limit) {
        $this.linelen = $this.width - 2
      }
    }
    else {
      $this.linelen = ($this.items | Measure-Object -Maximum {
        ($_.text).Length
        }).Maximum
    }
    if ($this.title -ne "") {
      $this.DrawTitle($this.title, $true)
    }
    $this.nbToDraw = $this.height
    if ($this.header -ne "") {
      $this.nbToDraw = $this.height - 1
    }
    while (-not $stop) {
      if ($redraw) {
        if ($search) {
          # TODO: Gérer les coordonnées pour intégrer le cadre
          [Console]::setcursorposition(0, $this.Y)
          [console]::Write($this.SearchColor.Render("Search: "))
          [console]::CursorVisible = $true
          # TODO: Utiliser Gum input pour encoder la recherche
          $this.filter = $global:host.UI.ReadLine()
          [console]::CursorVisible = $false
          $search = $false
          $redraw = $true
          Continue
        }
        else {
          [console]::Write("".PadLeft(80, " ")) 
        }
        if ($this.filter -and ($this.filter -ne "")) {
          $VisibleItems = $this.items | Where-Object {
            [regex]::IsMatch((Build-Candy $_.text), $this.filter)
          } | Select-Object -Skip (($this.page - 1) * $this.nbToDraw) -First $this.nbToDraw
          $this.pages = [math]::Ceiling($VisibleItems.Count / $this.nbToDraw)
        }
        else {
          $VisibleItems = $this.items | Select-Object -Skip (($this.page - 1) * $this.nbToDraw) -First $this.nbToDraw
          $this.pages = [math]::Ceiling($this.items.Count / $this.nbToDraw)
        }
        $buffer = $this.MakeBufer($VisibleItems)
        [Console]::setcursorposition(0, $this.Y)
        if ($this.index -gt $VisibleItems.Count - 1 ) {
          $this.index = 0
        }
        if ($this.header -ne "") {
          $leftoffset = ""
          if ($this.limit -eq $false) {
            $leftoffset = " " * $this.headoffset
          } 
          $head = $this.header #.PadRight(($this.width + 2 + $leftoffset.Length ), " ")
          Write-Candy "$leftoffset <U>$($head)</U>" 
        }
        [Console]::Write($buffer)
        
        $this.DrawFooter()
      }
      $redraw = $false
      if ($global:Host.UI.RawUI.KeyAvailable) {
        [System.Management.Automation.Host.KeyInfo]$key = $($global:host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown'))
        if ($env:DEBUGVISUAL -eq $true) {
          $bottom = ($this.height + 2) + $this.Y
          [Console]::setcursorposition(0, $bottom)
          if ($this.filter -and ($this.filter -ne "")) {
            Write-Host "Filter : $($this.filter)"
          }
          else {
            Write-Host "No Filter "
          }
          [Console]::setcursorposition(0, ($bottom + 1))
          [Console]::write("Key: $($key.VirtualKeyCode) Char : $($key.Character)")
          [Console]::setcursorposition(0, ($bottom + 2))
          [Console]::write("Key: $($key.ControlKeyState) ")
          [Console]::setcursorposition(20, ($bottom + 1))
          [Console]::write("LineLen: $($this.linelen) ")
          
        }
        switch ($key.VirtualKeyCode) {
          { (($_ -ge 65) -and ($_ -le 101)) -or (($_ -ge 48) -and ($_ -le 57)) } {
            $car = $key.Character.ToString()
            if ($key.ControlKeyState -eq "ShiftPressed") {
              $car = $car.ToUpper()
            }
            $this.filter = $this.filter + $Car
            $redraw = $true
          }
          8 {
            # Backspace
            if ($this.filter.Length -gt 0) {
              $this.filter = $this.filter.Substring(0, $this.filter.Length - 1)
              $redraw = $true
            }
          }
          38 {
            # Up
            
            $this.index--
            $redraw = $true
            
          }
          40 {
            # Down
            
            $this.index++
            $redraw = $true
            
          }
          191 {
            if ($key.ControlKeyState -eq "ShiftPressed") {
              $search = $true
              $redraw = $true
            }
          }
          37 {
            # Left
            if ($key.ControlKeyState -eq "LeftCtrlPressed") {

            }
            else {
              if ($this.page -gt 1) {
                $this.page--
                $redraw = $true
                [System.Collections.Generic.List[ListItem]]$VisibleItems = $this.items | Select-Object -Skip (($this.page - 1) * $this.height) -First $this.height
              }
            }
          }
          39 {
            # Right
            if ($key.ControlKeyState -eq "LeftCtrlPressed") {

            }
            else {
              if ($this.page -lt $this.pages) {
                $this.page++
                $redraw = $true
                [System.Collections.Generic.List[ListItem]]$VisibleItems = $this.items | Select-Object -Skip (($this.page - 1) * $this.height) -First $this.height
              }
            }
          }
          9 {
            # Tab
            $VisibleItems[$this.index].checked = -not $VisibleItems[$this.index].checked
            $this.index++
            $redraw = $true
          }
          13 {
            # Enter
            $stop = $true
            if ($this.limit) {
              if ($key.ControlKeyState -eq "LeftCtrlPressed") {
                $continue = $true
              }
              $result = $VisibleItems[$this.index]
            }
            else {
              $this.items | ForEach-Object {
                if ($_.checked) {
                  $result += $_
                }
              }
            }
          }
          27 {
            $stop = $true
          }
        }
        if ($redraw) {
          if ($this.index -lt 0) {
            $this.index = 0
          }
          if ($this.index -gt $VisibleItems.Count - 1) {
            $this.index = $VisibleItems.Count - 1
          }
        }
        # [console]::Clear()
      }
    }
    [console]::CursorVisible = $true
    [Console]::Clear()
    if ($continue) {
      $fields = "text", "value", "chained"
    }
    else {
      $fields = "text", "value"
    }
    
    return $result | Select-Object -Property $fields
  }

}

class Confirm {
  [string]$Message = ""
  [Option[]]$Choices
  [int]$width = $Host.UI.RawUI.BufferSize.Width - 2
  [bool]$fullscreen = $false
  [Color]$SeletedColor = [color]::new([Colors]::White(), [Colors]::BlueViolet())
  [Color]$OptionColor = [color]::new([Colors]::Cyan())
  [Color]$MessageColor = [color]::new([Colors]::White())
  [int]$index = 0
  Confirm(
    [string]$Message,
    [Option[]]$Choices,
    [int]$width
  ) {
    $this.Message = $Message
    $this.Choices = $Choices
    $this.fullscreen = $false
    $this.width = $width
  }

  Confirm(
    [string]$Message,
    [Option[]]$Choices,
    [bool]$fullscreen
  ) {
    $this.Message = $Message
    $this.Choices = $Choices
    $this.fullscreen = $true
  }

  [void] LoadTheme() {
    # $this.SeletedColor = [Color]::new($script:Theme.Choice.SelectedForeground ? $script:Theme.Choice.SelectedForeground : [Colors]::White()
    # ,
    # $script:Theme.Choice.SelectedBackground ? $script:Theme.Choice.SelectedBackground : [Colors]::BlueViolet())
    # $this.OptionColor = [Color]::new($script:Theme.Choice.OptionColor ? $script:Theme.Choice.OptionColor : [Colors]::Cyan())
    # $this.MessageColor = [Color]::new($script:Theme.Choice.MessageColor ? $script:Theme.Choice.MessageColor : [Colors]::White())
  }

  [PSCustomObject] Display() {
    $this.LoadTheme()
    $result = $null
    $title = $this.Message
    Write-Candy -Text $title -Align Center -Width $this.width -Border "rounded"
    $nbChoices = $this.Choices.Count
    $choiceWidth = [Math]::Floor($this.width / $nbChoices) - 4
    [Console]::WriteLine()
    $buffer = $this.choices | ForEach-Object {
      $text = $_.text
      $padding = [Math]::Ceiling(($choiceWidth - $text.Length) / 2)
      $filler = "".padleft($padding, " ")
      $text = $filler.Remove([math]::Floor($padding / 2), [math]::Floor($text.Length / 2)).Insert([math]::Floor($padding / 2), $text)
      
      $_.text = $text
    }
    $buttonswidth = 0
    $this.Choices | ForEach-Object {
      $buttonswidth += $_.text.Length
    }
    $spaceleft = $this.width - $buttonswidth
    $filler = [math]::Floor($spaceleft / ($this.Choices.Count + 1))
    $space = "".padleft($filler, " ")
    $stop = $false
    $y = [Console]::CursorTop
    [console]::CursorVisible = $false
    $this.index = 0
    $i = 0
    $this.Choices | ForEach-Object {
      if ($_.selected) {
        $this.index = $i
      }
      $i++
    }
    $this.Choices[$this.index].selected = $true
    while (-not $stop) {
      $buffer = $this.Choices | ForEach-Object {
        if ($_.selected) {
          $text = $this.SeletedColor.render($_.text)
        }
        else {
          $text = $this.OptionColor.render($_.text)
        }
        "$space$($text)"
      } | Out-String 
      [Console]::SetCursorPosition(0, $y)
      [Console]::Write(($buffer -split "`r`n") -join '')
      if ($global:Host.UI.RawUI.KeyAvailable) {
        [System.Management.Automation.Host.KeyInfo]$key = $($global:host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown'))
        if ($key.VirtualKeyCode -eq 13) {
          $stop = $true
          $result = $this.Choices | Where-Object { $_.selected -eq $true }
        }
        if ($key.VirtualKeyCode -eq 37) {
          # Left
          $this.Choices[$this.index].selected = $false
          if ($this.index -eq 0) {
            $this.index = $this.Choices.Count - 1
          }
          else {
            $this.index--
          }
          $this.Choices[$this.index].selected = $true
        }
        if ($key.VirtualKeyCode -eq 39) {
          # Right
          $this.Choices[$this.index].selected = $false
          if ($this.index -eq ($this.Choices.Count - 1)) {
            $this.index = 0
          }
          else {
            $this.index++
          }
          $this.Choices[$this.index].selected = $true
        }
      }
    }
    [console]::CursorVisible = $true
    $fields = "value"
    return $result | Select-Object -Property $fields
  }
}

class Style {
  # TODO: Ajouter une gestion de la couleur et de l'alignement pat ligne
  # TODO: Ajouter des "sections" au composant
  [int]$width = $Host.UI.RawUI.BufferSize.Width - 2
  [string]$text = ""
  [Color]$color = [Color]::new([Colors]::White(), $null)
  [Styles]$style = [Styles]::Normal
  [bool]$border = $false
  [hashtable]$borderType = [Border]::GetBorder("Rounded")
  [Align]$align = [Align]::left

  Style(
    [string]$text
  ) {
    $this.text = $text
  }

  [void] SetColor(
    [candyColor]$Foreground,
    [candyColor]$Background
  ) {
    $this.color = [Color]::new($Foreground, $Background)
  }

  [void] SetColor(
    [candyColor]$Foreground
  ) {
    $this.color = [Color]::new($Foreground, $null)
  }

  [void] SetStyle(
    [Styles]$style
  ) {
    $this.style = $style
  }

  [void] SetBorder(
    [bool]$border
  ) {
    $this.border = $border
  }

  [void] setAlign(
    [Align]$align
  ) {
    $this.align = $align
  }

  [void] SetWidth(
    [int]$width
  ) {
    $this.width = $width
  }

  [void] SetLabel(
    [string]$text
  ) {
    $this.text = $text
  }

  [String] Render() {
    $labels = $this.text -split "`n"
    $label = $labels | ForEach-Object {
      $lbl = $_
      switch ($this.align) {
        Center {
          $lblClean = $lbl -replace "\e\[[\d;]*m", ''
          $lbllen = ($lblClean).Length
          $padding = ($this.width - $lbllen) / 2
          $leftpadding = [int][Math]::Floor($padding)
          $rightpadding = [int][Math]::Ceiling($padding)
          $lbl = (" " * $leftPadding) + $lbl + (" " * $rightPadding)
        }
        Right {
          $lbl = $lbl.PadLeft($this.width, " ")
        }
        Left {
          $lbl = $lbl.PadRight($this.width , " ")
        }
      }
      if ($this.color -ne [Color]::Empty) {
        $this.color.style = $this.style
        $lbl = $this.color.render($lbl)
      }
      $lbl
    }
    $result = $label -join "`n"
    if ($this.border) {
      $top = $this.borderType.TopLeft + "".PadLeft(($this.width), $this.borderType.Top) + $this.borderType.TopRight
      $result = $label | ForEach-Object {
        $this.borderType.Left + $_ + $this.borderType.Right
      } | Out-String
      $result = $top + "`n" + $result + $this.borderType.BottomLeft + "".PadLeft(($this.width), $this.borderType.Bottom) + $this.borderType.BottomRight
    }
    return $result
  }

}

class Pager {
  $buffer
  [int]$height = $Host.UI.RawUI.BufferSize.Height - 2
  [int]$width = $Host.UI.RawUI.BufferSize.Width - 2
  [int]$offset = 0
  [int]$index = 0
  [Color]$selectedColor = [Color]::new($null, $null)
  [hashtable]$borderType = [Border]::GetBorder("Rounded")
  [hashtable]$actions = @{}

  Pager(
    [string]$file
  ) {
    # TODO: check if bat is installed
    [console]::OutputEncoding = [System.Text.Encoding]::UTF8
    $this.buffer = Invoke-Expression -Command "bat $file --style='numbers' -f --terminal-width $($this.width)"
    $this.selectedColor.style = [Styles]::Underline
  }

  [void] Display() {
    [console]::OutputEncoding = [System.Text.Encoding]::UTF8
    [console]::Clear()
    $stop = $false
    [console]::CursorVisible = $false
    while (-not $stop) {
      $i = 0
      $cache = $this.buffer | Select-Object -Skip $this.offset -First $this.height | ForEach-Object {
        if ($i -eq $this.index) {
          $line = $this.selectedColor.render(($_ -replace "\e\[[\d;]*m", '').padright($this.width, " "))
        }
        else {
          $offset = ($_ -replace "\e\[[\d;]*m", '').Length
          $line = $_.padright(($this.width + ($_.Length - $offset)), " ")
        }
        
        $i++
        $this.borderType.Left + $line + $this.borderType.Right
      } | Out-String
      $cache = ($this.borderType.TopLeft + "".padleft($this.width, $this.borderType.top) + $this.borderType.TopRight) + "`n" + $cache + ($this.borderType.BottomLeft + "".padleft($this.width, $this.borderType.bottom) + $this.borderType.BottomRight)
      [console]::setcursorposition(0, 0)
      [console]::write($cache)
      if ($global:Host.UI.RawUI.KeyAvailable) {
        [System.Management.Automation.Host.KeyInfo]$key = $($global:host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown'))
        switch ($key.VirtualKeyCode) {
          38 {
            # Up
            $this.index --
            if ($this.index -lt 0) {
              $this.index = 0
              if ($this.offset -gt 0) {
                $this.offset--
              }
            }
            
          }
          40 {
            # Down
            $this.index++
            if ($this.index -gt ($this.height - 1)) {
              $this.index = $this.height - 1
              if ($this.offset -lt ($this.buffer.Length - $this.height)) {
                $this.offset++
              }
              
            }
          }
          27 {
            $stop = $true
          }
          default {
            $action = $this.actions[$key.VirtualKeyCode]
            if ($action) {
              $action.Invoke()
            }
          }
        }
      }
    }
    [console]::CursorVisible = $true
  }
}

function Write-Candy {
  param (
    [Parameter(ValueFromPipeline = $true)]
    [string]$Text,
    [int]$Width = -1,
    [Align]$Align = [Align]::Left,
    [String]$Border = "None",
    [switch]$fullscreen = $false
  )
  Write-Host (Build-Candy -Text $Text -Width $Width -Align $Align -Border $Border -fullscreen:$fullscreen)
}
function Build-Candy {
  param (
    [Parameter(ValueFromPipeline = $true)]
    [string]$Text,
    [int]$Width = -1,
    [Align]$Align = [Align]::Left,
    [String]$Border = "None",
    [switch]$fullscreen = $false
  )
  #TODO: Styles imbriqués
  #TODO: Gérer le muilti-ligne:
  if (($null -eq $Text) -or ($Text -eq "")) {
    return ""
  }

  function  parseForegroundColor {
    param (
      [string]$text,
      [string]$pattern,
      [switch]$alias
    )
    $currentIndex = 0
    $Colormatches = [regex]::Matches($Text, $Pattern)
    $buffer = ""
    if ($Colormatches.Count -eq 0) {
      return $text
    }
    foreach ($match in $Colormatches) {
      if ($match.Index -gt $currentIndex) {
        $buffer = [string]::concat($buffer, $Text.Substring($currentIndex, $match.Index - $currentIndex))
      }
      $color = $match.Groups['color'].Value
      if ($alias.IsPresent) {
        $innerText = [color]::Applycolor16(($match.Groups['text'].Value), $color, -1)
      }
      else {
        $innerText = [Color]::ApplyColorRGB(($match.Groups['text'].Value), $color, "")
      }
      $buffer = [string]::concat($buffer, $innerText)
      $currentIndex = $match.Index + $match.Length
    }
  
    if ($currentIndex -lt $Text.Length) {
      $buffer = [string]::concat($buffer, $Text.Substring($currentIndex))
    }
    return $buffer  
  }

  function  parseBackgroundColor {
    param (
      [string]$text,
      [string]$pattern,
      [switch]$alias
    )
    $currentIndex = 0
    $Colormatches = [regex]::Matches($Text, $Pattern)
    $buffer = ""
    if ($Colormatches.Count -eq 0) {
      return $text
    }
    foreach ($match in $Colormatches) {
      if ($match.Index -gt $currentIndex) {
        $buffer = [string]::concat($buffer, $Text.Substring($currentIndex, $match.Index - $currentIndex))
      }
      $color = $match.Groups['color'].Value
      if ($alias.IsPresent) {
        $innerText = [color]::Applycolor16(($match.Groups['text'].Value), -1, $color)
      }
      else {
        $innerText = [Color]::ApplyColorRGB(($match.Groups['text'].Value), "", $color)
      }      
      $buffer = [string]::concat($buffer, $innerText)
      $currentIndex = $match.Index + $match.Length
    }
  
    if ($currentIndex -lt $Text.Length) {
      $buffer = [string]::concat($buffer, $Text.Substring($currentIndex))
    }
    return $buffer  
  }

  $lines = $text -split "`n"

  $buffer = $lines | ForEach-Object {
    $ForegroundPattern = '<(?<color>' + $($script:colors) + ')>(?<text>.*?)<\/\k<color>>'
    $BackgroundPattern = '\[(?<color>' + $($script:colors) + ')\](?<text>.*?)\[\/\k<color>\]'
    $buffer = parseBackgroundColor -text $_ -pattern $BackgroundPattern
    $buffer = parseForegroundColor -text $buffer -pattern $ForegroundPattern

    $ForegroundPattern = '<(?<color>\d{1,3})>(?<text>.*?)<\/\k<color>>'
    $BackgroundPattern = '\[(?<color>\d{1,3})\](?<text>.*?)\[\/\k<color>\]'
    $buffer = parseBackgroundColor -text $Buffer -pattern $BackgroundPattern -Alias
    $buffer = parseForegroundColor -text $buffer -pattern $ForegroundPattern -alias

    #TODO: refactor the styles at only one place
  
    $styles = @{
      "Underline" = @{
        "start" = "$esc[4m"
        "end"   = "$esc[24m"
      }
      "Bold"      = @{
        "start" = "$esc[1m"
        "end"   = "$esc[22m"
      }
      "Italic"    = @{
        "start" = "$esc[3m"
        "end"   = "$esc[23m"
      }
      "Strike"    = @{
        "start" = "$esc[9m"
        "end"   = "$esc[29m"
      }
      "Reverse"   = @{
        "start" = "$esc[7m"
        "end"   = "$esc[27m"
      }
      "U"         = @{
        "start" = "$esc[4m"
        "end"   = "$esc[24m"
      }
      "B"         = @{
        "start" = "$esc[1m"
        "end"   = "$esc[22m"
      }
      "I"         = @{
        "start" = "$esc[3m"
        "end"   = "$esc[23m"
      }
      "S"         = @{
        "start" = "$esc[9m"
        "end"   = "$esc[29m"
      }
      "R"         = @{
        "start" = "$esc[7m"
        "end"   = "$esc[27m"
      }
    }
    $styles.keys | ForEach-Object {
      $start = $styles[$_].start
      $end = $styles[$_].end
      if ($buffer.Contains("<$($_)>")) {
        $buffer = $buffer -replace "<$($_)>", $start
        $buffer = $buffer -replace "</$($_)>", $end
      }
    }
    $buffer
  }
  
  # $buffer2 = [Color]::endStyle($buffer)
  

  if ($Width -eq -1) {
    if ($FullScreen.IsPresent) {
      $Width = $Host.UI.RawUI.BufferSize.Width - 2
    }
    else {
      $Width = [candyString]::GetDisplayWidth($buffer2)
    }
  } 

  if ($Width -gt 0) {
    $Buffer2 = $buffer | ForEach-Object {
      [candyString]::PadString($_, $Width, " ", $Align)
    }
  }
  else {
    $buffer2 = $buffer
  }
  
  if ($Border.ToLower() -ne "none") {
    $borderType = [Border]::GetBorder($Border)
    $bufferwidth = 0
    $bufferlen = 0
    $buffer2 | ForEach-Object {
      $bw = [candyString]::GetDisplayWidth($_)
      if ($bw -gt $bufferwidth) {
        $bufferwidth = $bw 
        $bufferlen = [candyString]::GetDisplayLength($_)
      }
    }
    
    $diff = $bufferwidth - $bufferlen
    $buffer = $borderType.TopLeft + "".PadLeft(($bufferwidth - $diff), $borderType.Top) + $borderType.TopRight + "`n" 
    $buffer2 | ForEach-Object {
      $buffer += $borderType.Left + [candystring]::PadString($_,($bufferwidth - $diff)," ",$align) + $borderType.Right + "`n"
    }
    $buffer += $borderType.BottomLeft + "".PadLeft(($bufferwidth - $diff), $borderType.Bottom) + $borderType.BottomRight
  }
  else {
    $buffer = $buffer2 -join "`n"
  }
  

  $buffer
}

function Confirm-Candy {
  param (
    [string]$Title,
    [String]$Choices,
    [int]$Width = -1,
    [switch]$Fullscreen = $false
  )
  [Option[]]$options = @()
  $Choices -split "`n" | ForEach-Object {
    $options += [Option]::new($_, $_)
  }
  if ($Fullscreen) {
    $confirm = [Confirm]::new($title, $options, $Fullscreen)
  }
  else {
    if ($Width -eq -1) {
      $Width = [math]::floor($Host.UI.RawUI.BufferSize.Width / 2)
    }
    $confirm = [Confirm]::new($title, $options, $Width)
  }
  $result = $confirm.Display()
  return $result
}

function Select-Candy {
  param(
    [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
    [pscustomobject[]]$Input,
    [string]$Title = "",
    [switch]$limit = $false
  )
  begin {
    [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 
    [console]::Clear()
    [System.Collections.Generic.List[ListItem]]$items = [System.Collections.Generic.List[ListItem]]::new()
  }
  
  process {
    foreach ($i in $Input) {
      # $i.item
      $items.Add([ListItem]::new($i.CandyLabel, $i.Item))
    } 
  }
  

  end {
    $list = [List]::new($items)  
    # $list.SetHeight(10)
    # $list.SetLimit($True)
    $list.fullscreen = $true
    if ($title -ne "") {
      $list.SetTitle($title)
    }
    $c = $list.Display()
    return $c
  } 
}

Function Select-CandyColor {
  param (
    [switch]$clipboard
  )
  $color = [Color]::Pick()
  if ($clipboard.IsPresent) {
    $color | Set-Clipboard
  }
  else {
    $color
  }
}

Function Select-CandyColor8 {
  param (
    [switch]$clipboard
  )
  $color = [Color]::Pick8()
  if ($clipboard.IsPresent) {
    $color | Set-Clipboard
  }
  else {
    $color
  }
}

$script:colors = [candyColor]::colorList()

$ExportableTypes =@(
    [Styles],[Align],[Theme],[candyString],[candyColor],[Colors],[Color],[Option],[Border],[Spinner],[ListItem],[List],[Confirm],[Style],[Pager]
)
$TypeAcceleratorsClass = [psobject].Assembly.GetType(
    'System.Management.Automation.TypeAccelerators'
)

$ExistingTypeAccelerators = $TypeAcceleratorsClass::Get

foreach ($Type in $ExportableTypes) {
  if ($Type.FullName -notin $ExistingTypeAccelerators.Keys) {
    $TypeAcceleratorsClass::Add($Type.FullName, $Type)
  }
}

$MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = {
    foreach($Type in $ExportableTypes) {
        $TypeAcceleratorsClass::Remove($Type.FullName)
    }
}.GetNewClosure()