
Imports System.Net
Imports System.Net.WebRequestMethods
Imports System.IO

Imports FluentFTP


Module Utils_FTP

    Friend Working As Boolean = False
    Friend AbortFlag As Boolean
    Friend Delegate Sub ProgressCallBack(ByVal params As ProgressParams)

    Friend Structure ProgressParams
        Dim progress As Double
        Dim fileLength As Long
        Dim bytesRead As Int32
        Dim bytesTransferred As Long
        Dim secondsTotal As Double
        Dim secondsElapsed As Double
        Dim secondsRemaining As Double
        Dim kbps As Double
        Dim timeStart As Date
        Dim elapsedTime As Double
        Dim totalTime As Double
    End Structure

    Friend Structure FileParams
        Dim CompletePath As String
        Dim FileName As String
        Dim Length As Long
        Dim Type As String
        Dim Modified As String
        Dim users As String
        Dim permissions As String
    End Structure

    ' ===================================================================================================
    '   FTP_ListDirectoryDetails - Get directory listing with FluentFTP (UTF-8 support)
    ' ===================================================================================================
    Friend Function FTP_ListDirectoryDetails(ByVal ServerDirectory As String, _
                                             ByVal UserName As String, _
                                             ByVal Password As String) As List(Of FileParams)

        Dim items As New List(Of FileParams)

        If Working Then Return items
        Working = True
        Application.UseWaitCursor = True
        Cursor.Current = Cursors.WaitCursor

        Try
            ' Normalize the directory path
            Dim normalizedDir As String = ServerDirectory
            If Not normalizedDir.EndsWith("/") Then
                normalizedDir &= "/"
            End If

            Dim relativePath As String = FtpPathToRelative(normalizedDir)

            Using client As New FtpClient("ftp.theremino.com", UserName, Password)
                '
                ' Force standard passive modality (PASV) instead of EPSV
                client.DataConnectionType = FtpDataConnectionType.PASV
                client.Encoding = System.Text.Encoding.UTF8
                '
                Dim listing As FtpListItem() = client.GetListing(relativePath)

                For Each item As FtpListItem In listing
                    Dim fp As New FileParams()
                    fp.FileName = item.Name
                    fp.CompletePath = normalizedDir & item.Name
                    fp.Length = item.Size
                    fp.Modified = item.Modified.ToString("g")

                    If item.Type = FtpFileSystemObjectType.Directory Then
                        fp.Type = "dir"
                    Else
                        fp.Type = "file"
                    End If

                    fp.permissions = ""
                    fp.users = ""

                    If fp.FileName <> "." AndAlso fp.FileName <> ".." Then
                        items.Add(fp)
                    End If
                Next
            End Using

        Catch ex As Exception
            MsgBox("FTP Error: " & ex.Message)
        End Try

        Application.UseWaitCursor = False
        Cursor.Current = Cursors.Default
        Working = False
        Return items
    End Function


    ' ===================================================================================================
    '   UpdateFolderStats
    ' ===================================================================================================
    Friend Sub UpdateFolderStats(ByVal items As List(Of FileParams))
        ' ----------------------------------------------------------------- Initialize counters
        Dim totFolders As Integer = 0
        Dim totFiles As Integer = 0
        ' ----------------------------------------------------------------- Loop through items to count
        For Each item As FileParams In items
            If item.Type = "dir" Then
                totFolders += 1
            Else
                totFiles += 1
            End If
        Next
        ' ----------------------------------------------------------------- Update the status label
        Form_Main.GroupBox_Advancement.Text = totFolders & " Folders / " & totFiles & " Files"
    End Sub

   


    ' ===================================================================================================
    '   FTP_Upload - Upload file with FluentFTP (UTF-8 support)
    ' ===================================================================================================
    Friend Sub FTP_Upload(ByVal LocalFile As String, _
                           ByVal ServerFile As String, _
                           ByVal UserName As String, _
                           ByVal Password As String, _
                           Optional ByVal Callback As ProgressCallBack = Nothing)

        AbortFlag = False

        If Working Then Exit Sub
        Working = True
        Application.UseWaitCursor = True
        Cursor.Current = Cursors.WaitCursor

        Try
            ' Convert FTP URL to relative path for the server
            Dim relativePath As String = FtpPathToRelative(ServerFile)

            Using client As New FtpClient("ftp.theremino.com", UserName, Password)
                '
                ' Force standard passive modality (PASV) instead of EPSV
                client.DataConnectionType = FtpDataConnectionType.PASV
                client.Encoding = System.Text.Encoding.UTF8

                Dim status As FtpStatus = client.UploadFile(LocalFile, relativePath, FtpRemoteExists.Overwrite, True)

                If status <> FtpStatus.Success Then
                    MsgBox("Upload failed with status: " & status.ToString())
                End If
            End Using

        Catch ex As Exception
            Dim msg As String = "FTP Error: " & ex.Message
            If ex.InnerException IsNot Nothing Then
                msg &= vbCrLf & vbCrLf & "Inner: " & ex.InnerException.Message
            End If
            MsgBox(msg)
        End Try

        Application.UseWaitCursor = False
        Cursor.Current = Cursors.Default
        Working = False
    End Sub

    ' ===================================================================================================
    '   FTP_Download - Download file with FluentFTP (UTF-8 support)
    ' ===================================================================================================
    Friend Sub FTP_Download(ByVal LocalFile As String, _
                            ByVal ServerFile As String, _
                            ByVal UserName As String, _
                            ByVal Password As String, _
                            Optional ByVal Length As Long = -1, _
                            Optional ByVal Callback As ProgressCallBack = Nothing)


        AbortFlag = False

        If Working Then Exit Sub
        Working = True
        Application.UseWaitCursor = True
        Cursor.Current = Cursors.WaitCursor

        Try
            Dim relativePath As String = FtpPathToRelative(ServerFile)

            Using client As New FtpClient("ftp.theremino.com", UserName, Password)
                '
                ' Force standard passive modality (PASV) instead of EPSV
                client.DataConnectionType = FtpDataConnectionType.PASV
                client.Encoding = System.Text.Encoding.UTF8

                ' DownloadFile(localPath, remotePath, existsMode)
                Dim status As FtpStatus = client.DownloadFile(LocalFile, relativePath, FtpLocalExists.Overwrite)

                If status <> FtpStatus.Success Then
                    MsgBox("Download failed with status: " & status.ToString())
                End If
            End Using

        Catch ex As Exception
            Dim msg As String = "FTP Error: " & ex.Message
            If ex.InnerException IsNot Nothing Then
                msg &= vbCrLf & vbCrLf & "Inner: " & ex.InnerException.Message
            End If
            MsgBox(msg)
        End Try

        Application.UseWaitCursor = False
        Cursor.Current = Cursors.Default
        Working = False
    End Sub


    ' ===================================================================================================
    '   HTTP_Download
    ' ===================================================================================================
    Friend Sub HTTP_Download(ByVal LocalFile As String, _
                             ByVal ServerFile As String, _
                             Optional ByVal Callback As ProgressCallBack = Nothing)

        AbortFlag = False

        If Working Then Exit Sub
        Working = True
        Application.UseWaitCursor = True
        Cursor.Current = Cursors.WaitCursor

        Dim params As ProgressParams
        Dim WebRequest As WebRequest = Nothing
        Dim WebResponse As WebResponse = Nothing
        Dim HttpStream As Stream = Nothing
        Dim LocaleFileStream As FileStream = Nothing

        With params

            Try
                ' --------------------------------------------------------------------- init buffer
                Dim bytesPerBlock As Integer = 4096
                Dim buffer(bytesPerBlock - 1) As Byte

                ' --------------------------------------------------------------------- WEB request
                WebRequest = WebRequest.Create(ServerFile)

                ' --------------------------------------------------------------------- Set security protocol to Tls12
                ServicePointManager.SecurityProtocol = DirectCast(3072, SecurityProtocolType)

                ' --------------------------------------------------------------------- WEB response
                WebResponse = WebRequest.GetResponse

                ' --------------------------------------------------------------------- get the file length
                Try
                    .fileLength = WebResponse.ContentLength
                Catch ex As Exception
                    .fileLength = 0
                End Try

                ' --------------------------------------------------------------------- get the HTTP stream
                HttpStream = WebResponse.GetResponseStream()

                ' --------------------------------------------------------------------- open the locale file stream
                LocaleFileStream = New FileStream(LocalFile, _
                                                  FileMode.OpenOrCreate, _
                                                  FileAccess.Write)

                ' --------------------------------------------------------------------- start the read loop
                .bytesTransferred = 0
                .timeStart = Date.Now
                Dim oldseconds As Double = 0
                Do
                    ' ----------------------------------------------------------------- read from ftp
                    .bytesRead = HttpStream.Read(buffer, 0, buffer.Length)

                    If .bytesRead > 0 Then

                        ' ------------------------------------------------------------- update total bytes
                        .bytesTransferred += .bytesRead

                        ' ------------------------------------------------------------- write to local file
                        LocaleFileStream.Write(buffer, 0, .bytesRead)

                        ' ------------------------------------------------------------- update progress
                        If Callback IsNot Nothing Then
                            If .fileLength > 0 Then .progress = (.bytesTransferred * 100) \ .fileLength
                            .secondsElapsed = (Date.Now - .timeStart).TotalSeconds
                            .secondsTotal += 0.3 * (.secondsElapsed * .fileLength / .bytesTransferred - .secondsTotal)
                            .secondsRemaining = .secondsTotal - .secondsElapsed
                            If .secondsTotal > 0 Then .kbps = 8 / 1000 * .fileLength / .secondsTotal
                            If .secondsElapsed >= oldseconds + 0.1 Then
                                oldseconds = .secondsElapsed
                                Callback(params)
                            End If
                        End If
                    End If

                    If AbortFlag Then
                        WebRequest.Abort()
                        Exit Do
                    End If

                    Application.DoEvents()

                Loop While .bytesRead > 0


            Catch ex As Exception
                MsgBox(ex.Message)

            Finally
                If Callback IsNot Nothing Then
                    .progress = 0
                    Callback(params)
                End If
                If LocaleFileStream IsNot Nothing Then LocaleFileStream.Close()
                If HttpStream IsNot Nothing Then HttpStream.Close()
                If WebResponse IsNot Nothing Then WebResponse.Close()

            End Try

        End With

        Application.UseWaitCursor = False
        Cursor.Current = Cursors.Default
        Working = False
    End Sub


    ' ===================================================================================================
    '   FTP_DeleteFile - Delete file with FluentFTP (UTF-8 support)
    ' ===================================================================================================
    Friend Sub FTP_DeleteFile(ByVal FileName_WithPath As String, _
                              ByVal UserName As String, _
                              ByVal Password As String)

        If Working Then Exit Sub
        Working = True
        Application.UseWaitCursor = True
        Cursor.Current = Cursors.WaitCursor

        Try
            Dim relativePath As String = FtpPathToRelative(FileName_WithPath)

            Using client As New FtpClient("ftp.theremino.com", UserName, Password)
                '
                ' Force standard passive modality (PASV) instead of EPSV
                client.DataConnectionType = FtpDataConnectionType.PASV
                client.Encoding = System.Text.Encoding.UTF8
                client.DeleteFile(relativePath)
            End Using

        Catch ex As Exception
            MsgBox("FTP Error: " & ex.Message)
        End Try

        Application.UseWaitCursor = False
        Cursor.Current = Cursors.Default
        Working = False
    End Sub


    ' ===================================================================================================
    '   FTP_RemoveDirectory - Delete directory with FluentFTP (UTF-8 support)
    ' ===================================================================================================
    Friend Sub FTP_RemoveDirectory(ByVal DirectoryPath As String, _
                                   ByVal UserName As String, _
                                   ByVal Password As String)

        If working Then Exit Sub
        working = True
        Application.UseWaitCursor = True
        Cursor.Current = Cursors.WaitCursor

        Try
            Dim relativePath As String = FtpPathToRelative(DirectoryPath)

            Using client As New FtpClient("ftp.theremino.com", UserName, Password)
                '
                ' Force standard passive modality (PASV) instead of EPSV
                client.DataConnectionType = FtpDataConnectionType.PASV
                client.Encoding = System.Text.Encoding.UTF8
                client.DeleteDirectory(relativePath, FtpListOption.Recursive)
            End Using

        Catch ex As Exception
            MsgBox("FTP Error: " & ex.Message)
        End Try

        Application.UseWaitCursor = False
        Cursor.Current = Cursors.Default
        working = False
    End Sub


    ' ===================================================================================================
    '   FTP_MakeDirectory - Create directory with FluentFTP (UTF-8 support)
    ' ===================================================================================================
    Friend Sub FTP_MakeDirectory(ByVal Directory As String, _
                                 ByVal UserName As String, _
                                 ByVal Password As String)

        If working Then Exit Sub
        working = True
        Application.UseWaitCursor = True
        Cursor.Current = Cursors.WaitCursor

        Try
            Dim relativePath As String = FtpPathToRelative(Directory)

            Using client As New FtpClient("ftp.theremino.com", UserName, Password)
                '
                ' Force standard passive modality (PASV) instead of EPSV
                client.DataConnectionType = FtpDataConnectionType.PASV
                client.Encoding = System.Text.Encoding.UTF8
                client.CreateDirectory(relativePath, True) ' Create parent directories if needed
            End Using

        Catch ex As Exception
            ' Ignore errors for existing directories
        End Try

        Application.UseWaitCursor = False
        Cursor.Current = Cursors.Default
        working = False
    End Sub


    ' ===================================================================================================
    '   FTP_RenameFile - Rename file with FluentFTP (UTF-8 support)
    ' ===================================================================================================
    Friend Sub FTP_RenameFile(ByVal OldFileName_WithPath As String, _
                              ByVal NewFileName_WithoutPath As String, _
                              ByVal UserName As String, _
                              ByVal Password As String)

        If working Then Exit Sub
        working = True
        Application.UseWaitCursor = True
        Cursor.Current = Cursors.WaitCursor

        Try
            Dim oldRelativePath As String = FtpPathToRelative(OldFileName_WithPath)

            ' Get the directory from the old path
            Dim dirPath As String = System.IO.Path.GetDirectoryName(oldRelativePath).Replace("\", "/")
            Dim newRelativePath As String = dirPath & "/" & NewFileName_WithoutPath

            Using client As New FtpClient("ftp.theremino.com", UserName, Password)
                '
                ' Force standard passive modality (PASV) instead of EPSV
                client.DataConnectionType = FtpDataConnectionType.PASV
                client.Encoding = System.Text.Encoding.UTF8
                client.Rename(oldRelativePath, newRelativePath)
            End Using

        Catch ex As Exception
            MsgBox("FTP Error: " & ex.Message)
        End Try

        Application.UseWaitCursor = False
        Cursor.Current = Cursors.Default
        working = False
    End Sub

End Module
