import winim/[ lean
             , winstr 
             ]

# if a win32 window does a bad
# this will call into M$ land
# and get the associated err code's reason 
proc why*(err: DWORD): auto = 
 var
   theDWord: lean.DWORD =  FORMAT_MESSAGE_ALLOCATE_BUFFER or 
                           FORMAT_MESSAGE_FROM_SYSTEM or
                           FORMAT_MESSAGE_IGNORE_INSERTS
   theOtherdWord: lean.DWORD = cast [lean.DWORD](MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT))                         
   messageBuffer: LPSTR
   size = FormatMessageA( theDWord
                        ,  nil
                        , err
                        , theOtherDWord
                        , cast[LPSTR](addr messageBuffer)
                        , 0
                        , nil)
  
 result = messageBuffer

proc ctNewWString(s: static[string]): wstring =
  # copy from widestrs.nim, use WCHAR instead of Utf16Char
  const
    UNI_REPLACEMENT_CHAR = WCHAR(0xFFFD'u16)
    UNI_MAX_BMP = 0x0000FFFF
    UNI_MAX_UTF16 = 0x0010FFFF

    halfShift = 10
    halfBase = 0x0010000
    halfMask = 0x3FF

    UNI_SUR_HIGH_START = 0xD800
    UNI_SUR_LOW_START = 0xDC00
    UNI_SUR_LOW_END = 0xDFFF
    UNI_REPL = 0xFFFD

  template ones(n: untyped): untyped = ((1 shl n)-1)

  template fastRuneAt(s: cstring, i, L: int, result: untyped, doInc = true) =
    if ord(s[i]) <= 127:
      result = ord(s[i])
      when doInc: inc(i)
    elif ord(s[i]) shr 5 == 0b110:
      if i <= L - 2:
        result = (ord(s[i]) and (ones(5))) shl 6 or (ord(s[i+1]) and ones(6))
        when doInc: inc(i, 2)
      else:
        result = UNI_REPL
        when doInc: inc(i)
    elif ord(s[i]) shr 4 == 0b1110:
      if i <= L - 3:
        result = (ord(s[i]) and ones(4)) shl 12 or
                 (ord(s[i+1]) and ones(6)) shl 6 or
                 (ord(s[i+2]) and ones(6))
        when doInc: inc(i, 3)
      else:
        result = UNI_REPL
        when doInc: inc(i)
    elif ord(s[i]) shr 3 == 0b11110:
      if i <= L - 4:
        result = (ord(s[i]) and ones(3)) shl 18 or
                 (ord(s[i+1]) and ones(6)) shl 12 or
                 (ord(s[i+2]) and ones(6)) shl 6 or
                 (ord(s[i+3]) and ones(6))
        when doInc: inc(i, 4)
      else:
        result = UNI_REPL
        when doInc: inc(i)
    else:
      result = 0xFFFD
      when doInc: inc(i)

  iterator runes(s: cstring, L: int): int =
    var
      i = 0
      ret: int

    while i < L:
      fastRuneAt(s, i, L, ret, true)
      yield ret

  iterator WCHARs(source: cstring, L: int): WCHAR =
    for ch in runes(source, L):
      if ch <=% UNI_MAX_BMP:
        if ch >=% UNI_SUR_HIGH_START and ch <=% UNI_SUR_LOW_END:
          yield UNI_REPLACEMENT_CHAR
        else:
          yield WCHAR(ch)
      elif ch >% UNI_MAX_UTF16:
        yield UNI_REPLACEMENT_CHAR
      else:
        let ch = ch -% halfBase
        yield WCHAR((ch shr halfShift) +% UNI_SUR_HIGH_START)
        yield WCHAR((ch and halfMask) +% UNI_SUR_LOW_START)

  var ret: string
  for u in WCHARs(s, s.len):
    ret.add char(u and 0xFF)
    ret.add char(u shr 8)

  ret.add "\0\0"
  result = wstring ret

# no const to avoid Error: "VM does not support cast from tyCstring to tyPtr"
template L_no_const*(x: string): wstring =
  ## Generate const wstring from `static[string]` at compile-time.
  var thing = ctNewWString(x)
  thing