#? stdtmpl(subsChar='?') | standard
#proc generateNsisSetup(c: ConfigData): string =
#  result = "; NSIS script generated by niminst\n" &
#           "; To regenerate run ``niminst nsis`` or ``koch nsis``\n"

;--------------------------------
; Included headers
  ; Modern User Interface 2.0 Header
  !include "MUI2.nsh"

  ; File Functions Header, used to get the current drive root.
  !include "FileFunc.nsh"

;--------------------------------
; Global variables and defines
  !define PRODUCT_NAME "?c.displayName"
  !define PRODUCT_VERSION "?c.version"
  !define PRODUCT_PUBLISHER "?c.authors"
  !define PRODUCT_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\?{c.name}.exe"
  !define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
  !define PRODUCT_UNINST_ROOT_KEY "HKCU"
  !define PRODUCT_STARTMENU_REGVAL "NSIS:StartMenuDir"


;--------------------------------
; General Setup Information

  ; Name and output file
  Name "?{c.name} ?{c.version}"
  OutFile "?{c.name}_?{c.version}.exe"

  ; Default installation folder
  ; This is changed later (in .onInit) to the root directory, if possible.
  InstallDir "$PROGRAMFILES?{when sizeof(int) == 8: "64" else: ""}\?{c.name}-?{c.version}\"

  ; Get installation folder from registry if available
  InstallDirRegKey HKCU "Software\c.name\c.version" ""

  ; Request user level application privileges.
  RequestExecutionLevel user

  ; Allow installation to the root drive directory.
  AllowRootDirInstall false

  ; Maximum compression!
  SetCompressor /SOLID /FINAL lzma

  ; Installer and Uninstaller Icons
  ; Icon "nim.ico"
  ; UninstallIcon "nim.ico"

  ; Set installation details to be shown by default
  ShowInstDetails show
  ShowUnInstDetails show

;--------------------------------
; Interface Settings

  ; Warn the user if aborting during installation/uninstallation
  !define MUI_ABORTWARNING
  !define MUI_UNABORTWARNING

  ; Don't show a description for sections
  !define MUI_COMPONENTSPAGE_NODESC

;--------------------------------
; Pages

  ; Setup the installer pages
  !insertmacro MUI_PAGE_WELCOME
  !insertmacro MUI_PAGE_LICENSE "?{expandFilename(c.license)}"
  !insertmacro MUI_PAGE_COMPONENTS
  !insertmacro MUI_PAGE_DIRECTORY

  ; Setup the start menu entry page
  var ICONS_GROUP
  !define MUI_STARTMENUPAGE_DEFAULTFOLDER "?{c.displayName}"
  !define MUI_STARTMENUPAGE_REGISTRY_ROOT "${PRODUCT_UNINST_ROOT_KEY}"
  !define MUI_STARTMENUPAGE_REGISTRY_KEY "${PRODUCT_UNINST_KEY}"
  !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "${PRODUCT_STARTMENU_REGVAL}"
  !insertmacro MUI_PAGE_STARTMENU Application $ICONS_GROUP

  !insertmacro MUI_PAGE_INSTFILES
  !insertmacro MUI_PAGE_FINISH

  ; Setup the uninstaller pages
  !insertmacro MUI_UNPAGE_CONFIRM
  !insertmacro MUI_UNPAGE_INSTFILES

;--------------------------------
;Languages

  !insertmacro MUI_LANGUAGE "English"

;--------------------------------
;Installer Sections

  ; The core section. This is comprised of a base Nim installation,
  ; such as what would be retrieved via git, and an already bootstrapped
  ; Nim binary.
  Section "Core Files" CoreSection
    ; This is a mandotory section
    SectionIn RO

    ; Output files to the base installation directory
    SetOutPath "$INSTDIR"

    ; Only overwrite newer files
    SetOverwrite ifnewer

    ; Write all the files to the output directory.
    #for i in low(FileCategory)..fcWindows:
    #  for f in items(c.cat[i]):
         SetOutPath "$INSTDIR\?{splitFile(f).dir.toWin}"
         File "?{expandFilename(f).toWin}"
    #  end for
    #end for

    ; Write out the uninstaller
    WriteUninstaller "$INSTDIR\uninstaller.exe"
  SectionEnd

  Section "-Add Registry Keys" RegistrySection
    ; Write application registry keys
    WriteRegStr HKCU "${PRODUCT_DIR_REGKEY}" "" "$INSTDIR\bin\?{c.name}.exe"
    WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)"
    WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninstaller.exe"
    WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\bin\?{c.name}.exe"
    WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}"
    WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
    ; Reset the output path
    SetOutPath "$INSTDIR"
  SectionEnd

  ; Section for adding the shortcuts related to files and applications
  Section "-Setup Shortcuts" ShortcutsSection
    !insertmacro MUI_STARTMENU_WRITE_BEGIN Application
      CreateDirectory "$SMPROGRAMS\$ICONS_GROUP"

      #if c.app == appConsole:
        CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\?{c.displayName}.lnk" "$INSTDIR\start.bat"
        CreateShortCut "$DESKTOP\?{c.displayName}.lnk" "$INSTDIR\start.bat"
      #else:
        CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\?{c.displayName}.lnk" "$INSTDIR\?{c.name}.exe"
        CreateShortCut "$DESKTOP\?{c.displayName}.lnk" "$INSTDIR\?{c.name}.exe"
      #end if

      ; Write the shortcut to the uninstaller
      CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\Uninstall.lnk" "$INSTDIR\uninstaller.exe"
    !insertmacro MUI_STARTMENU_WRITE_END
  SectionEnd

  ; Section for adding tools to the PATH variable
  Section "Setup Path Environment" PathSection
    Push "$INSTDIR\bin"
    Call AddToPath
    Push "$INSTDIR\dist\mingw"
    Call AddToPath
    Push "$INSTDIR\dist\mingw\bin"
    Call AddToPath
    Push "$PROFILE\.nimble\bin"
    Call AddToPath
  SectionEnd

  ; The downloadable sections. These sections are automatically generated by
  ; niminst and the template filters.
  #var i = 0
  #for download in c.downloads:
  #  inc i
  #  let d = download.split('|')
  #  if d.len != 5 and d.len != 6:
  #    quit("download string needs 5..6 parts: " & download)
  #  end if
  #  let sectionName = d[0]
  #  let dir = d[1]
  #  let zipName = d[2]
  #  let size = d[3]
  #  let url = d[4]
  Section /o "?sectionName" ?{i}Section
    ; Add the section size to the total size.
    AddSize ?size

    ; Download the file, and if successful, extract it to the given directory
    ; otherwise,
    retry:
    NSISdl::download "?url" "$TEMP\?zipName"
    Pop $0
    ${If} $0 == "success"
      ZipDLL::extractall "$TEMP\?zipName" "$INSTDIR\?dir"
      Delete "$TEMP\?zipName"
    ${ElseIf} $0 == "cancel"
      MessageBox MB_ICONQUESTION|MB_YESNO|MB_TOPMOST \
                 "Download of component '?sectionName' cancelled. Continue installation process??" \
                 IDYES ignore
      abort
    ${Else}
      MessageBox MB_ICONSTOP|MB_ABORTRETRYIGNORE|MB_TOPMOST "Error: $0" \
                 IDRETRY retry IDIGNORE ignore
      abort
    ${EndIf}

    ; Shortcuts
    #  if d.len >= 6:
    #    let startMenuEntry = d[5]
    #    let e = splitFile(startMenuEntry).name.capitalizeAscii
      !insertmacro MUI_STARTMENU_WRITE_BEGIN Application
        CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\?{e}.lnk" "$INSTDIR\?dir\?{startMenuEntry.toWin}"
      !insertmacro MUI_STARTMENU_WRITE_END
    #  end if

    ignore:
  SectionEnd
  #end

;--------------------------------
; Section Descriptions
  ; Series of strings describing each section
  ; LangString DESC_CoreSection ${LANG_ENGLISH} "Core Nim files"

  ; The macros to actually insert the descriptions into the sections.
  ; Each description above should have a corresponding MUI_DESCRIPTION_TEXT
  ; macro linking the section to the description.
  ; !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
  ;   !insertmacro MUI_DESCRIPTION_TEXT ${CoreSection} $(DESC_CoreSection)
  ; !insertmacro MUI_FUNCTION_DESCRIPTION_END


;--------------------------------
; Uninstaller Sections

  Section "Uninstall"
    ; Remove previously created shortcuts
    !insertmacro MUI_STARTMENU_GETFOLDER "Application" $ICONS_GROUP
    Delete "$DESKTOP\?{c.displayName}.lnk"

    ; Remove installed application files
    RMDir /r "$SMPROGRAMS\$ICONS_GROUP"
    RMDir /r "$INSTDIR"

    ; Remove the previously created registry key
    DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
    DeleteRegKey HKCU "${PRODUCT_DIR_REGKEY}"
    SetAutoClose true

    ; Remove entries from the PATH environment variable
    Push "$INSTDIR\bin"
    Call un.RemoveFromPath
    Push "$INSTDIR\dist\mingw"
    Call un.RemoveFromPath
    Push "$INSTDIR\dist\mingw\bin"
    Call un.RemoveFromPath
    Push "$PROFILE\.nimble\bin"
    Call un.RemoveFromPath
  SectionEnd

;--------------------------------
; Function hooks

  Function .onInit
    ${GetRoot} "$EXEDIR" $R0
    strCpy $INSTDIR "$R0\?{c.name}"
  FunctionEnd


;--------------------------------------------------------------------
; Path functions
;
; Based on example from:
; http://nsis.sourceforge.net/Path_Manipulation
;
; Actually based on:
; https://www.smartmontools.org/browser/trunk/smartmontools/os_win32/installer.nsi#L636


!include "WinMessages.nsh"

; Registry Entry for environment (NT4,2000,XP)
; All users:
;!define Environ 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"'
; Current user only:
!define Environ 'HKCU "Environment"'


; AddToPath - Appends dir to PATH
;   (does not work on Win9x/ME)
;
; Usage:
;   Push "dir"
;   Call AddToPath

Function AddToPath
  Exch $0
  Push $1
  Push $2
  Push $3
  Push $4

  ; NSIS ReadRegStr returns empty string on string overflow
  ; Native calls are used here to check actual length of PATH

  ; $4 = RegOpenKey(HKEY_CURRENT_USER, "Environment", &$3)
  System::Call "advapi32::RegOpenKey(i 0x80000001, t'Environment', *i.r3) i.r4"
  IntCmp $4 0 0 done done
  ; $4 = RegQueryValueEx($3, "PATH", (DWORD*)0, (DWORD*)0, &$1, ($2=NSIS_MAX_STRLEN, &$2))
  ; RegCloseKey($3)
  System::Call "advapi32::RegQueryValueEx(i $3, t'PATH', i 0, i 0, t.r1, *i ${NSIS_MAX_STRLEN} r2) i.r4"
  System::Call "advapi32::RegCloseKey(i $3)"

  IntCmp $4 234 0 +4 +4 ; $4 == ERROR_MORE_DATA
    DetailPrint "AddToPath: original length $2 > ${NSIS_MAX_STRLEN}"
    MessageBox MB_OK "PATH not updated, original length $2 > ${NSIS_MAX_STRLEN}"
    Goto done

  IntCmp $4 0 +5 ; $4 != NO_ERROR
    IntCmp $4 2 +3 ; $4 != ERROR_FILE_NOT_FOUND
      DetailPrint "AddToPath: unexpected error code $4"
      Goto done
    StrCpy $1 ""

  ; Check if already in PATH
  Push "$1;"
  Push "$0;"
  Call StrStr
  Pop $2
  StrCmp $2 "" 0 done
  Push "$1;"
  Push "$0\;"
  Call StrStr
  Pop $2
  StrCmp $2 "" 0 done

  ; Prevent NSIS string overflow
  StrLen $2 $0
  StrLen $3 $1
  IntOp $2 $2 + $3
  IntOp $2 $2 + 2 ; $2 = strlen(dir) + strlen(PATH) + sizeof(";")
  IntCmp $2 ${NSIS_MAX_STRLEN} +4 +4 0
    DetailPrint "AddToPath: new length $2 > ${NSIS_MAX_STRLEN}"
    MessageBox MB_OK "PATH not updated, new length $2 > ${NSIS_MAX_STRLEN}."
    Goto done

  ; Append dir to PATH
  DetailPrint "Add to PATH: $0"
  StrCpy $2 $1 1 -1
  StrCmp $2 ";" 0 +2
    StrCpy $1 $1 -1 ; remove trailing ';'
  StrCmp $1 "" +2   ; no leading ';'
    StrCpy $0 "$1;$0"
  WriteRegExpandStr ${Environ} "PATH" $0
  SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000

done:
  Pop $4
  Pop $3
  Pop $2
  Pop $1
  Pop $0
FunctionEnd


; RemoveFromPath - Removes dir from PATH
;
; Usage:
;   Push "dir"
;   Call RemoveFromPath

Function un.RemoveFromPath
  Exch $0
  Push $1
  Push $2
  Push $3
  Push $4
  Push $5
  Push $6

  ReadRegStr $1 ${Environ} "PATH"
  StrCpy $5 $1 1 -1
  StrCmp $5 ";" +2
    StrCpy $1 "$1;" ; ensure trailing ';'
  Push $1
  Push "$0;"
  Call un.StrStr
  Pop $2 ; pos of our dir
  StrCmp $2 "" done

  DetailPrint "Remove from PATH: $0"
  StrLen $3 "$0;"
  StrLen $4 $2
  StrCpy $5 $1 -$4 ; $5 is now the part before the path to remove
  StrCpy $6 $2 "" $3 ; $6 is now the part after the path to remove
  StrCpy $3 "$5$6"
  StrCpy $5 $3 1 -1
  StrCmp $5 ";" 0 +2
    StrCpy $3 $3 -1 ; remove trailing ';'
  WriteRegExpandStr ${Environ} "PATH" $3
  SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000

done:
  Pop $6
  Pop $5
  Pop $4
  Pop $3
  Pop $2
  Pop $1
  Pop $0
FunctionEnd


; StrStr - find substring in a string
;
; Usage:
;   Push "this is some string"
;   Push "some"
;   Call StrStr
;   Pop $0 ; "some string"

!macro StrStr un
Function ${un}StrStr
  Exch $R1 ; $R1=substring, stack=[old$R1,string,...]
  Exch     ;                stack=[string,old$R1,...]
  Exch $R2 ; $R2=string,    stack=[old$R2,old$R1,...]
  Push $R3
  Push $R4
  Push $R5
  StrLen $R3 $R1
  StrCpy $R4 0
  ; $R1=substring, $R2=string, $R3=strlen(substring)
  ; $R4=count, $R5=tmp
  loop:
    StrCpy $R5 $R2 $R3 $R4
    StrCmp $R5 $R1 done
    StrCmp $R5 "" done
    IntOp $R4 $R4 + 1
    Goto loop
done:
  StrCpy $R1 $R2 "" $R4
  Pop $R5
  Pop $R4
  Pop $R3
  Pop $R2
  Exch $R1 ; $R1=old$R1, stack=[result,...]
FunctionEnd
!macroend
!insertmacro StrStr ""
!insertmacro StrStr "un."
