--$$$GSEScript$$$
--GSEVersion: 14.10.0.0
-----------------------------------------------------------------------------------------------------------------------------------
--  AzureTranslate (Lua Version)
--  v1.0.1
--
--  This is a Swyx Forum Open Source Project.
--  https://www.swyxforum.com/archive/projects/
--
--  The MIT License (MIT)
--  Copyright (c) 2024-25 by Swyx Forum
--  Copyright (c) 2024-25 by Tom Wellige
--  All Rights Reserved.
--
--  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 
--  documentation files (the "Software"), to deal in the Software without restriction, including without limitation 
--  the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and 
--  to permit persons to whom the Software is furnished to do so, subject to the following conditions:
--
--  The above copyright notice and this permission notice must be included in all copies or substantial portions 
--  of the Software.
--
--  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 
--  TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
--  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 
--  CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
--  DEALINGS IN THE SOFTWARE.
--
-----------------------------------------------------------------------------------------------------------------------------------

local AZURETRANSLATE_BUILD_CNT            = "1.0.1"
local AZURETRANSLATE_URL_TOKEN            = "https://#REGION#.api.cognitive.microsoft.com/sts/v1.0/issueToken"
local AZURETRANSLATE_URL_TRANSLATE        = "https://api.cognitive.microsofttranslator.com/translate?api-version=3.0"

local AZURETRANSLATE_RETURN_OK            = "0"
local AZURETRANSLATE_RETURN_FAILED        = "1"

local AZURETRANSLATE_ERROR_NONE           = 0
local AZURETRANSLATE_ERROR_PARAMETER      = 1
local AZURETRANSLATE_ERROR_WEBREQUEST     = 2
local AZURETRANSLATE_ERROR_FILESYSTEM     = 3

-- translation results also as global variables (for easy use with GSE actions)
g_sAzureTranslate_LatestTranslation                = ""
g_sAzureTranslate_LatestDetectedLanguage           = ""
g_sAzureTranslate_LatestDetectedLanguageConfidence = ""


AzureTranslateClass = {}
AzureTranslateClass.__index = AzureTranslateClass

---------------------------------------------------------------------
-- Name:     Create
--           ======
-- 
-- Create and initialize local/private variables.
--
-- Parameter:
--      none
--
-- return value:
--      none
--
----------------------------------------------------------------------
function AzureTranslateClass:Create()

    PBXScript.OutputTrace ("-------> AzureTranslate:Create")
    PBXScript.OutputTrace ("Version " .. AZURETRANSLATE_BUILD_CNT .. " (Lua)")

    local this =
    {

        -- gse action
        m_sResult                = AZURETRANSLATE_RETURN_OK,

        -- rest api
        m_sRegion                = "westeurope",
        m_sSubscriptionKey       = "",
        m_sAccessToken           = "",
        m_nLatestResponseCode    = 0,

        -- text
        m_sSourceText            = "",
        m_sSourceLanguage        = "",
        m_sTargetText            = "",
        m_sTargetLanguage        = "",

        m_sDetectedLanguage      = "",
        m_sDetectedLanguageScore = "",

        -- errors
        m_nLastError             = AZURETRANSLATE_ERROR_NONE,
        m_sLastErrorDescr        = ""

    }

    setmetatable(this, AzureTranslateClass)

    PBXScript.OutputTrace ("<------- AzureTranslate:Create")
    return this
end


---------------------------------------------------------------------
-- Property handling methods
---------------------------------------------------------------------

-- sVersion
function AzureTranslateClass:sVersion()
    -- get property
    PBXScript.OutputTrace ("Get AzureTranslate:nResult = " .. AZURETRANSLATE_BUILD_CNT)
    return AZURETRANSLATE_BUILD_CNT
end

-- nResult
function AzureTranslateClass:nResult()
    -- get property
    PBXScript.OutputTrace ("Get AzureTranslate:nResult = " .. self.m_sResult)
    return self.m_sResult
end

-- nLatestResponseCode
function AzureTranslateClass:nLatestResponseCode()
    -- get property
    PBXScript.OutputTrace ("Get AzureTranslate:nLatestResponseCode = " .. self.m_nLatestResponseCode)
    return self.m_nLatestResponseCode
end

-- sRegion
function AzureTranslateClass:sRegion(s)
    if (s == nil) then
        -- get property
        s = self.m_sRegion
        PBXScript.OutputTrace ("Get AzureTranslate:sRegion = " .. s)
        return s
    else
        -- set property
        s = StringTrim(s)
        self.m_sRegion = s
        PBXScript.OutputTrace ("Set AzureTranslate:sRegion = " .. s)
    end
end

-- sSubscriptionKey
function AzureTranslateClass:sSubscriptionKey(s)
    if (s == nil) then
        -- get property
        s = self.m_sSubscriptionKey
        PBXScript.OutputTrace ("Get AzureTranslate:sSubscriptionKey = **** HIDDEN ****")
        return s
    else
        -- set property
        s = StringTrim(s)
        self.m_sSubscriptionKey = s
        PBXScript.OutputTrace ("Set AzureTranslate:sSubscriptionKey = **** HIDDEN ****")
    end
end

-- sSourceText
function AzureTranslateClass:sSourceText(s)
    if (s == nil) then
        -- get property
        s = self.m_sSourceText
        PBXScript.OutputTrace ("Get AzureTranslate:sSourceText = " .. s)
        return s
    else
        -- set property
        s = StringTrim(s)
        self.m_sSourceText = s
        PBXScript.OutputTrace ("Set AzureTranslate:sSourceText = " .. s)
    end
end

-- sSourceLanguage
function AzureTranslateClass:sSourceLanguage(s)
    if (s == nil) then
        -- get property
        s = self.m_sSourceLanguage
        PBXScript.OutputTrace ("Get AzureTranslate:sSourceLanguage = " .. s)
        return s
    else
        -- set property
        s = StringTrim(s)
        self.m_sSourceLanguage = s
        PBXScript.OutputTrace ("Set AzureTranslate:sSourceLanguage = " .. s)
    end
end

-- sTargetText
function AzureTranslateClass:sTargetText(s)
    -- get property
    PBXScript.OutputTrace ("Get AzureTranslate:sTargetText = " .. self.m_sTargetText)
    return self.m_sTargetText
end

-- sTargetLanguage
function AzureTranslateClass:sTargetLanguage(s)
    if (s == nil) then
        -- get property
        s = self.m_sTargetLanguage
        PBXScript.OutputTrace ("Get AzureTranslate:sTargetLanguage = " .. s)
        return s
    else
        -- set property
        s = StringTrim(s)
        self.m_sTargetLanguage = s
        PBXScript.OutputTrace ("Set AzureTranslate:sTargetLanguage = " .. s)
    end
end

-- sDetectedLanguage
function AzureTranslateClass:sDetectedLanguage()
    -- get property
    PBXScript.OutputTrace ("Get AzureTranslate:sDetectedLanguage = " .. self.m_sDetectedLanguage)
    return self.m_sDetectedLanguage
end

-- sDetectedLanguageScore
function AzureTranslateClass:sDetectedLanguageScore()
    -- get property
    PBXScript.OutputTrace ("Get AzureTranslate:sDetectedLanguageScore = " .. self.m_sDetectedLanguageScore)
    return self.m_sLastErrorDescr
end

-- nLastError
function AzureTranslateClass:nLastError()
    -- get property
    PBXScript.OutputTrace ("Get AzureTranslate:nLastError = " .. self.m_nLastError)
    return self.m_nLastError
end

-- sLastErrorDescr
function AzureTranslateClass:sLastErrorDescr()
    -- get property
    PBXScript.OutputTrace ("Get AzureTranslate:sLastErrorDescr = " .. self.m_sLastErrorDescr)
    return self.m_sLastErrorDescr
end


---------------------------------------------------------------------
-- Private Functions
---------------------------------------------------------------------


---------------------------------------------------------------------
--
-- Clear
--
-- Clear/initializes all "temp" variables.
--
-- Parameter:
--
-- Returns:
--
---------------------------------------------------------------------
function AzureTranslateClass:Clear()
    PBXScript.OutputTrace ("-------> AzureTranslate:Clear()")

    self.m_sResult                = AZURETRANSLATE_RETURN_OK
    self.m_sAccessToken           = ""
    self.m_nLatestResponseCode    = 0
    self.m_nLastError             = AZURETRANSLATE_ERROR_NONE
    self.m_sLastErrorDescr        = ""

    self.m_sTargetText            = ""
    self.m_sDetectedLanguage      = ""
    self.m_sDetectedLanguageScore = ""

    PBXScript.OutputTrace ("<------- AzureTranslate:Clear()")
end


---------------------------------------------------------------------
--
-- CheckParameters
--
-- Checks if all needed parameters have been provided.
--
-- Parameter:
--
-- Returns:
--      m_sResult       AZURETRANSLATE_RETURN_OK, AZURETRANSLATE_RETURN_FAILED
--      m_nLastError    AZURETRANSLATE_ERROR_NONE, AZURETRANSLATE_ERROR_PARAMETER
--
---------------------------------------------------------------------
function AzureTranslateClass:CheckParameters()
    PBXScript.OutputTrace ("-------> AzureTranslate:CheckParameters()")

    if (self.m_sRegion == "") then
        self.m_sResult         = AZURETRANSLATE_RETURN_FAILED
        self.m_nLastError      = AZURETRANSLATE_ERROR_PARAMETER
        self.m_sLastErrorDescr = "Invalid sRegion parameter! Must not be empty."
        PBXScript.OutputTrace (self.m_sLastErrorDescr)
    end

    if (self.m_sSubscriptionKey == "") then
        self.m_sResult         = AZURETRANSLATE_RETURN_FAILED
        self.m_nLastError      = AZURETRANSLATE_ERROR_PARAMETER
        self.m_sLastErrorDescr = "Invalid sSubscriptionKey parameter! Must not be empty."
        PBXScript.OutputTrace (self.m_sLastErrorDescr)
    end

    if (self.m_sSourceText == "") then
        self.m_sResult         = AZURETRANSLATE_RETURN_FAILED
        self.m_nLastError      = AZURETRANSLATE_ERROR_PARAMETER
        self.m_sLastErrorDescr = "Invalid sSourceText parameter! Must not be empty."
        PBXScript.OutputTrace (self.m_sLastErrorDescr)
    end

    if (self.m_sTargetLanguage == "") then
        self.m_sResult         = AZURETRANSLATE_RETURN_FAILED
        self.m_nLastError      = AZURETRANSLATE_ERROR_PARAMETER
        self.m_sLastErrorDescr = "Invalid sTargetLanguage parameter! Must not be empty."
        PBXScript.OutputTrace (self.m_sLastErrorDescr)
    end

    PBXScript.OutputTrace ("<------- AzureTranslate:CheckParameters()")
end


---------------------------------------------------------------------
--
-- GetAccessToken
--
-- Gets fresh access token from azure portal.
--
-- Parameter:
--
-- Returns:
--
---------------------------------------------------------------------
function AzureTranslateClass:GetAccessToken()
    PBXScript.OutputTrace ("-------> AzureTranslate:GetAccessToken()")

    local sURL, oWebRequest, nResponseCode, sResponseBody

    sURL = AZURETRANSLATE_URL_TOKEN
    sURL = StringReplace(sURL, "#REGION#", self.m_sRegion)
    PBXScript.OutputTrace ("sURL = "  .. sURL)

    local webreq = require "PBXWebRequest"
    if (webreq ~= nil) then

        local hSession, nResponseCode, sResponseBody    

        hSession = webreq.new()

        hSession:HttpAuth  (PBXHttpAuthNone)
        hSession:HttpVerb  (PBXHttpVerbPost)
        hSession:URL       (sURL)
        hSession:AddHeader ("Ocp-Apim-Subscription-Key: " .. self.m_sSubscriptionKey)

        nResponseCode = hSession:Execute()
        sResponseBody = hSession:ResponseBody()
        PBXScript.OutputTrace ("nResponseCode = " .. nResponseCode)


        if (nResponseCode == 200) then
            self.m_sAccessToken    = sResponseBody
        else
            self.m_sResult         = AZURETRANSLATE_RETURN_FAILED
            self.m_nLastError      = AZURETRANSLATE_ERROR_WEBREQUEST
            self.m_sLastErrorDescr = "Azure web request failed! (" .. sResponseBody .. ")"
            PBXScript.OutputTrace (self.m_sLastErrorDescr)
        end

        self.m_nLatestResponseCode = nResponseCode
    end

    PBXScript.OutputTrace ("<------- AzureTranslate:GetAccessToken()")
end


---------------------------------------------------------------------
--
-- RunTranslation
--
-- Runs request against cognitive services to return generated wav audio file.
--
-- Parameter:
--
-- Returns:
--
---------------------------------------------------------------------
function AzureTranslateClass:RunTranslation()
    PBXScript.OutputTrace ("-------> AzureTranslateClass:RunTranslation()")

    local sURL, sRequestBody

    sURL = AZURETRANSLATE_URL_TRANSLATE
    if (self.m_sSourceLanguage ~= "") then
        sURL = sURL .. "&from=" .. self.m_sSourceLanguage
    end
    if (self.m_sTargetLanguage ~= "") then
        sURL = sURL .. "&to=" .. self.m_sTargetLanguage
    end
    PBXScript.OutputTrace ("sURL = " .. sURL)


    local a  = {}
    a.text = self.m_sSourceText
    local jsonHlp = NewJsonHelper()
    sRequestBody = jsonHlp:encode(a)
    sRequestBody = "[" .. sRequestBody .. "]"
    PBXScript.OutputTrace ("sRequestBody = " .. sRequestBody)


    local webreq = require "PBXWebRequest"
    if (webreq ~= nil) then

        local hSession, nResponseCode, sResponseBody

        hSession = webreq.new()

        hSession:HttpAuth   (PBXHttpAuthBearer)
        hSession:AuthPass   (self.m_sAccessToken)
        hSession:HttpVerb   (PBXHttpVerbPost)
        hSession:URL        (sURL)
        hSession:RequestBody(sRequestBody)
        hSession:AddHeader  ("Content-Type: application/json; charset=UTF-8")

        nResponseCode = hSession:Execute()
        sResponseBody = hSession:ResponseBody()

        PBXScript.OutputTrace ("nResponseCode = " .. nResponseCode)

        if (nResponseCode == 200) and (sResponseBody ~= "") then
            PBXScript.OutputTrace ("sResponseBody = " .. sResponseBody)

            local a = {}
            a = jsonHlp:decode(sResponseBody)

            self.m_sTargetText = a[1].translations[1].text
            PBXScript.OutputTrace ("m_sTargetText = " .. self.m_sTargetText)
            g_sAzureTranslate_LatestTranslation = self.m_sTargetText

            if (self.m_sSourceLanguage == "") then
                self.m_sDetectedLanguage      = a[1].detectedLanguage.language
                self.m_sDetectedLanguageScore = a[1].detectedLanguage.score

                PBXScript.OutputTrace ("m_sDetectedLanguage = "      .. self.m_sDetectedLanguage)
                PBXScript.OutputTrace ("m_sDetectedLanguageScore = " .. self.m_sDetectedLanguageScore)

                g_sAzureTranslate_LatestDetectedLanguage           = self.m_sDetectedLanguage
                g_sAzureTranslate_LatestDetectedLanguageConfidence = self.m_sDetectedLanguageScore
            end
        else
            self.m_sResult         = AZURETRANSLATE_RETURN_FAILED
            self.m_nLastError      = AZURETRANSLATE_ERROR_WEBREQUEST
            self.m_sLastErrorDescr = "Azure web request failed! (" .. sResponseBody .. ")"
            PBXScript.OutputTrace (self.m_sLastErrorDescr)
        end

        self.m_nLatestResponseCode = nResponseCode

    end

    PBXScript.OutputTrace ("<------- AzureTranslateClass:RunTranslation()")
end


---------------------------------------------------------------------
-- Public Functions
---------------------------------------------------------------------


---------------------------------------------------------------------
--
-- Run
--
-- Performs the entire TTS.
--
-- Parameter:
--
-- Returns:
--
---------------------------------------------------------------------
function AzureTranslateClass:Run()
    PBXScript.OutputTrace ("-------> AzureTranslate:Run()")

    self:Clear()
    self:CheckParameters()

    if (self.m_sResult == AZURETRANSLATE_RETURN_OK) and (self.m_nLastError == AZURETRANSLATE_ERROR_NONE) then
        self:GetAccessToken()
    end

    if (self.m_sResult == AZURETRANSLATE_RETURN_OK) and (self.m_nLastError == AZURETRANSLATE_ERROR_NONE) then
        self:RunTranslation()
    end

    PBXScript.OutputTrace ("<------- AzureTranslate:Run()")
    return self.m_sResult
end

---- SIG ---- Begin Signature
---- SIG ---- WXGNKKDSNKKTJLPMYUNMCHHUCTECMFNDDASSDWYILMXFLXILODDCJYTIMWLC
---- SIG ---- JYDLSDOOMPOQFLKPSLMLZCOQJFHYXFZKASREUCMBEGTVLJSHPLPPAGNFFHOP
---- SIG ---- NVESADLRMVNPEMMPAHIJJUPHLOHLHNHRHEOOYBYIGPJTLRUNCRVOMZOMRBPN
---- SIG ---- HQLPPNKVLVKKIFEXELRJJRDDZNEYCLLHHDWXLZDBKXJLZZXJIYSIUOPMLPHN
---- SIG ---- WULXWHROLEOVNEPIJDPWKXTMKAGRAKCONFOOUBKBVLSEIGGEBMTUWVOCMNUD
---- SIG ---- MUSEEMSEFZVPXCFUBTBTXOTHCJHIAICKKPOIWPJNOLOXSHFWGKLVOBNRRNPK
---- SIG ---- LFKRZROWOLCMGTJLIRFMARBOOGNCJGVNLQMPNKJBPQCVPPFIXIPCKFKENLBI
---- SIG ---- NRBXIEUGTBYLYFHYPFOXCKJXGVQMSVUSDQRZOMTRXRGLGKUJDWVJMKLSPMBW
---- SIG ---- ENLGFKHMNNKOUMNMLIKMDKWOURKKXOPFXEMPVBBEKAAOEKVPLUMNARINIANN
---- SIG ---- YFSLKKYSYTZMBVYKQXAYLWGIDMOSDDHBSSSZOPHLHIJCLAWORMILABNSEPFJ
---- SIG ---- JTKPDOPIUBBLVSTLQRKUTQYYRAIRRXLIKZUYDXRZOVBBKBEXLLBKTFXBKXTL
---- SIG ---- UBPNXNRIRQCILLNNLVODTMUGEFOVGIUPZPLNDUPRPLOSJDTBPMCFDMVYHLCY
---- SIG ---- HPZWLGPHZCWWCRIDOXLYJPXMIMJDHPCRKVUBWIOLLPTWONFOQPYPMQXONEOH
---- SIG ---- PROSLANVWZKHCQTXAMVXRZWPARUMBILVOILKZBBVBGMCKMOBJLKGJMACKNCF
---- SIG ---- BOKKPABLXEJMENOXXVGPOKEBPZKGUNOPKFRKYCUGIZRYOLSPKRALKXEAZVNJ
---- SIG ---- EGSVBPEPOODPENLFORQNYOUQCLAOMYKGQPPUFHSNVMPKLROGZFKMACDLRCQV
---- SIG ---- XYQCMCVSKBQISEELKFLOVXMMOXJZXIJTKQLDNOSQZRKCBRPBKJFKKBYPAKNU
---- SIG ---- PWJO
---- SIG ---- End Signature