compile function rti_HTTP_Download_UI( object, method, param1, param2, param3, param4, param5, param6, param7, param8 )
/*
   ** Copyright (C) 2012-2019 Revelation Software Inc. All Rights Reserved **

   Author   : Captain C
   Date     : June 2019
   Purpose  : Commuter module for the RTI_HTTP_DOWNLOAD_UI form


   Comments
   ========


   Amended  Date       Reason
   =======  ====       ======
   Mr C     09 Mar 22  The form's visibility is controlled from it's caller
                       (i.e. rti_HTTP_Download)
*/
   #pragma precomp event_precomp

   declare function get_Property, retStack, rti_Res2Str, rti_ErrorText
   declare function rti_Convert, msWin_GetTickCount64, exec_Method
   $insert rti_HTTP_Download_Equates
   $insert ps_HTTPClient_Equates
   $insert rti_Resources_Equates
   $insert rti_SSP_Equates
   $insert logical
   
   equ UDP_TIMEDATA$ to "@_TIMEDATA"
   
   if assigned( object ) else object = ""
   if assigned( method ) else method = ""
   if assigned( param1 ) else param1 = ""
   if assigned( param2 ) else param2 = ""
   if assigned( param3 ) else param3 = ""
   if assigned( param4 ) else param4 = ""
   if assigned( param5 ) else param5 = ""
   if assigned( param6 ) else param6 = ""
   if assigned( param7 ) else param7 = ""
   if assigned( param8 ) else param8 = ""
   
   errorText = ""
   abort     = FALSE$
   retVal    = ""
   
   atWindow  = object[1,"."]
   atCtrl    = object[col2()+1,\00\]
   if bLen( atWindow ) else
      atWindow = @window
   end

   if bLen( method ) then
      locate method in "CREATE,CLOSE,PROGRESS,READYSTATECHANGED,TIMEOUT" using "," setting pos then
         on pos goSub onCreate,onClose,onProgress,onReadyStateChanged,onTimeout
      end else
         // ERR002: Invalid method "%1% passed to the %2% procedure
         errorText = rti_Res2Str( RESID$, "ERR002", method : @fm : retStack()<1> )
         abort     =  TRUE$
      end
   end else
      // ERR001: No method passed to the %1% procedure
      errorText = rti_Res2Str( RESID$, "ERR001", retStack()<1> )
      abort     =  TRUE$
   end

return retVal

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// onClose subroutine
//
// CLOSE event handler for the 
//
// ----------------------------------------------------------------------------
onClose:

   rs = .hcl_Download->readyState
   begin case
      case ( rs == PS_HCL_RS_UNSENT$ )
         null
      case ( rs == PS_HCL_RS_DONE$ )
         null
      case OTHERWISE$
         @@window->$@_ABORT = TRUE$
         .hcl_Download->abort( "" )
         goSub hclDownload_ReturnResponse 
   end case

return

///////////////////////////////////////////////////////////////////////////////
// onCreate subroutine
//
// CREATE event handler 
//
// ----------------------------------------------------------------------------
// [i] param1 : CreateParam. Contains an @fm delimited list UI information 
//            : as passed to the RTI_HTTP_DOWNLOAD proc
// ----------------------------------------------------------------------------
onCreate:

   @@window->$@_CREATEPARAM = param1

   startTime = msWin_GetTickCount64()
   prevTime  = startTime
   timeData  = startTime : @fm : prevTime
      
   @@window->$@_TIMEDATA    = timeData
   
   if ( param1<HDL_UIPARAM_POS_SYNCPROGRESS$> ) then
      .prb_Download->SyncTaskBar = TRUE$
   end
   
   // The form's visibility is controlled from rti_HTTP_Download()
   // @atWindow->visible = TRUE$
   
   if ( param1<HDL_UIPARAM_POS_MODAL$> ) then
      call set_Property( param1<HDL_UIPARAM_POS_PARENTWIN$>, "ENABLED", FALSE$ )
   end
   
   // We're all set - return to the caller to set the HCL_DOWNLOAD properties  
   // execute the OPEN/SEND methods ...
   
return

///////////////////////////////////////////////////////////////////////////////
// onProgress subroutine
//
// PROGRESS event dispatch handler
//
// ----------------------------------------------------------------------------
// [i] param1 : bytesReceived. Number of bytes received between this PROGRESS 
//            : event and the previous one   
// [i] param2 : bytesDownloaded. Total Number of bytes downloaded so far
// [i] param3 : bytesExpected. Total number of bytes expected from the server
// ----------------------------------------------------------------------------
onProgress:

   bytesReceived   = param1
   bytesDownloaded = param2
   bytesExpected   = param3
   
   begin case
      case ( atCtrl == "HCL_DOWNLOAD" )
         goSub hclDownload_OnProgress
   end case
   
return

///////////////////////////////////////////////////////////////////////////////
// onReadyStateChanged subroutine
//
// READYSTATECHANGED event dispatch handler
//
// ----------------------------------------------------------------------------
// [i] param1 : newState
// ----------------------------------------------------------------------------
onReadyStateChanged:

   newState = param1
   
   begin case
      case ( atCtrl == "HCL_DOWNLOAD" )
         goSub hclDownload_OnReadyStateChanged
   end case
   
return

///////////////////////////////////////////////////////////////////////////////
// onTimeout subroutine
//
// TIMEOUT event dispatch handler
//
// ----------------------------------------------------------------------------
// [i] param1 : StatusID, identifies the handle that timed out (connect, open,
//            : request)
// ----------------------------------------------------------------------------
onTimeout:

   statusID = param1
   
   begin case
      case ( atCtrl == "HCL_DOWNLOAD" )
         goSub hclDownload_OnTimeout
   end case
   
return

///////////////////////////////////////////////////////////////////////////////
#region HCL_DOWNLOAD
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// hclDownload_OnProgress subroutine
//
// PROGRESS event handler for the HCL_DOWNLOAD control
//
//    * Update the progress bar
//    * Update the Estimated Time Left (ETL)
//    * Update the Transfer Rate
//
// ----------------------------------------------------------------------------
// [i] : bytesReceived. Number of bytes received between this PROGRESS event
//     : and the previous one                 
// [i] : bytesDownloaded. Total Number of bytes downloaded so far 
// [i] : bytesExpected. Total number of bytes expected from the server
// ----------------------------------------------------------------------------
hclDownload_OnProgress:

   timeData = @@window->$@_TIMEDATA
   
   startTime = timeData<1>
   prevTime  = timeData<2>
   prevRate  = timeData<3>
   
   now       = msWin_GetTickCount64()
   
   if ( now > prevTime ) else
      now = prevTime + 1
   end
   
   elapsedTime  = now - startTime
   transferRate = int( ( bytesDownloaded / elapsedTime ) * 1000 )  ; // in secs

   etlText = ""
   trText  = ""
   
   if ( bytesExpected ) then
   
      // If we know how much we're expected to deal with we can 
      // calculate how much time is left and ho far we are 
      // through
   
      if ( .prb_download->marquee ) then
         .prb_download->marquee  = 0 ; // ms
         .prb_download->showText = TRUE$
      end
      
      .prb_download->value = int( ( bytesDownloaded / bytesExpected ) * 100 )
      
      etlSecs  = ( bytesExpected - bytesDownloaded ) / transferRate ; // seconds to complete
      
      etlHours = 0
      etlMins  = 0
      
      if ( etlSecs > 3600 ) then
         etlHours = int( etlSecs / 3600 )
         etlSecs  = mod( etlSecs , 3600 )
      end
      
      if ( etlSecs > 60 ) then
         etlMins = int( etlSecs / 60 )
         etlSecs = mod( etlSecs , 60 )
      end
      
      etlSecs = int( etlSecs )
      
      if ( etlHours ) then
         etlText := etlHours : " hours "
      end
      
      if ( etlMins ) then
         etlText := etlMins : " minutes "
      end
      
      if ( etlSecs ) then
         etlText := etlSecs : " seconds "
      end
      
      cb = bytesDownloaded
      goSub hclDownload_OnProgress_bytesToText
      
      etlText := " (" : cb : " " : sf : " of "
      
      cb = bytesExpected
      goSub hclDownload_OnProgress_bytesToText
      
      etlText := cb : " " : sf : " downloaded)"
      
   end else
      // We can't calculate the ETL or the progress done because we don't
      // how much we've got left to down load
      
      // Ensure we set the progress bar to a marquee
      if ( .prb_download->marquee ) else
         .prb_download->showText = FALSE$
         .prb_download->marquee  = 20 ; // ms
      end
      
      // We can however show the amount copied....
      
      cb = bytesDownloaded
      goSub hclDownload_OnProgress_bytesToText
      
      etlText = cb : " " : sf : " downloaded"
      
   end
   
   cb = transferRate
   goSub hclDownload_OnProgress_bytesToText
   
   begin case
      case ( transferRate > 0x100000 )
         // We're working in MB
         sf = "Mb/s"
         
      case ( transferRate > 0x400 )
         // We're working in KB
         sf = "Kb/s"
      
      case OTHERWISE$
         sf = "b/s"
         
   end case
   
   trText = cb : " " : sf
   
   .txt_ETL->text = etlText
   .txt_TR->text  = trText
   
   timeData<2> = now
   timeData<3> = transferRate
   
   @@window->$@_TIMEDATA = timeData
   
return

///////////////////////////////////////////////////////////////////////////////
// hclDownload_OnProgress_bytesToText subroutine
//
// Simple routine to translate the number of bytes into MB, KB etc ...
//
// ----------------------------------------------------------------------------
// [i,o] cb : Number of bytes in, translated amount out
// [o]   sf : Suffic to append to the translated amount - MB, KB or "Bytes"
// ----------------------------------------------------------------------------
hclDownload_OnProgress_bytesToText:
      
   // We can however show the amount copied....
   begin case
      case ( cb > 0x100000 )
         cb = oconv( ( cb / 0x100000 ) * 100, "MD2" )
         sf = "MB"
         
      case ( cb > 0x400 )
         cb = oconv( ( cb / 0x400 ) * 100, "MD2" )
         sf = "KB"  
         
      case OTHERWISE$
         sf = "Bytes"
         
   end case
   
return

///////////////////////////////////////////////////////////////////////////////

hclDownload_OnReadyStateChanged:

   begin case
      case ( newState == PS_HCL_RS_DONE$ )
         // When we get this notification the client has finished downloading  
         // from theserver so we need to grab the content and then pass it to 
         // the parent's ENDDIALOG event or the specified callback proc.
         goSub hclDownload_ReturnResponse
         
      case OTHERWISE$
         null
         
   end case
   
return

///////////////////////////////////////////////////////////////////////////////
// hclDownload_OnTimeout subroutine
//
// TIMEOUT event handler for the HCL_DOWNLOAD control
//
// Return what we have to the caller - the status code returned should be 408
//
// ----------------------------------------------------------------------------
hclDownload_OnTimeout:
   goSub hclDownload_ReturnResponse
return

///////////////////////////////////////////////////////////////////////////////
// hclDownload_ReturnResponse subroutine
//
// Call back with the reponse content and close the dialog.
// 
// ----------------------------------------------------------------------------
hclDownload_ReturnResponse:

   // Details we need were passed to the create event which we saved in a UDP:
   createParam = @@window->$@_CREATEPARAM
   
   parentID = createParam<HDL_UIPARAM_POS_PARENTWIN$>
   bModal   = createParam<HDL_UIPARAM_POS_MODAL$>
   asyncID  = createParam<HDL_UIPARAM_POS_ENDDIALOGASYNCID$>
      
   // Renable the parent window if we have one...
   if bLen( parentID ) then
      if bModal then
         @parentID->enabled = TRUE$
      end
   end
   
   // Remove this dialog from screen
   @@window->hide( "" )
   
   if ( createParam<HDL_UIPARAM_POS_SYNCPROGRESS$> ) then
      .prb_Download->value = 0
      .prb_Download->syncTaskbar = FALSE$
   end
   
   // We send back two parameters to the callback event/proc
   //
   // 1) An array of response data
   //
   //       <1> AsyncID or CallBackID
   //       <2> Response Status Code
   //       <3> Response Status Text
   //       <4> Content Length
   //       <5> Response Header Names
   //       <6> Bytes received for the response content
   //
   // 2) The response content (unless this was a file download)
   
   rspInfo    = ""
   
   tmp = .hcl_Download->responseStatus
   
   rspInfo<HDL_RSPINFO_POS_STATUSCODE$> = tmp<PS_HCL_RSPSTAT_POS_CODE$>
   rspInfo<HDL_RSPINFO_POS_STATUSTEXT$> = tmp<PS_HCL_RSPSTAT_POS_TEXT$>
   rspInfo<HDL_RSPINFO_POS_CONTENTLEN$> = tmp<PS_HCL_RSPSTAT_POS_CNTLEN$>
   rspInfo<HDL_RSPINFO_POS_HEADERS$>    = rti_Convert( .hcl_Download->responseHeaders, @fm, @vm )
   
   responseFile = .hcl_Download->responseFile
   if bLen( responseFile ) then
      rspInfo<HDL_RSPINFO_POS_BYTESRECEIVED$> = dir( responseFile )<1>
      rspContent = ""
   end else
      rspContent = .hcl_Download->GetResponseContent( "" )
      rspInfo<HDL_RSPINFO_POS_BYTESRECEIVED$> = bLen( rspContent )
   end
   
   // If ww're here because of an Abort() call then we don't bother closing...
   if ( @@window->$@_ABORT ) else
      call post_Event( @window, "CLOSE" )
   end
   
   // Now decide where to send the data
   if bLen( asyncID ) then
      // Send to the parent's ENDDIALOG
      if ( @parentID->handle ) then
      
         rspInfo<HDL_RSPINFO_POS_ID$> = asyncID
         call send_Event( parentID, "ENDDIALOG", atWindow, rspContent, rspInfo )
         
      end
   end else
      // Send to the callback proc
      procID = createParam<HDL_UIPARAM_POS_CALLBACKPROC$>
      if bLen( procID ) then
         rspInfo<HDL_RSPINFO_POS_ID$> = createParam<HDL_UIPARAM_POS_CALLBACKID$>
         call @procID( rspInfo, rspContent )
      end
  end
  
return

///////////////////////////////////////////////////////////////////////////////
#endregion HCL_DOWNLOAD
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
$insert copyright
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
