EnableExplicit

XIncludeFile #IncludePath + "GetOpenSavePatcher.pb"
XIncludeFile #IncludePath + "LocalAlloc_FromString.pb"
XIncludeFile #IncludePath + "GetDlgItemText.pb"
XIncludeFile #IncludePath + "FormatMessage.pb"

Structure GetSaveFilename_CustomParams
  ErrExistsTitle.s
  ErrExistsText.s
  ErrROTitle.s
  ErrROText.s
  EndStructure

Structure GetSaveFilename_CustomParamsInt
  Filters.i
  ;LenFilters.i
  ErrExistsTitle.i
  ErrExistsText.i
  ErrROTitle.i
  ErrROText.i
  RealResult.i
  EndStructure

Procedure.i GetSaveFileName_FindFilterIndex(*ISED.GetSaveFilename_CustomParamsInt, Extension.s)
  Define.i xResult
  Define.i xLong1
  Define.i xLong2
  Define.i xLong3
  Define.s xChar1
  Define.s xChar2
  
  If *ISED
    If *ISED\Filters
      xChar1 = PeekS(*ISED\Filters)
      Repeat
        xLong1 = FindString(xChar1, "|", xLong2 + 1)
        If xLong1 = 0
          Break
          EndIf
        xLong2 = FindString(xChar1, "|", xLong1 + 1)
        If xLong2 = 0
          Break
          EndIf
        xLong3 + 1
        xChar2 = Mid(xChar1, xLong1 + 1, xLong2 - xLong1 - 1) + ";"
        If FindString(xChar2, "*." + Extension + ";")
          xResult = xLong3
          Break
          EndIf
        ForEver
      EndIf
    EndIf
  ProcedureReturn xResult
  EndProcedure

Procedure.s GetSaveFileName_ExtensionByIndex(*ISED.GetSaveFilename_CustomParamsInt, Index.i)
  Define.s xResult
  Define.i xLong1
  Define.i xLong2
  Define.s xChar1
  
  If *ISED And Index > 0
    If *ISED\Filters
      xChar1 = PeekS(*ISED\Filters)
      If Len(xChar1)
        Repeat
          xLong1 = FindString(xChar1, "|", xLong2 + 1)
          If xLong1 = 0
            Break
            EndIf
          xLong2 = FindString(xChar1, "|", xLong1 + 1)
          If xLong2 = 0
            Break
            EndIf
          Index - 1
          If Index = 0
            xChar1 = Mid(xChar1, xLong1 + 1, xLong2 - xLong1 - 1) + ";"
            xChar1 = Left(xChar1, FindString(xChar1, ";") - 1)
            If Left(xChar1, 2) = "*."
              xResult = Mid(xChar1, 3)
              EndIf
            Break
            EndIf
          ForEver
        EndIf
      EndIf
    EndIf
  ProcedureReturn xResult
  EndProcedure

Procedure.i GetSaveFileName_HookHandle(hDlgChild.i, *ptrNMHDR.OFNOTIFY)
  Define.i xLong1
  Define.i xLong2
  Define *xType2.OPENFILENAME
  Define *xType3.GetSaveFilename_CustomParamsInt
  Define.s xChar1
  Define.s xChar2
  
;Public Const FILE_ATTRIBUTE_READONLY = &H1
;Public Const FILE_ATTRIBUTE_HIDDEN = &H2
;Public Const FILE_ATTRIBUTE_SYSTEM = &H4
;Public Const FILE_ATTRIBUTE_DIRECTORY = &H10
  
  ; 4*4 = filters
  ; 7*4 = index
  If *ptrNMHDR
    If *ptrNMHDR\hdr\code = -607
      *xType2 = *ptrNMHDR\lpOFN
      If *xType2
        *xType2 = *ptrNMHDR\lpOFN
        If *xType2\lpstrFilter And *xType2\lCustData
          *xType3 = *xType2\lCustData
          hDlgChild = GetParent_(hDlgChild)
          If hDlgChild
            xChar1 = GetDlgItemText(hDlgChild, $480)
            If Len(xChar1)
              xLong1 = InStrRevChar(xChar1, 46) ; "."
              If xLong1
                xLong2 = GetSaveFileName_FindFilterIndex(*xType3, Mid(xChar1, xLong1 + 1))
                If xLong2 And Not (xLong2 = *xType2\nFilterIndex)
                  xChar1 = Left(xChar1, xLong1 - 1)
                  SendMessage_(hDlgChild, 1128, $480, @xChar1)
                  EndIf
                EndIf
              EndIf
            EndIf
          EndIf
        EndIf
      ElseIf *ptrNMHDR\hdr\code = -606
      If *ptrNMHDR\lpOFN
        *xType2 = *ptrNMHDR\lpOFN
        If *xType2\lpstrFile And *xType2\lCustData
          *xType3 = *xType2\lCustData
          xChar1 = PeekS(*xType2\lpstrFile)
          If Len(xChar1)
            ; check attributes
            xLong1 = InStrRevChar(xChar1, 46) ; "."
            If xLong1
              If GetSaveFileName_FindFilterIndex(*xType3, Mid(xChar1, xLong1 + 1))
                Goto FilenameComplete
                EndIf
              EndIf
            xChar1 = xChar1 + "." + GetSaveFileName_ExtensionByIndex(*xType3, *xType2\nFilterIndex)
FilenameComplete:
            xLong1 = GetFileAttributes(xChar1)
            If (xLong1 & $ffffffff) = $ffffffff
              Goto Ok
              EndIf
            If (xLong1 & $17) = 0
              xChar2 = ReplaceString(PeekS(*xType3\ErrExistsText), "$F", xChar1)
              If MessageBox_(GetParent_(hDlgChild), @xChar2, *xType3\ErrExistsTitle, #MB_OKCANCEL | #MB_ICONEXCLAMATION) = #IDOK
Ok:
                If *xType3\RealResult
                  LocalFree_(*xType3\RealResult)
                  EndIf
                *xType3\RealResult = LocalAlloc_FromString(xChar1, Len(xChar1))
                ProcedureReturn 0
                EndIf
              Else
              xChar2 = ReplaceString(PeekS(*xType3\ErrROText), "$F", xChar1)
              MessageBox_(GetParent_(hDlgChild), @xChar2, *xType3\ErrROTitle, #MB_ICONEXCLAMATION)
              EndIf
            EndIf
          EndIf
        ;Debug.Print "Result:"; xChar1
        EndIf
      EndIf
    
    SetWindowLong_(hDlgChild, 0, 1)
    ProcedureReturn 1
    EndIf
  EndProcedure

Procedure.i GetSaveFileName_Hook(hDlgChild.i, Msg.i, wParam.i, lParam.i)
  If Msg = $4E ; WM_Notify
    ProcedureReturn GetSaveFileName_HookHandle(hDlgChild, lParam)
    EndIf
  ProcedureReturn 0
  EndProcedure

Procedure.s GetSaveFileName(hWnd.i, Title.s, Filename.s, Patterns.s, *SpParams.GetSaveFilename_CustomParams)
  Define.i xLong1
  Define.i xLong2
  Define.i xLong3
  Define.i xLong4
  Define.s xChar2
  Define.s xChar4
  Define.OPENFILENAME xType2
  Define.GetSaveFilename_CustomParamsInt xType3
  
  If *SpParams
    xLong1 = Len(*SpParams\ErrExistsText)
    xLong2 = Len(*SpParams\ErrExistsTitle)
    xLong3 = Len(*SpParams\ErrROText)
    xLong4 = Len(*SpParams\ErrROTitle)
    EndIf

  If Right(Patterns, 1) = "|"
    Patterns = Left(Patterns, Len(Patterns) - 1)
    EndIf

  With xType3
    If xLong1
      \ErrExistsText = @*SpParams\ErrExistsText
      Else
      xChar2 = FormatMessage($1200, 0, 80)
      If Len(xChar2)
        \ErrExistsText = @xChar2
        EndIf
      EndIf
    If xLong2
      \ErrExistsTitle = @*SpParams\ErrExistsTitle
      EndIf
    If xLong3
      \ErrROText = @*SpParams\ErrROText
      Else
      xChar4 = FormatMessage($1200, 0, 82)
      If Len(xChar4)
        \ErrROText = @xChar4
        EndIf
      EndIf
    If xLong4
      \ErrROTitle = @*SpParams\ErrROTitle
      EndIf
    EndWith
    
  GetOpenSavePatcher_Patch(@xType2, hWnd, Filename, Patterns)
  Patterns + "|"
  ;xType3\LenFilters = Len(Patterns)
  xType3\Filters = @Patterns
  xType2\lCustData = @xType3
  With xType2
    If \lpstrFile
      Filename = PeekS(\lpstrFile)
      xLong1 = InstrRevChar(Filename, 92)
      If xLong1
        Filename = Mid(Filename, xLong1 + 1)
        EndIf
      xLong1 = InstrRevChar(Filename, 46) ; "."
      If xLong1
        xLong2 = GetSaveFileName_FindFilterIndex(@xType3, Mid(Filename, xLong1 + 1))
        If xLong2
          \nFilterIndex = xLong2
          PokeS(\lpstrFile, Left(Filename, xLong1 - 1))
          EndIf
        EndIf
      EndIf

    \lpfnHook = @GetSaveFileName_Hook()
    \Flags = #OFN_ENABLEHOOK | #OFN_HIDEREADONLY | #OFN_EXPLORER | #OFN_ENABLESIZING
    EndWith
  Filename = ""
  xLong1 = GetSaveFileName_(@xType2)
  If xLong1
    If xType3\RealResult
      Filename = PeekS(xType3\RealResult)
      EndIf
    EndIf
  ;xType1.FilterIndex = xType2.nFilterIndex
  GetOpenSavePatcher_Free(@xType2)
  If xType3\RealResult
    LocalFree_(xType3\RealResult)
    EndIf
  ProcedureReturn Filename
  EndProcedure

; IDE Options = PureBasic 5.21 LTS (Windows - x64)
; CursorPosition = 250
; FirstLine = 241
; Folding = -
; EnableUnicode
; EnableXP