; ENVIRONMENT
;------------------------------------------------
#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
;#Warn  ; Enable warnings to assist with detecting common errors.
;DetectHiddenWindows, On
#SingleInstance, Force
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.
;SetKeyDelay, 500
CoordMode, ToolTip, Screen
CoordMode, Mouse, Screen
; #NoTrayIcon

; MyGlobalVars() ; import custom global variables
;Menu, Tray, Icon, %A_AHKSyncthingDir%\Icons\ICONNAMEHERE

; Notes/Extra Info/#Includes
;------------------------------------------------
#Include %A_scriptdir%\Libs\RunCMD.ahk


; ENVIRONMENT
;------------------------------------------------
#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
#SingleInstance, Force
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.

Argument = %1%

; Icon taken from: https://github.com/PapirusDevelopmentTeam/papirus-icon-theme/tree/master/Papirus
Menu, Tray, Icon, %A_scriptdir%/icon.ico

;------------------------------------------------
; SETTINGS AND VARIABLES
;------------------------------------------------
; Filepaths of Browsers

Brave = C:\Users\%A_Username%\AppData\Local\BraveSoftware\Brave-Browser\Application\brave.exe
BraveIncognito = %Brave% --incognito

Chrome = C:\Program Files\Iridium\iridium.exe
ChromeIncognito = %Chrome% --incognito

; IniRead, Chromium, C:\Program Files\Iridium\iridium.exe
; Msgbox % "Chromium: " Chromium
Chromium = C:\Program Files\Iridium\iridium.exe
ChromiumIncognito = %Chromium% --incognito

Firefox = C:\Program Files\Mozilla Firefox\firefox.exe
FirefoxPrivate = %Firefox% -private-window

Librewolf = C:\Program Files\LibreWolf\librewolf.exe
LibrewolfPrivate = %Librewolf% -private-window

; https://codeberg.org/strubbl/wallabag-add-article
WallabagAddArticleExe = %A_scriptdir%\Libs\wallabag-add-article.exe


; Pocket Settings
PocketAccessToken=
PocketConsumerKey=

; Telegram Settings
TelegramChatID=
TelegramBotToken=


; KDE Connect Settings
KDEConnectCLIExeFilepath=C:\Program Files\KDE Connect\bin\kdeconnect-cli.exe
KDEConnectDeviceOne=OP7
KDEConnectDeviceTwo=S4
KDEConnectDeviceThree=

; yt-dlp Settings
AudioDownloadFolder = C:\Users\%A_UserName%\Downloads
VideoDownloadFolder = C:\Users\%A_UserName%\Downloads
VideoResolution = 720
AudioQuality = Best  ; Best OR MP3


; Todo.txt filepath
todotxtFilepath = C:\Users\%A_UserName%\Syncthing\WhiteMountain\Todo\WMT-todo.txt


; Trigger Within These Applications Only
GroupAdd, GroupOfBrowsers, ahk_exe firefox.exe
GroupAdd, GroupOfBrowsers, ahk_exe chrome.exe
GroupAdd, GroupOfBrowsers, ahk_exe brave.exe
GroupAdd, GroupOfBrowsers, ahk_exe librewolf.exe
GroupAdd, GroupOfBrowsers, ahk_exe iridium.exe

#IfWinActive, ahk_group GroupOfBrowsers


; Key Shortcuts:
;Complete List can be found at: 
; https://autohotkey.com/docs/KeyList.htm#Advanced_buttons
;# : Windows
;+ : Shift
;^ : Ctrl
;! : Alt

; Open with Browser 
; Function arguments should match the variable that contains your browser filepath 
!f::OpenWithBrowser(Firefox) 
!+f::OpenWithBrowser(FirefoxPrivate) 

!b::OpenWithBrowser(Brave)
!+b::OpenWithBrowser(BraveIncognito)

; !+c::OpenWithBrowser(Chromium)
!c::OpenWithBrowser(ChromiumIncognito)

; Open with Archive.org
!a::OpenWithArchiveOrg("SameTab")
!+a::OpenWithArchiveOrg("NewTab")

; KDE Connect
!o::OpenOnKDEConnectDevice(KDEConnectDeviceOne, KDEConnectCLIExeFilepath)
!+o::OpenOnKDEConnectDevice(KDEConnectDeviceTwo, KDEConnectCLIExeFilepath)

; yt-dlp 
!y::YouTubeDL("Video", VideoResolution, VideoDownloadFolder)
!+y::YouTubeDL("Audio", AudioQuality, AudioDownloadFolder)

; pocket
; !p::SavePageToPocket(PocketConsumerKey, PocketAccessToken)

; Wallabag
!p::SavePageToWallabag(WallabagAddArticleExe)

!d::SaveToLinkDing()

; Telegram
!m::SaveToTelegram(TelegramBotToken, TelegramChatID)

!+m::SaveToTelegram(TelegramBotToken, TelegramChatID)

!t::SaveToTelegram(TelegramBotToken, TelegramChatID)


; Clipboard
!x::CopyURLToClipboard()


!q::EmailWithOutlook()
#if


; Functions
;------------------------------------------------
EmailWithOutlook(){
	TabURL := GetURLofActiveTab()
	if(TabURL = "")
	Return

	WindowTitle := GetTitleofActiveTab()

	; If outlook is NOT currently open, start it
	If !WinExist("ahk_class rctrl_renwnd32") ;
	{
		run, Outlook
		WinWait, ahk_class rctrl_renwnd32
		sleep, 500	; Wait for animation to catch up
	}

	; Create outlook COM connection
	Ol := ComObjCreate("Outlook.Application").CreateItem(0) ;Connect to Outlook object		
	; Ol.TO := "test@gmail.com"	;Send to Variable content that resides within "Email"

	Ol.Subject := WindowTitle

	Ol.HTMLBody := TabURL
	Ol.Display ; Create gui window of Outlook message
	; Ol.Send() ;Sends the email

	; CenterTooltipOnScreen("URL Emailed with Outlook", 1000)

	Return
}


CopyURLToClipboard(){
	TabURL := GetURLofActiveTab()
	if(TabURL = "")
	Return

	Clipboard := TabURL

	CenterTooltipOnScreen("Tab URL Copied to Clipboard Successfully", 1000)
	; ShowTooltip("Tab URL Copied to Clipboard Successfully", 1000)
	return
}


AppendToDoTxt(todotxtFilepath){
	TabURL := GetURLofActiveTab()
	if(TabURL = "")
	Return

	WindowTitle := GetTitleofActiveTab()

	InputBox, WindowTitle, Note Title: , Please Input Note Title:,,,,,,,,%WindowTitle%
	if ErrorLevel
	return

	FormatTime, todaydate,,yyyy-MM-dd
	FileAppend, `n%todaydate% %WindowTitle%: %TabURL% @rl,%todotxtFilepath%

	return
}


YouTubeDL(Type, Quality, DownloadFolder){
	; Requires yt-dlp to be installed
	; https://github.com/yt-dlp/yt-dlp

	TabURL := GetURLofActiveTab()
	if(TabURL = "")
	Return

	if(Type = "Video")
	Command = yt-dlp -f "bestvideo[height<=%Quality%]+bestaudio" --embed-thumbnail --add-metadata --no-mtime -o `"%DownloadFolder%\`%(title)s.`%(ext)s`" %TabURL%
	if(Type = "Audio"){
		if(Quality = "Best")
		command = yt-dlp -f bestaudio --extract-audio --embed-thumbnail --add-metadata --no-mtime -o `"%DownloadFolder%\`%(title)s.`%(ext)s`" %TabURL%
		else, 
		command = yt-dlp --extract-audio --audio-format mp3 --audio-quality 5 --no-mtime -o `"%DownloadFolder%\`%(title)s.`%(ext)s`" %TabURL%
	}

	run, %comspec% /C %command%
	Return
}


OpenWithArchiveOrg(TabOption){
	TabURL := GetURLofActiveTab()
	if(TabURL = "")
	Return

	NewURL = https://web.archive.org/web/2/%TabURL%

	if(TabOption = "SameTab"){
		send, %NewURL%
		send, {Enter}
	}

	if(TabOption = "NewTab"){
		send, ^t ; new tab
		sleep, 250
		send, %NewURL%
		send, {Enter}
	}
	return
}

SaveToTelegram(TelegramBotToken, TelegramChatID){
	TabURL := GetURLofActiveTab()
	if(TabURL = "")
	Return

	PostURL = https://api.telegram.org/bot%TelegramBotToken%/sendmessage?chat_id=%TelegramChatID%&text=%TabURL%
	PostStatus := URLDownloadToVar(PostURL)
	
	SuccessString = "message_id"
	if(!InStr(PostStatus, SuccessString)){
		MsgBox, Failed to Submit Post
		return
	}

	; MsgBox
	CenterTooltipOnScreen("Tab Posted to Telegram Successfully", 1000)
	; TrayTip, Browser Tab, Tap Posted to Telegram Successfully, 5 ;, Seconds, Options]
	; msgbox
	; ShowTooltip("Tab Posted to Telegram Successfully", 1000)
	return
}


SaveToLinkDing(){
	; Requires linkding extension to be installed
	; https://addons.mozilla.org/en-US/firefox/addon/linkding-extension/

	; TabURL := GetURLofActiveTab()
	TabTitle := GetTitleofActiveTab()
	Clipboard := TabTitle

	send, !+l

	return
}



SavePageToWallabag(WallabagAddArticleExe){
	; requires
	; https://codeberg.org/strubbl/wallabag-add-article

	TabURL := GetURLofActiveTab()
	if(TabURL = "")
	Return

	; Msgbox % "TabURL: " TabURL
	command = "%WallabagAddArticleExe%" %TabURL%
	Status := RunCMD(command)

	TrayTip, Browser Tab, Tap Posted to wallabag Successfully, 5 ;, Seconds, Options]

	; Clipboard := command
	; run, %comspec% /k "%WallabagAddArticleExe%" %TabURL%
	; msgbox
	; run, %WallabagAddArticleExe% %TabURL%
	; msgbox 
}


SavePageToPocket(PocketConsumerKey, PocketAccessToken){
	; https://getpocket.com/developer/docs/v3/add
	TabURL := GetURLofActiveTab()
	if(TabURL = "")
	Return

	url_str := "https://getpocket.com/v3/add"
	objParam := { "url" : TabURL                                                                              
	,"consumer_key" : PocketConsumerKey
	,"access_token" : PocketAccessToken }

	Status := SubmitJsonObject(url_str, objParam)

	SuccessStatusCode = response_code":"200"
	if(!InStr(Status, SuccessStatusCode)){
		MsgBox, Failed to Submit to Pocket
		return
	}

	CenterTooltipOnScreen("Tab Saved to Pocket Successfully", 1000)
	return
}

ShowTooltip(Text, LengthOfTime){
	ToolTip, %Text%
	sleep, %LengthOfTime%
	ToolTip
	return
}

OpenOnKDEConnectDevice(DeviceID, KDEConnectCLIExeFilepath){
	TabURL := GetURLofActiveTab()
	if(TabURL = "")
	Return

	command = "%KDEConnectCLIExeFilepath%" -n "%DeviceID%" --share "%TabURL%"
	; Clipboard := command
	Status := RunCMD(command)

	if(InStr(Status, "error") OR InStr(Status, "Couldn't find device")){
		MsgBox, Failed to Send URL.`nIs Device Connected?
		return
	}
	CenterTooltipOnScreen("Tab Sent Successfully", 1000)

	return
}


OpenWithBrowser(BrowserFilepath){
	TabURL := GetURLofActiveTab()
	if(TabURL = "")
	Return

	run, %BrowserFilepath% %TabURL%
}


GetURLofActiveTab(){
	ClipBackup := ClipboardAll ; Backup the clipboard contents
	Clipboard :=	; Empty clipboard
	send, {Escape} ; send the escape key to click out of ALT menu in case user is using an ALT+Letter shortcut

	Loop, 5 {
		send, ^l  ; Send control + l ; Default shortcut to select URL bar in most browsers
		sleep, 250
		send, ^c  ; send control + c
		ClipWait, .5 ; Wait for clipboard to get contents
		if(ErrorLevel){ ; if error, wait .5 seconds and try sending control + c again
			sleep, 500
			Continue
		}
		else, 
		break
	}
	send, {Escape} ; escape out of url bar activation

	TabURL := Clipboard

	Clipboard := ClipBackup ; restore clipboard contents
	Return TabURL
}

GetTitleofActiveTab(){
	WinGetTitle, WindowTitle, A
	WindowTitle := StrReplace(WindowTitle, "LibreWolf", "")
	; StrReplace(Haystack, SearchText [, ReplaceText, OutputVarCount, Limit := -1])
	WindowTitle := StrReplace(WindowTitle, "Brave", "")
	WindowTitle := StrReplace(WindowTitle, "Chromium", "")
	WindowTitle := StrReplace(WindowTitle, "Chrome", "")
	WindowTitle := StrReplace(WindowTitle, "GitHub - ", "")
	TitleLen := StrLen(WindowTitle)
	TitleLen := TitleLen - 3
	WindowTitle := SubStr(WindowTitle, 1, TitleLen)
	; WindowTitle := StrReplace(WindowTitle, "—", "")
	; Msgbox % "WindowTitle: " WindowTitle

	return WindowTitle
}



; -------------------------------
; CreateFormData - Creates "multipart/form-data" for http post
; Needed for Pocket Posting
; -------------------------------
SubmitJsonObject(url_str, objParam){
	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 := 

	; Clipboard := json_resp                                             ; free COM object
	return json_resp

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

; 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-------------------------------
