;---FUNCTIONS-----------------------------------------------------------------------
; Misc Functions that are called by both the Video and Social Media poster


; -------------------------------Variables-------------------------------
; Declare global variables here so they don't have to be declared in each script
global DriverURLArray
global DriverTitleArray
global LastWebsitePostURL
global ScreenshotsTaken
global ChromeFilepath
global CurrentTabURL

; #Include %A_ScriptDir%\ChromeAutomationFunctions.ahk
; #Include %A_ScriptDir%\RunCMD.ahk

#Include C:\Users\%A_username%\Syncthing\Freedomain\Uploaders\Lib\ChromeAutomationFunctions.ahk
#Include C:\Users\%A_username%\Syncthing\Freedomain\Uploaders\Lib\RunCMD.ahk


DevModeMsgBox(Message){
    if(!DevMode)
    return

    Msgbox, 4096, DevModeMsgBox, %Message%
    return
}


CheckForUpdates(){
    ; msgbox, checking for updates
    Message = Checking For Updates
    SaveOrPostProgress(Message,PostType:="Tooltip,ErrorLoggingTextFile")


    ; Msgbox % "FreedomScriptsChangelogURL: " FreedomScriptsChangelogURL
    ; Msgbox % "ChangelogIniFilepath: " ChangelogIniFilepath
    ; Msgbox % "ScriptSettingsSectio: " ScriptSettingsSection
    ; Msgbox % "SettingsIniFilepath: " SettingsIniFilepath

    UrlDownloadToFile, %FreedomScriptsChangelogURL%, %ChangelogIniFilepath%
    if(ErrorLevel){
        Message = Failed to Download Changelog 
        SaveOrPostProgress(Message:=Message,PostType:="Tooltip,ErrorLoggingTextFile,ErrorSummaryVar,DiscordErrorLogging")
        Return
    }

    IniRead, UpdateVersion, %ChangelogIniFilepath%, %ScriptSettingsSection%, ScriptVersion, %A_Space%
    if(UpdateVersion = ""){
        ToolTip
        ; MsgBox, update version is blank.
        Return

    }


    if(ScriptVersion != UpdateVersion){
        ; msgbox, update found!
        Message = Script Update Found
        SaveOrPostProgress(Message:=Message,PostType:="Tooltip,ErrorLoggingTextFile")
        IniWrite, 1, %SettingsIniFilepath%, %ScriptSettingsSection%, UpdateAvailable

        GuiControl,, UpdateAvailable, Update Available - Click to View
    }
    else, {
        Message = No Updates Found
        SaveOrPostProgress(Message:=Message,PostType:="Tooltip,ErrorLoggingTextFile")
        IniWrite, 0, %SettingsIniFilepath%, %ScriptSettingsSection%, UpdateAvailable

    }
    ToolTip
}




UpdateScript(){
    ; Msgbox % "ChangelogIniFilepath: " ChangelogIniFilepath
    ; Msgbox % "ScriptSettingsSection: " ScriptSettingsSection

    IniRead, Changelog, %ChangelogIniFilepath%, %ScriptSettingsSection%, Changelog, %A_Space%
    Changelog := StrReplace(Changelog, "--", "`n-")

    IniRead, UpdateVersion, %ChangelogIniFilepath%, %ScriptSettingsSection%, ScriptVersion, %A_Space%

    if(ScriptVersion = UpdateVersion){
        IniWrite, 0, %SettingsIniFilepath%, %ScriptSettingsSection%, UpdateAvailable
        MsgBox, You are Up-To-Date
        ; IniRead, UpdateAvailable, %SettingsIniFilepath%, %ScriptSettingsSection%, UpdateAvailable, 0
        return
    }

    Changelog = %ScriptVersion% -> %UpdateVersion%`n`n-%Changelog%

    OnMessage(0x44, "OnMsgBoxUpdateAvailable")
    MsgBox 0x44, Update Available, %Changelog%
    OnMessage(0x44, "")

    IfMsgBox Yes, {
        Return
        } Else IfMsgBox No, {
            run, "%FreedomainProgramUpdaterFilepath%" "%ScriptSettingsSection%"
            ; Return
            ExitApp
        }
        return
    }



    ; -------------------------------SaveOrPostProgress-------------------------------
    ; PostType:="Tooltip,ErrorLoggingTextFile,ErrorSummaryVar,DiscordErrorLogging,DiscordVideos"
    SaveOrPostProgress(Message:="",PostType:=""){ 

        MessageBU := Message
        ; Msgbox % "PostType: " PostType
        ; Msgbox % "CurrentSite: " CurrentSite

        if(CurrentSite != "")
        Message := CurrentSite . ": " . Message

        if(InStr(PostType, "Tooltip")){
            TooltipThis(Message)
        }
        if(InStr(PostType, "ErrorLoggingTextFile")){
            Func_LogErrorsToTextFile(Message)
        }
        if(InStr(PostType, "ErrorSummaryVar")){
            Func_LogErrorsToVar(Message)
        }
        if(InStr(PostType, "DiscordErrorLogging")){
            PostToDiscordChannel(Message,DiscordErrorLoggingWebhookBotURL)
        }
        if(InStr(PostType, "DiscordVideos")){
            Message := MessageBU
            PostToDiscordChannel(Message,DiscordVideosWebhookURL)
        }
        if(InStr(PostType, "DiscordParler")){
            Message := MessageBU
            PostToDiscordChannel(Message,DiscordParlerWebhookURL)
        }



    }
    ; -------------------------------/SaveOrPostProgress-------------------------------
    TakeScreenshotOfPage(SaveFilepath := ""){

        if(!ScreenshotResult)
        return

        TooltipThis("Sleeping 5 Seconds Before Taking Screenshot")
        ; sleep, 5000

        if(SaveFilepath = ""){
         SaveFilepath := ErrorLoggingDirectory
     }

     ; if(!FileExist(SaveFilepath))
     FileCreateDir, %SaveFilepath%

 /*   if(!TakeScreenshotsOfErrors)
    Return
    */
    ; Take a screenshot of the page and save it. 
    FormatTime, TodayDate , YYYYMMDDHH24MISS, yyyyMMdd_hhmmss 

    ; Msgbox % "ErrorLoggingDirectory: " ErrorLoggingDirectory
    savepath = %SaveFilepath%\%TodayDate%.jpg
    ; Msgbox % "savepath: " savepath
    try driver.TakeScreenshot().SaveAs(savepath)

    ; ScreenshotsTaken := 1

}




; -------------------------------TooltipThis-------------------------------
TooltipThis(String){
    ; Xposition := StrLen(String)
    StringFirstLine := StrSplit(String, "`n")
    StringFirstLine := StringFirstLine[1]
    Xposition := StrLen(StringFirstLine)
    ; Msgbox % "String: " String
    Xposition := Xposition * 4
    X := (A_ScreenWidth / 2) - Xposition
    ; Msgbox % "X: " X
    if(ShowTooltipProgress){
        ToolTip, %String%, %X%, 0
    }
}
; -------------------------------/TooltipThis-------------------------------

;------------------------------------------------
TimedToolTip(Text, x="", y="",RemoveAfterTime:=2000, SetWhichToolTip="") {  
    if(X = ""){
        Xposition := StrLen(Text)
        ; Msgbox % "Xposition: " Xposition
        Xposition := Xposition * 4
        ; Xposition := 0
        X := (A_ScreenWidth / 2) - Xposition
        ; Msgbox % "X: " X
    }
    (y = "")?(y:=0):(y:=y)
    
    ; Msgbox % "y: " y
    ToolTip, %Text%, %X%, %Y%
    SetTimer, RMApp_ToolTipRASub, % - RemoveAfterTime   
    return
    RMApp_ToolTipRASub:
    ToolTip,,,, %WhichToolTip%
    return
}


; -------------------------------LogErrorsToTextFile-------------------------------
Func_LogErrorsToTextFile(Text){
    ; ErrorLoggingFile := Filepath
    FormatTime, TodayDate , YYYYMMDDHH24MISS, yyyyMMdd_hhmmss
    text =
    (

        ---------------%TodayDate%---------------
        %Text%

        )
    FileAppend, %Text%, %ErrorLoggingFilePath%
}
; -------------------------------/LogErrorsToTextFile-------------------------------

; -------------------------------LogErrorsToVar-------------------------------
Func_LogErrorsToVar(Text){
    ErrorLogVar .= "`n" . Text
}
; -------------------------------/LogErrorsToVar-------------------------------


; -------------------------------Discord-------------------------------
PostToDiscordChannel(Message,WebhookChannel){
    ; Msgbox % "Message: " Message
    ; Msgbox % "WebhookChannel: " WebhookChannel

    ; Replace all the json forbidden characters
    SingleQuote = "
    ReplacedQuote = \"

    Message := StrReplace(Message, "\", "\\") ; Replace Tabs
    Message := StrReplace(Message, SingleQuote, ReplacedQuote) ; Replace single quote 
    Message := StrReplace(Message, "`r", "") ; Replace Carriage return
    Message := StrReplace(Message, A_Tab, "\t") ; Replace Tabs
    Message := StrReplace(Message, "`n", "\n") ; Escape New Line Character 
    Message := StrReplace(Message, "`f", "\f") ; Replace Tabs


    ; Convert into json string
    JsonString=
    (
    {
      "content": "%Message%"
  }
  )

    ; Msgbox % "JsonString: " JsonString
    ; 
    try WebRequest := ComObjCreate("WinHttp.WinHttpRequest.5.1")
    try WebRequest.Open("POST", WebhookChannel, false)
    try WebRequest.SetRequestHeader("Content-Type", "application/json")
    try WebRequest.Send(JsonString)  
}


; -------------------------------/Discord-------------------------------



; -------------------------------TelegramAPI-------------------------------
TelegramMsgBox(Text:="", TelegramBotToken := "", TelegramBotChatID :=""){
    ; Send Images: 
    ; https://www.autohotkey.com/boards/viewtopic.php?f=76&t=68417&p=294332#p294332

    ; Msgbox % "Text: " Text


    ; Replace all forbidden characters - https://www.ascii-code.com/
    ; Text := StrReplace(Text, "`%", "%25") ; percent with 
    Text := StrReplace(Text, "`n", "%0A") ; New Line
    ; Text := StrReplace(Text, "`n", "%0A") ; New Line

    ; Text := StrReplace(Text, " ", "%23") ; New Line
    ; Text := 
    ; Text := StrReplace(Text, "`n", "%0A") ; New Line

    ; Msgbox % "Text: " Text

    ErrorLoggingPath = %A_ScriptDir%\Lib\ErrorLogging\check.rups

    loop 3
    {
        UrlDownloadToFile https://api.telegram.org/bot%TelegramBotToken%/sendmessage?chat_id=%TelegramBotChatID%&text=%Text%, %ErrorLoggingPath%
        sleep 1000
        ifexist %ErrorLoggingPath%
        {
            break
        }
        if A_index = 3
        {
            Message = Post failed due to API Issue. 
            SaveOrPostProgress(Message:=Message,PostType:="Tooltip,ErrorLoggingTextFile,ErrorSummaryVar,DiscordErrorLogging")
            Return "Failed"
            ; MsgBox, 16,, something went wrong with sending
        }
    }
    FileRead, TelegramOutput, %ErrorLoggingPath%
    TelegramOutput := "API Result: " . TelegramOutput
    ; Msgbox % "TelegramOutput: " TelegramOutput
    filedelete %ErrorLoggingPath%
    SaveOrPostProgress(Message:=TelegramOutput,PostType:="ErrorLoggingTextFile")

    if(InStr(TelegramOutput, "error_code")){
        Return "Failed"
    }

}

; -------------------------------/TelegramAPI-------------------------------

; Telegram Message API
;------------------------------------------------
SendTelegramMessage(token, chatID, text := "", ParseMode := "MarkdownV2")       ; you could add more options; compare the Telegram API docs
{
    ; ParseMode := "MarkdownV2"
    ; msgbox
    ; Msgbox % "text: " text
    url_str := "https://api.telegram.org/bot" token "/sendMessage"
    objParam := { "chat_id" : chatID                                                                              
    ,"parse_mode" : ParseMode
    ,"text" : text }

    CreateFormData(postData, hdr_ContentType, objParam)
    whr := ComObjCreate("WinHttp.WinHttpRequest.5.1")
    whr.Open("POST", url_str, true)
    whr.SetRequestHeader("Content-Type", hdr_ContentType)
    ; whr.SetRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko")                ; ???????
    whr.Option(6) := False ; No auto redirect
    whr.Send(postData)
    whr.WaitForResponse()
    json_resp := whr.ResponseText
    whr :=                                              ; free COM object
    ; Msgbox % "json_resp: " json_resp
    if(InStr(json_resp, "error_code"))
    Return json_resp
}

/*

*bold \*text*
_italic \*text_
__underline__
~strikethrough~
*bold _italic bold ~italic bold strikethrough~ __underline italic bold___ bold*
[inline URL](http://www.example.com/)
[inline mention of a user](tg://user?id=123456789)
`inline fixed-width code`
```
pre-formatted fixed-width code block
```
```python
pre-formatted fixed-width code block written in the Python programming language
```

*/



; -------------------------------Telegram Image Sending-------------------------------
; https://www.autohotkey.com/boards/viewtopic.php?t=68417
SendTelegramPhoto(token, chatID, file, caption := "", ParseMode := "MarkdownV2")       ; you could add more options; compare the Telegram API docs
{

    ; ParseMode := "MarkdownV2"
    url_str := "https://api.telegram.org/bot" token "/sendPhoto" 
    objParam := {       "chat_id" : chatID                                                                              
    ,   "photo" : [file]  
    ,   "parse_mode" : ParseMode 
    , "caption" : caption}

    CreateFormData(postData, hdr_ContentType, objParam)
    whr := ComObjCreate("WinHttp.WinHttpRequest.5.1")
    whr.Open("POST", url_str, true)
    whr.SetRequestHeader("Content-Type", hdr_ContentType)
    ; whr.SetRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko")                ; ???????
    whr.Option(6) := False ; No auto redirect
    whr.Send(postData)
    whr.WaitForResponse()
    json_resp := whr.ResponseText
    whr :=                                              ; free COM object
    ; Msgbox % "json_resp: " json_resp

    if(InStr(json_resp, "error_code"))
    Return json_resp
}


; -------------------------------Telegram Image Sending-------------------------------
; https://www.autohotkey.com/boards/viewtopic.php?t=68417
SendTelegramVideo(token, chatID, file, caption := "", ParseMode := "MarkdownV2")       ; you could add more options; compare the Telegram API docs
{

    ; ParseMode := "MarkdownV2"
    url_str := "https://api.telegram.org/bot" token "/sendVideo?caption=" caption
    objParam := {       "chat_id" : chatID                                                                              
    ,   "video" : [file]  
    ,   "parse_mode" : ParseMode }

    CreateFormData(postData, hdr_ContentType, objParam)
    whr := ComObjCreate("WinHttp.WinHttpRequest.5.1")
    whr.Open("POST", url_str, true)
    whr.SetRequestHeader("Content-Type", hdr_ContentType)
    ; whr.SetRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko")                ; ???????
    whr.Option(6) := False ; No auto redirect
    whr.Send(postData)
    whr.WaitForResponse()
    json_resp := whr.ResponseText
    whr :=                                              ; free COM object
    ; Msgbox % "json_resp: " json_resp

    if(InStr(json_resp, "error_code"))
    Return json_resp
}



; -------------------------------Telegram File Sending-------------------------------
; https://www.autohotkey.com/boards/viewtopic.php?t=68417
SendTelegramFile(token, chatID, file, caption := "", ParseMode := "MarkdownV2")       ; you could add more options; compare the Telegram API docs
{

    ; ParseMode := "MarkdownV2"
    url_str := "https://api.telegram.org/bot" token "/sendDocument?caption=" caption
    objParam := {       "chat_id" : chatID                                                                              
    ,   "document" : [file]  
    ,   "parse_mode" : ParseMode }

    CreateFormData(postData, hdr_ContentType, objParam)
    whr := ComObjCreate("WinHttp.WinHttpRequest.5.1")
    whr.Open("POST", url_str, true)
    whr.SetRequestHeader("Content-Type", hdr_ContentType)
    ; whr.SetRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko")                ; ???????
    whr.Option(6) := False ; No auto redirect
    whr.Send(postData)
    whr.WaitForResponse()
    json_resp := whr.ResponseText
    whr :=                                              ; free COM object
    ; Msgbox % "json_resp: " json_resp

    if(InStr(json_resp, "error_code"))
    Return json_resp
}






; -------------------------------/Telegram API Image Sending-------------------------------


ASCIISTRReplace(Message){
    ; Msgbox % "Message of function: " Message
    ; https://tech.saigonist.com/b/code/escaping-special-characters-markdown.html
    ; Replace all forbidden characters - https://www.ascii-code.com/

    LineBreakChar = `%0A ; Used for API


    Message := StrReplace(Message, "\", "\\") ; replace all new line characters with the global charater
    Message := StrReplace(Message, "*", "\*") ; replace all new line characters with the global charater
    ; Message := StrReplace(Message, "`n", LineBreakChar) ; replace all new line characters with the global charater
    Message := StrReplace(Message, "!", "\!") ; replace all new line characters with the global charater
    Message := StrReplace(Message, "_", "\_") ; replace all new line characters with the global charater
    Message := StrReplace(Message, "{", "\{") ; replace all new line characters with the global charater
    Message := StrReplace(Message, "}", "\}") ; replace all new line characters with the global charater
    Message := StrReplace(Message, "[", "\[") ; replace all new line characters with the global charater
    Message := StrReplace(Message, "]", "\]") ; replace all new line characters with the global charater
    Message := StrReplace(Message, "(", "\(") ; replace all new line characters with the global charater
    Message := StrReplace(Message, ")", "\)") ; replace all new line characters with the global charater
    Message := StrReplace(Message, "#", "\#") ; replace all new line characters with the global charater
    Message := StrReplace(Message, "+", "\+") ; replace all new line characters with the global charater
    Message := StrReplace(Message, "-", "\-") ; replace all new line characters with the global charater
    Message := StrReplace(Message, ".", "\.") ; replace all new line characters with the global charater
    Message := StrReplace(Message, ">", "\>") ; replace all new line characters with the global charater
    Message := StrReplace(Message, "<", "\<") ; replace all new line characters with the global charater
    Message := StrReplace(Message, "=", "\=") ; replace all new line characters with the global charater
    Message := StrReplace(Message, "|", "\|") ; replace all new line characters with the global charater
    Message := StrReplace(Message, "~", "\~") ; replace all new line characters with the global charater
    Message := StrReplace(Message, "`", "\`") ; replace all new line characters with the global charater
    Message := StrReplace(Message, "$", "\$") ; replace all new line characters with the global charater
    ; Message := StrReplace(Message, "%", "\%") ; replace all new line characters with the global charater
    Message := StrReplace(Message, "&", "\&") ; replace all new line characters with the global charater

    ; Msgbox % "replaced Message: " Message


    Return Message
}



; -------------------------------Discord Images-------------------------------
UploadImageToDiscord(Webhook, Message:="", Filepath:=""){

    filepath := [Filepath] ; Create object
    
    objParam := {file: filepath, content: Message}
    CreateFormData(PostData, hdr_ContentType, objParam)

    HTTP := ComObjCreate("WinHTTP.WinHTTPRequest.5.1")
    HTTP.Open("POST", Webhook, true)
    HTTP.SetRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko")
    HTTP.SetRequestHeader("Content-Type", hdr_ContentType)
    HTTP.SetRequestHeader("Pragma", "no-cache")
    HTTP.SetRequestHeader("Cache-Control", "no-cache, no-store")
    HTTP.SetRequestHeader("If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT")
    HTTP.Send(PostData)
    HTTP.WaitForResponse()
    ; msgbox % HTTP.ResponseText
    return HTTP.ResponseText
}
; -------------------------------/Discord Images-------------------------------


;---------------------------Discord Messages---------------------
UploadMessageToDiscord(Webhook, Message:=""){

    ; filepath := [Filepath] ; Create object

    objParam := {file: filepath, content: Message}
    CreateFormData(PostData, hdr_ContentType, objParam)

    HTTP := ComObjCreate("WinHTTP.WinHTTPRequest.5.1")
    HTTP.Open("POST", Webhook, true)
    HTTP.SetRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko")
    HTTP.SetRequestHeader("Content-Type", hdr_ContentType)
    HTTP.SetRequestHeader("Pragma", "no-cache")
    HTTP.SetRequestHeader("Cache-Control", "no-cache, no-store")
    HTTP.SetRequestHeader("If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT")
    HTTP.Send(PostData)
    HTTP.WaitForResponse()
    ; msgbox % HTTP.ResponseText
    return HTTP.ResponseText
}




;---------------------------/Discord Messages---------------------







; -------------------------------CreateFormData - Creates "multipart/form-data" for http post-------------------------------

; Used for WinHttp.WinHttpRequest.5.1, Msxml2.XMLHTTP ...
CreateFormData(ByRef retData, ByRef retHeader, objParam) {
    New CreateFormData(retData, retHeader, objParam)
}

; Used for WinInet
CreateFormData_WinInet(ByRef retData, ByRef retHeader, objParam) {
    New CreateFormData(safeArr, retHeader, objParam)

    size := safeArr.MaxIndex() + 1
    VarSetCapacity(retData, size, 1)
    DllCall("oleaut32\SafeArrayAccessData", "ptr", ComObjValue(safeArr), "ptr*", pdata)
    DllCall("RtlMoveMemory", "ptr", &retData, "ptr", pdata, "ptr", size)
    DllCall("oleaut32\SafeArrayUnaccessData", "ptr", ComObjValue(safeArr))
}

Class CreateFormData {

    __New(ByRef retData, ByRef retHeader, objParam) {

        CRLF := "`r`n"

        Boundary := this.RandomBoundary()
        BoundaryLine := "------------------------------" . Boundary

        ; Loop input paramters
        binArrs := []
        For k, v in objParam
        {
            If IsObject(v) {
                For i, FileName in v
                {
                    str := BoundaryLine . CRLF
                    . "Content-Disposition: form-data; name=""" . k . """; filename=""" . FileName . """" . CRLF
                    . "Content-Type: " . this.MimeType(FileName) . CRLF . CRLF
                    binArrs.Push( BinArr_FromString(str) )
                    binArrs.Push( BinArr_FromFile(FileName) )
                    binArrs.Push( BinArr_FromString(CRLF) )
                }
            } 
            Else {
                str := BoundaryLine . CRLF
                . "Content-Disposition: form-data; name=""" . k """" . CRLF . CRLF
                . v . CRLF
                binArrs.Push( BinArr_FromString(str) )
            }
        }

        str := BoundaryLine . "--" . CRLF
        binArrs.Push( BinArr_FromString(str) )

        retData := BinArr_Join(binArrs*)
        retHeader := "multipart/form-data; boundary=----------------------------" . Boundary
    }

    RandomBoundary() {
        str := "0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z"
        Sort, str, D| Random
        str := StrReplace(str, "|")
        Return SubStr(str, 1, 12)
    }

    MimeType(FileName) {
        n := FileOpen(FileName, "r").ReadUInt()
        Return (n        = 0x474E5089) ? "image/png"
        : (n        = 0x38464947) ? "image/gif"
        : (n&0xFFFF = 0x4D42    ) ? "image/bmp"
        : (n&0xFFFF = 0xD8FF    ) ? "image/jpeg"
        : (n&0xFFFF = 0x4949    ) ? "image/tiff"
        : (n&0xFFFF = 0x4D4D    ) ? "image/tiff"
        : "application/octet-stream"
    }

}
;#############################################################################################################
; Update: 2015-6-4 - Added BinArr_ToFile()

BinArr_FromString(str) {
    oADO := ComObjCreate("ADODB.Stream")

    oADO.Type := 2 ; adTypeText
    oADO.Mode := 3 ; adModeReadWrite
    oADO.Open
    oADO.Charset := "UTF-8"
    oADO.WriteText(str)

    oADO.Position := 0
    oADO.Type := 1 ; adTypeBinary
    oADO.Position := 3 ; Skip UTF-8 BOM
    return oADO.Read, oADO.Close
}

BinArr_FromFile(FileName) {
    oADO := ComObjCreate("ADODB.Stream")

    oADO.Type := 1 ; adTypeBinary
    oADO.Open
    oADO.LoadFromFile(FileName)
    return oADO.Read, oADO.Close
}

BinArr_Join(Arrays*) {
    oADO := ComObjCreate("ADODB.Stream")

    oADO.Type := 1 ; adTypeBinary
    oADO.Mode := 3 ; adModeReadWrite
    oADO.Open
    For i, arr in Arrays
    oADO.Write(arr)
    oADO.Position := 0
    return oADO.Read, oADO.Close
}

BinArr_ToString(BinArr, Encoding := "UTF-8") {
    oADO := ComObjCreate("ADODB.Stream")

    oADO.Type := 1 ; adTypeBinary
    oADO.Mode := 3 ; adModeReadWrite
    oADO.Open
    oADO.Write(BinArr)

    oADO.Position := 0
    oADO.Type := 2 ; adTypeText
    oADO.Charset  := Encoding 
    return oADO.ReadText, oADO.Close
}

BinArr_ToFile(BinArr, FileName) {
    oADO := ComObjCreate("ADODB.Stream")

    oADO.Type := 1 ; adTypeBinary
    oADO.Open
    oADO.Write(BinArr)
    oADO.SaveToFile(FileName, 2)
    oADO.Close
}
; -------------------------------/CreateFormData - Creates "multipart/form-data" for http post-------------------------------

; -------------------------------CheckDirExistAndCreate-------------------------------
; Check if directory exists and if not, create it
CheckDirExistAndCreate(Path){
    if(!FileExist(Path)){
        FileCreateDir, %Path%
    }
}
; -------------------------------/CheckDirExistAndCreate-------------------------------



CheckIfUpdateAvailable(Filepath, CurrentVersion){
    FileRead, ScriptUpdateContents, %Filepath%
    FileContents := StrSplit(ScriptUpdateContents, "ScriptVersion")
    FileContents := FileContents[2]
    FileContents := StrSplit(FileContents, "`n")
    UpdateVersion := FileContents[1]
    ; msgbox % UpdateVersion

    if(!InStr(UpdateVersion, CurrentVersion)){ ; Update found for file, write to settings.ini for next time script is run
        Message = Update Available to Download
        SaveOrPostProgress(Message:=Message,PostType:="Tooltip,ErrorLoggingTextFile")
        Return True
    }
    Return False
}



FindUpdateChangeLog(Filepath){
    FileRead, ScriptUpdateContents, %Filepath%

    ; Set Variables
    ChangelogStart =;---Changelog------------------------------------------------------
    ChangelogEnd =;---/Changelog------------------------------------------------------

    ; Msgbox % "ScriptUpdateContents: " ScriptUpdateContents
    ChangeLog := StrSplit(ScriptUpdateContents, ChangelogStart)
    ChangeLog := ChangeLog[2]
    ChangeLog := StrSplit(ChangeLog, ChangelogEnd)
    ChangeLog := ChangeLog[1]
    Return Changelog
}






CheckSeleniumDriver(){
    try Driver := SChrome_Get("", ChromeProfile := "")  ; open new tab page with with specified profile
    catch e {
        ShowSeleniumErrorMsgbox()
        ; Message = Failed to Make Connection to Chrome. Check for Any Open Dialogue Boxes or out of date ChromeDriver
        ; SaveOrPostProgress(Message:=Message,PostType:="Tooltip,ErrorLoggingTextFile,DiscordErrorLogging")
        Return "Failed"
    }
    ; Driver := SChrome_Get("https://www.bitchute.com/", ChromeProfile)  ; open new tab page with with specified profile
    ; driver.executeScript("return document.readyState").equals("complete") ; wait until page loads completely before proceeding
    try driver.executeScript("return document.readyState").equals("complete") ; wait until page loads completely before proceeding
    catch e {
        ShowSeleniumErrorMsgbox()
        ; Message = Failed to send command to Chrome. Check for an Open Dialogue Box.
        ; SaveOrPostProgress(Message:=Message,PostType:="Tooltip,ErrorLoggingTextFile,DiscordErrorLogging")
        Return "Failed"
    }
    DriverStatus := 1
}

SeleniumConnectToActiveTab(IP_Port := "127.0.0.1:9222"){
    Driver := ComObjCreate("Selenium.ChromeDriver")
    Driver.SetCapability("debuggerAddress", IP_Port)
    try Driver.Start()
    catch e {
        ShowSeleniumErrorMsgbox()
        ; msgbox, failed to connect to Chrome for some reason.
        ; Message = Failed to Connect to Chrome for some reason.
        ; SaveOrPostProgress(Message:=Message,PostType:="Tooltip,ErrorLoggingTextFile,ErrorSummaryVar,DiscordErrorLogging")
    }
    return Driver
}

CreateArrayOfTabs() {
    Message = Creating an Array of All Chrome Tabs
    SaveOrPostProgress(Message:=Message,PostType:="Tooltip,ErrorLoggingTextFile")

    DriverTitleArray := [] ; Create an array
    DriverURLArray := []

    try TotalTabsFound := Driver.Windows.Count

    ; if only 1 tab exists, grab info, push to array and exit early
    if(TotalTabsFound = 1){
        try Title := Driver.Title
        try URL := Driver.URL

        if(Title != "")
        DriverTitleArray.Push(Title)
        if(URL != "")
        DriverURLArray.Push(URL)
        return
    }

    ; Msgbox % "TotalTabsFound: " TotalTabsFound
    ; if(DevMode)
    ; Msgbox % "TotalTabsFound: " TotalTabsFound
    ; msgbox % "total Tabs:" Driver.Windows.Count
    ; Message = Creating an Array of Chrome Tabs
    ; SaveOrPostProgress(Message:=Message,PostType:="Tooltip,ErrorLoggingTextFile")

    ; StartTime := A_TickCount
    ; Sleep, 1000
    StartTime := A_TickCount

    Loop, {
        Message = Creating an Array of All Chrome Tabs`nCurrent Loop: %A_index%/%TotalTabsFound% (Cannot loop through unloaded tabs)`nSometimes might get stuck for a short while if there is something loading in active tab
        TooltipThis(Message)

        ; SaveOrPostProgress(Message:=URL,PostType:="Tooltip,ErrorLoggingTextFile,ErrorSummaryVar")
        ; if(ArrayContainsURL = 3 AND A_index > TotalTabsFound)
        ; Break


        TabEndTime :=  A_TickCount - TabFoundStartTime
        TabEndTimeArray .= TabEndTime . ","


        ; Message = Time to switch to new tab: %TabEndTime%`nCurrent Loop: %A_index%/%TotalTabsFound%
        ; SaveOrPostProgress(Message:=Message,PostType:="ErrorLoggingTextFile,DiscordErrorLogging")

        TabFoundStartTime := A_TickCount


        IndexMinus3 := A_index - 3
        ; if(FirstURLPosition AND SecondURLPosition AND ThirdURLPosition AND IndexPlus3 > TotalTabsFound OR A_index = 30) {
            if(IndexMinus3 > TotalTabsFound) {
                ; DevModeMsgBox("First 3 tabs found. Breaking")
                ; Msgbox % "A_index: " A_index "`n" "IndexPlus3: " IndexPlus3
                break

            }

            if(TotalTabsFound < 4 AND A_index = 4)
            break
            ; TimeToGrabCurrentTab := A_TickCount - StartTime
            ; Msgbox % "TimeToGrabCurrentTab: " TimeToGrabCurrentTab

            ; Stop page refresh if it's happening
            ; try driver.executeScript("return window.stop")

            try Title := Driver.Title
            try URL := Driver.URL

            ; TimeToGrabCurrentTab := A_TickCount - StartTime
            ; Msgbox % "TimeToGrabCurrentTab: " TimeToGrabCurrentTab
            ; VAR := HasVal(DriverURLArray, STRINGVAR)    ; returns position of value in array
            if(HasVal(DriverURLArray, URL))    ; returns position of value in array
            {
                URLPositionInArray := HasVal(DriverURLArray, URL)
                if(URLPositionInArray = 1){
                    FirstURLPosition := 1
                }
                if(URLPositionInArray = 2){
                    SecondURLPosition := 1
                }
                if(URLPositionInArray = 3){
                    ThirdURLPosition := 1
                }
                if(URLPositionInArray = 4){
                    FourthURLPosition := 1
                }
                ; Msgbox % "PositionInArray: " PositionInArray
                ; ArrayContainsURL += 1
            }
            ; TimeToCheckArray := A_TickCount - StartTime

            ; Msgbox % "ArrayContainsURL: " ArrayContainsURL

            if(Title != "")
            DriverTitleArray.Push(Title)
            if(URL != "")
            DriverURLArray.Push(URL)

/*            
if(TotalTabsFound = 1){
                TimeToKickOut := A_TickCount - StartTime

                ; Msgbox % "TimeToGrabCurrentTab: " TimeToGrabCurrentTab
                ; Msgbox % "TimeToCheckArray: " TimeToCheckArray
                ; Msgbox % "TimeToKickOut: " TimeToKickOut
                ; MsgBox,  %ElapsedTime% milliseconds have elapsed.
                ; Msgbox % "A_index: " A_index
                ; DevModeMsgBox("breaking early on loop 1?")
                break 
                } ; if there is only 1 tab, then break out of loop and don't bother looping through same tab

                */
                try driver.SwitchToNextWindow               ;Focuses Selenium on the newly opened/next window. 
                } ; end of loop

                ; Iterate from 1 to the end of the array:
                Loop % DriverURLArray.Length() 
                {
                    ARRAYNAMEList .= DriverURLArray[A_Index] . "`n"
                    ; ArrayItem := DriverURLArray[A_Index]
                    ; MsgBox % DriverURLArray[A_Index]

                }
                ; Msgbox % "ARRAYNAMEList: " ARRAYNAMEList

                TimeToLoopThroughAllTabs := A_TickCount - StartTime
                Message = Time to Loop through all tabs: %TimeToLoopThroughAllTabs%
                SaveOrPostProgress(Message:=Message,PostType:="ErrorLoggingTextFile,DiscordErrorLogging")
                ; DevModeMsgBox(Message)
                return
                } ; /FUNC

                InputFilePathIntoOpenWindow(Filepath){
                    ; Msgbox % "Filepath: " Filepath
                    Message = Waiting for "Open" window to appear to input filepath into
                    SaveOrPostProgress(Message:=Message,PostType:="Tooltip,ErrorLoggingTextFile")
                    ; Tooltip,Waiting for "Open" window to appear to input filepath into,850,0
                    ; WinWait, Open
                    WinWait,Open,,5 ; Wait for 10 seconds for window
                    if(ErrorLevel)
                    {
                        ; msgbox, failed to find window.
                        Return "Failed"
                    }
                    WinActivate, Open
                    sleep, 1000
                    ControlSetText, Edit1, %Filepath%, Open
                    sleep, 1000
                    ControlSend, Edit1, {Enter}, Open
                    sleep, 1000

                    ; Do an extra check in case the Open window is still open.
                    OpenWindowExist := WinExist("Open")
                    if(OpenWindowExist)
                    ControlSend, Edit1, {Enter}, Open
                    ToolTip, 
                }

                ; -------------------------------NavigateFromBaseURLTo-------------------------------
                NavigateFromBaseURLTo(URL){
                    if(!DriverStatus){ ; if not connected to selenium chrome, then re-connect
                        Status := CheckSeleniumDriver()
                        if(Status)
                        Return "Failed"
                        
                        CreateArrayOfTabs() ; store all open tabs to memory for later usage
                    }


                    if(ReuseTabs){  ; pull out base of URL and check if it's within the array of tabs
                    URLBase := StrReplace(URL,"https://","")
                    URLBase := StrSplit(URLBase, "/")
                    URLBase := URLBase[1]
                    URLBase := StrReplace(URLBase, "www.","")
                }

                if(!HasSubstringVal(DriverURLArray, URLBase)) ; if base url is not in tab array, create new tab and push url to array
                {
                    try, run "%ChromeFilepath%" "%URL%"
                    DriverURLArray.Push(URL)    ; Append an item to the array
                    NewTabCreated := 1
                    sleep, 2000
                }
                ; msgbox,

                Message = Finding Tab
                SaveOrPostProgress(Message:=Message,PostType:="Tooltip,ErrorLoggingTextFile")

                TabFoundSuccessfully := 

                NumberOfTabLoops := DriverURLArray.Length()

                ; NumberOfTabLoops := Driver.Windows.Count + 2
                loop %  NumberOfTabLoops { ; loop through tabs to find matching tab
                   try driver.SwitchToNextWindow()   

                   ; sleep, 1000
                   ; msgbox, looping through tabs
                   try CurrentTabTitle := driver.window.title

                   ; caused by tab being manually closed. Not sure if this is the only cause though.
                   if(CurrentTabTitle = "") {
                    Message = CurrentTabTitle: %CurrentTabTitle%
                    DevModeMsgBox(Message)

                    Message = CurrenttabURL: %CurrentTabURL%
                    DevModeMsgBox(Message)

                    ; try driver.SwitchToNextWindow
                    Continue
                }


                try CurrentTabURL := driver.Url

                if(InStr(CurrentTabURL, URLBase)){
                    ; msgbox, found it.`n%CurrentTabTitle% = %PageTitle%`n%CurrentTabURL% = %URLBase%
                    TabFoundSuccessfully := 1
                    ; msgbox %CurrentTabTitle% = %PageTitle%
                    ; msgbox %CurrentTabURL% = %URL%
                    Break
                }
            } 

            if(!NewTabCreated){ ; if re-using a tab then we want to re-navigate to URL because it might be the wrong page
                try driver.Get(URL) ;Open selected URL
                try driver.executeScript("return document.readyState").equals("complete") ; wait until page loads completely before proceeding
                catch e {
                    ; msgbox, error caught. trying something
                    try driver.switchToalert().accept()
                    try driver.Get(URL) ;Open selected URL
                    catch e {
                        Message = Failed to Navigate to %URL%: Please Check for Any Open Dialogue Boxes
                        SaveOrPostProgress(Message:=Message,PostType:="Tooltip,ErrorLoggingTextFile,ErrorSummaryVar,DiscordErrorLogging")
                        Return "Failed"
                    }
                }
            }
            ; msgbox, no error caught
            ; }


            Return
        } 
        ; End of Function
        ; -------------------------------/NavigateFromBaseURL-------------------------------

        SaveDriverURL(){ ; save the url of the result page. That way if a tab is not found for a site, we can open up a tab from this tab instead of middle of nowhere. That way we can keep the tabs together
            try LastWebsitePostURL := driver.URL
        }


        ; -------------------------------SChrome_Get-------------------------------
        SChrome_Get(URL := "", Profile := "Profile 1", IP_Port := "127.0.0.1:9222"){
            IP_Port_Nr := RegExReplace(IP_Port, ".*:(\d*)", "$1")
            if WinExist("ahk_exe Chrome.exe"){
                WinGet, pid, PID, ahk_exe chrome.exe
                for item in ComObjGet("winmgmts:").ExecQuery("SELECT * FROM Win32_Process WHERE ProcessId='" pid "'"){
                    if RegExMatch(item.CommandLine, "i)--remote-debugging-port=\K\d+", port){
                        break
                    }
                }

                if (Port=""){
                    MsgBox, 36, ,Chrome Needs to be started in debugging mode in order for Autohotkey to connect to it.`nIs it ok to restart Chrome in debugmode to enable a connection?
                    IfMsgBox, Yes
                    {
                        Message = Restarting Chrome in Debug Mode
                        SaveOrPostProgress(Message:=Message,PostType:="Tooltip,ErrorLoggingTextFile,DiscordErrorLogging")

                        While(WinExist("ahk_exe chrome.exe")) {
                            WinClose, ahk_exe chrome.exe
                        }
                        Process, WaitClose, chrome.exe
                    }
                    Else{
                        Msgbox, 4096, Error, Cannot connect to Chrome profile if it is Not running in debug mode. Script Terminating
                        ExitApp
                        ; @todo: Make this error out to the script result screen
                    }
                }
            }

            ; ; yuriy's settings
            ; IniRead, ChromeFilepath, C:\Users\%A_username%\Documents\Autohotkey\Lib\ScriptSettings.ini, Selenium, %A_Computername%, %A_Space%

            ; ; Establish Variable with Filepath to be used throughout the script
            ; if(ChromeFilepath = ""){
                if(FileExist("C:\Program Files\Google\Chrome\Application\chrome.exe")){
                    ChromeFilepath = C:\Program Files\Google\Chrome\Application\chrome.exe
                }
                else if (FileExist("C:\Program Files (x86)\Google\Chrome\Application\chrome.exe")){
                    ChromeFilepath = C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
                }
                

                if(ChromeFilepath = ""){
                    Message = Failed to find chrome.exe in the usual locations.
                    SaveOrPostProgress(Message:=Message,PostType:="Tooltip,ErrorLoggingTextFile,DiscordErrorLogging")
                    MsgBox 0x30, Error!, Unable to find Chrome.exe in the usual locations. `nScript Exiting. 
                    ExitApp
                }

                if(!winExist("ahk_exe chrome.exe")){
                    message = %ChromeFilepath% --remote-debugging-port=%IP_Port_Nr% %URL%
                    Clipboard := Message
                    DevModeMsgBox(message)
                    run, %ChromeFilepath% --remote-debugging-port=%IP_Port_Nr% %URL%
                }

                Driver := ComObjCreate("Selenium.ChromeDriver")
                Driver.SetCapability("debuggerAddress", IP_Port)
                try Driver.Start()
                catch e {

                    ShowSeleniumErrorMsgbox()

                    } ; end of catch

                    ; Save current chrome version to ini file
                    return Driver
                }
                ; -------------------------------/SChrome_Get-------------------------------

                ShowSeleniumErrorMsgbox(){
                    GetChromeVersionCommand = powershell (Get-Item '%ChromeFilepath%').VersionInfo.ProductVersion
                    Chromeversion := RunCMD(GetChromeVersionCommand)
                    ChromeVersion := StrReplace(ChromeVersion, "`n", "")

                    ; Clipboard := ChromeVersion
                    ; Msgbox % "Chromeversion: " Chromeversion
                    IniRead, PreviousWorkingChromeVersion, Settings.ini, Misc, ChromeVersion, %A_Space%
                    ; IniWrite, %ChromeVersion%, Settings.ini, Misc, ChromeVersion
                    ; msgbox, failed to connect to Chrome for some reason.
                    ; Message = Failed to Connect to Chrome. Most likely problem is either Chrome has some sort of dialogue box open or ChromeDriver needs to be updated.
                    ; SaveOrPostProgress(Message:=Message,PostType:="Tooltip,ErrorLoggingTextFile,ErrorSummaryVar,DiscordErrorLogging")

                    OnMessage(0x44, "OnMsgBoxChromeDriverFailed")
                    MsgBox 0x40043, Error, Failed to Connect to Chrome. `nMost likely issue is either Chrome has some sort of dialogue box open or ChromeDriver needs to be updated.`n`nClick "Reload" to reload the script to try again`nClick "ChromeDriver" to open up the ChromeDriver download page. `n`nClick "Instructions" to open up .pdf file with instructions for updating chromedriver.exe`n`nPossibly Helpful Info:`nCurrent Chrome Version: %Chromeversion%Chrome Version of Last Successfull Upload: %PreviousWorkingChromeVersion%
                    OnMessage(0x44, "")

                    IfMsgBox Yes, {
                        ; Reload script
                        Reload
                        } Else IfMsgBox No, {
                            run, https://chromedriver.chromium.org/downloads
                            ExitApp
                            ; open chromedriver site
                            } Else IfMsgBox Cancel, {
                                URL = https://freedomainplaylists.com/wp-content/FreedomainScripts/Update`%20Selenium`%20ChromeDriver.pdf
                                Filepath = %A_ScriptDir%\Lib\Update Selenium ChromeDriver.pdf
                                if(!FileExist(Filepath)){
                                    UrlDownloadToFile, %URL%, %Filepath%

                                    run, %Filepath%
                                }
                                ExitApp

                            }
                        }


                        ; -------------------------------HasVal-------------------------------
                        ; Function needed for finding a value in an array
                        HasVal(haystack, needle)
                        {
                            if !(IsObject(haystack)) || (haystack.Length() = 0)
                            return 0
                            for index, value in haystack
                            if (value = needle)
                            return index
                            return 0
                        }
                        ; -------------------------------/HasVal-------------------------------

                        HasSubstringVal(haystack, needle)
                        {
                            if !(IsObject(haystack)) || (haystack.Length() = 0)
                            return 0
                            for index, value in haystack
                            if (InStr(value, Needle))
                            return index
                            return 0
                        }

                        ; SubStr(String, StartingPos [, Length])

                        ; InStr(value, Needle)






                        ; -------------------------------FileXPro Get File Attributes-------------------------------
                        ;https://www.autohotkey.com/boards/viewtopic.php?t=59882

                        Filexpro( sFile := "", Kind := "", P* ) {           ; v.90 By SKAN on D1CC @ goo.gl/jyXFo9
                            Local
                            Static xDetails 

                            If ( sFile = "" )
                            {                                                           ;   Deinit static variable
                                xDetails := ""
                                Return
                            }

                            fex := {}, _FileExt := "" 

                            Loop, Files, % RTrim(sfile,"\*/."), DF 
                            {
                                If not FileExist( sFile:=A_LoopFileLongPath )
                                {
                                  Return
                              } 

                              SplitPath, sFile, _FileExt, _Dir, _Ext, _File, _Drv

                              If ( p[p.length()] = "xInfo" )                          ;  Last parameter is xInfo 
                              {
                                  p.Pop()                                           ;         Delete parameter
                                  fex.SetCapacity(11)                               ; Make room for Extra info
                                  fex["_Attrib"]    := A_LoopFileAttrib
                                  fex["_Dir"]       := _Dir
                                  fex["_Drv"]       := _Drv
                                  fex["_Ext"]       := _Ext
                                  fex["_File"]      := _File
                                  fex["_File.Ext"]  := _FileExt
                                  fex["_FilePath"]  := sFile
                                  fex["_FileSize"]  := A_LoopFileSize
                                  fex["_FileTimeA"] := A_LoopFileTimeAccessed
                                  fex["_FileTimeC"] := A_LoopFileTimeCreated
                                  fex["_FileTimeM"] := A_LoopFileTimeModified
                              }              
                              Break            
                          }

                          If Not ( _FileExt )                                   ;    Filepath not resolved
                          {
                            Return
                        }        


                        objShl := ComObjCreate("Shell.Application")
                        objDir := objShl.NameSpace(_Dir) 
                        objItm := objDir.ParseName(_FileExt) 

                        If ( VarSetCapacity(xDetails) = 0 )                           ;     Init static variable
                        {
                            i:=-1,  xDetails:={},  xDetails.SetCapacity(309)

                            While ( i++ < 309 )
                            {
                                xDetails[ objDir.GetDetailsOf(0,i) ] := i
                            } 

                            xDetails.Delete("")
                        }

                        If ( Kind and Kind <> objDir.GetDetailsOf(objItm,11) )        ;  File isn't desired kind  
                        {
                            Return
                        }

                        i:=0,  nParams:=p.Count(),  fex.SetCapacity(nParams + 11) 

                        While ( i++ < nParams )
                        {
                            Prop := p[i]

                            If ( (Dot:=InStr(Prop,".")) and (Prop:=(Dot=1 ? "System":"") . Prop) )
                            {
                              fex[Prop] := objItm.ExtendedProperty(Prop)
                              Continue 
                          }

                          If ( PropNum := xDetails[Prop] ) > -1
                          {
                              fex[Prop] := ObjDir.GetDetailsOf(objItm,PropNum)
                              Continue 
                          }  
                      }

                      fex.SetCapacity(-1)
                      Return fex  

                      } ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -



                      ; GuiButtonIcon
                      ;------------------------------------------------
                      GuiButtonIcon(Handle, File, Index := 1, Options := "")
                      {
                        RegExMatch(Options, "i)w\K\d+", W), (W="") ? W := 16 :
                        RegExMatch(Options, "i)h\K\d+", H), (H="") ? H := 16 :
                        RegExMatch(Options, "i)s\K\d+", S), S ? W := H := S :
                        RegExMatch(Options, "i)l\K\d+", L), (L="") ? L := 0 :
                        RegExMatch(Options, "i)t\K\d+", T), (T="") ? T := 0 :
                        RegExMatch(Options, "i)r\K\d+", R), (R="") ? R := 0 :
                        RegExMatch(Options, "i)b\K\d+", B), (B="") ? B := 0 :
                        RegExMatch(Options, "i)a\K\d+", A), (A="") ? A := 4 :
                        Psz := A_PtrSize = "" ? 4 : A_PtrSize, DW := "UInt", Ptr := A_PtrSize = "" ? DW : "Ptr"
                        VarSetCapacity( button_il, 20 + Psz, 0 )
                        NumPut( normal_il := DllCall( "ImageList_Create", DW, W, DW, H, DW, 0x21, DW, 1, DW, 1 ), button_il, 0, Ptr )   ; Width & Height
                        NumPut( L, button_il, 0 + Psz, DW )     ; Left Margin
                        NumPut( T, button_il, 4 + Psz, DW )     ; Top Margin
                        NumPut( R, button_il, 8 + Psz, DW )     ; Right Margin
                        NumPut( B, button_il, 12 + Psz, DW )    ; Bottom Margin 
                        NumPut( A, button_il, 16 + Psz, DW )    ; Alignment
                        SendMessage, BCM_SETIMAGELIST := 5634, 0, &button_il,, AHK_ID %Handle%
                        return IL_Add( normal_il, File, Index )
                    }

                    ; \GuiButtonIcon
                    ;------------------------------------------------


                    ToggleTestingMode(){

                        IniRead, TestingMode, Settings.ini, General, TestingMode, 0
                        ; Msgbox % "TestingMode: " TestingMode
                        if(TestingMode)
                        IniWrite, 0, Settings.ini, General, TestingMode
                        else, 
                        IniWrite, 1, Settings.ini, General, TestingMode
                    }

                    ToggleManualSubmit(){

                        IniRead, ManualSubmit, Settings.ini, General, ManualSubmit, 0
                        ; Msgbox % "TestingMode: " TestingMode
                        if(ManualSubmit)
                        IniWrite, 0, Settings.ini, General, ManualSubmit
                        else, 
                        IniWrite, 1, Settings.ini, General, ManualSubmit
                    }


                    ToggleDevMode(){

                        IniRead, DevMode, Settings.ini, General, DevMode, 0
                        ; Msgbox % "TestingMode: " TestingMode
                        if(DevMode)
                        IniWrite, 0, Settings.ini, General, DevMode
                        else, 
                        IniWrite, 1, Settings.ini, General, DevMode
                    }


                    FormatTextToJSText(Var){ ; Replaces AHK newline characters with javascript ones
/*\b  Backspace
\f  Form Feed
\n  New Line - done
\r  Carriage Return 
\t  Horizontal Tabulator
\v  Vertical Tabulator
\'  Single quote - done
\"  Double quote - done
\\  Backslash - done
*/

SingleQuotationmark = "
; Variable of Escaped Symbols
EscapedSingleQuote = \'
EscapedDoubleQuote = \"
EscapedBackslash = \\
EscapedNewLine = \n

; Replace each character that needs replacing in the text string
Var := StrReplace(Var, "\", EscapedBackslash)  ; needed otherwise selenium will error out
Var := StrReplace(Var, "`n", EscapedNewLine)    ; needed otherwise selenium will error out
Var := StrReplace(Var, "`r", "")  ; needed otherwise selenium will error out
Var := StrReplace(Var, SingleQuotationmark, EscapedDoubleQuote)  ; needed otherwise selenium will error out
Var := StrReplace(Var, "'", EscapedSingleQuote)  ; needed otherwise selenium will error out
; Var := StrReplace(Var, "`r", "")  ; needed otherwise selenium will error out
Return Var
}



; OnMsgbox Functions
;------------------------------------------------
OnMsgBoxUpdateAvailable() {
    DetectHiddenWindows, On
    Process, Exist
    If (WinExist("ahk_class #32770 ahk_pid " . ErrorLevel)) {
        ControlSetText Button1, Cancel
        ControlSetText Button2, Install
    }
}


OnMsgBoxChromeDriverFailed() {
    DetectHiddenWindows, On
    Process, Exist
    If (WinExist("ahk_class #32770 ahk_pid " . ErrorLevel)) {
        WinMove,, 0
        ControlSetText Button1, Reload Script
        ControlSetText Button2, ChromeDriver
        ControlSetText Button3, Instructions
    }
}


SaveCurrentChromeVersionToIniFile(){

    ; Msgbox % "ChromeFilepath: " ChromeFilepath
    GetChromeVersionCommand = powershell (Get-Item '%ChromeFilepath%').VersionInfo.ProductVersion
    Chromeversion := RunCMD(GetChromeVersionCommand)
    if(InStr(ChromeVersion, "scripts is disabled on this system")){
        ChromeVersion = Not Able to Grab Because: "scripts are disabled in powershell"
    }
    ; Msgbox % "Chromeversion: " Chromeversion

    ; Msgbox % "Chromeversion: " Chromeversion


    if(ChromeFilepath = "")
    return

    ; Chromeversion := StrSplit(Chromeversion, "`n")
    ; Chromeversion := Chromeversion[4]
    IniWrite, %ChromeVersion%, Settings.ini, Misc, ChromeVersion
}


SaveDriverURLOFErrorPage(){ ; save the url of the result page. That way if a tab is not found for a site, we can open up a tab from this tab instead of middle of nowhere. That way we can keep the tabs together
    try URLOfLastErrorPage := driver.URL
}



FindAndActivateTab(TabURL){
    DevModeMsgBox(TabURL)
    Loop, {
        if(A_index = 30)
        break

        try CurrentTabTitle := driver.window.title
        if(CurrentTabTitle = "") ; caused by tab being manually closed. Not sure if this is the only cause though.
        {
            ; DevModeMsgBox("currenttabtutle is blank")
            try driver.SwitchToNextWindow
            Continue
        }

        ; DevModeMsgBox(CurrentTabTitle)
        try CurrentTabURL := driver.Url
        DevModeMsgBox(CurrentTabURL)

        if(CurrentTabURL = TabURL)
        Break

        try driver.SwitchToNextWindow()   

    }

}
