if getOperatingSystem()~=0 then return end

require("lfs")
andtools={}

andtools.path=getAutorunPath()..[[andtools\]]  --todo: if nox is running, use the adb tool from nox instead
andtools.workpath=[[temp\]] --relative to andtools
lfs.mkdir(andtools.workpath)
andtools.deviceidselectioncommand=''
--apktool d -s apk.apk -o extracted
--apktool b extracted -o apk.apk


--keytool -genkey -noprompt -v -alias alias_name -dname "L=Goobelywho" -keyalg RSA -keysize 2048 -validity 10000 -storepass password123 -keystore cekeystore -keypass password123

--jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore cekeystore my_application.apk alias_name
--zipalign -p -f -v 4 package.apk package-za.apk

function andtools.getDeviceList()
  local list={}
  s=createStringList()
  s.text=runCommand(andtools.path..'adb.exe', {'devices','-l'})
  if s.Count>0 then
    s.delete(0)
  end

  for i=0,s.count-1 do
    if string.trim(s[i])~='' then
      local e={}
      e.id=s[i]:split(' ')

      e.desc=s[i]:sub(#e.id+1,#s[i]):trim()
      list[#list+1]=e
    end
  end

  s.destroy()
  return list
end

function andtools.getAndroidPackages()
  local output = runCommand(andtools.path..'adb.exe', andtools.deviceidselectioncommand..'shell "pm list packages"')
  
  local s = createStringList()
  s.text = output
  
  local packages = {}
  for i=0, s.Count-1 do
    local line = s[i]:trim()
    -- Filter for valid package lines and strip "package:" prefix
    if line:sub(1,8) == 'package:' then
      table.insert(packages, line:sub(9))
    end
  end
  
  s.destroy()
  return packages
end

function andtools.getPackagePath(packagename)
  -- Returns a table of paths (base.apk, split_config.xx.apk, etc.)
  local r = runCommand(andtools.path..'adb.exe', andtools.deviceidselectioncommand..'shell "pm path "'..packagename)
  local paths = {}
  local s = createStringList()
  s.text = r
  
  for i=0, s.Count-1 do
    local line = s[i]:trim()
    if line:sub(1,8) == 'package:' then
      table.insert(paths, line:sub(9))
    end
  end
  
  s.destroy()
  return paths
end

function andtools.patchAPK(packagename)
  -- 1. Get all split paths
  local packagePaths = andtools.getPackagePath(packagename)

  if #packagePaths == 0 then
    synchronize(function()
      messageDialog("Could not find package path for "..packagename, mtError)
    end)
    return
  end

  -- 2. Prepare directories
  -- We create a specific folder to hold the splits before merging
  local splitsDir = andtools.workpath .. [[splits\]]
  runCommand('cmd', {'/c', 'mkdir', splitsDir})
  -- Clear previous splits to avoid merging wrong files
  runCommand('cmd', {'/c', 'del', '/q', splitsDir..[[*.*]]})

  local mergedFilename = packagename .. '.apk'
  local localpath = andtools.workpath .. mergedFilename

  -- 3. Pull all APK parts
  synchronize(function()
    miAndroid.Caption='Android (Pulling APKs)'
  end)

  local pullSuccess = true
  for i, path in ipairs(packagePaths) do
    local partName = extractFileName(path)
    local dest = splitsDir .. partName
    local r, c = runCommand(andtools.path..'adb.exe', {andtools.deviceidselectioncommand, 'pull', path, dest})

    if not fileExists(dest) then
      pullSuccess = false
      print("Failed to pull: " .. path)
    end
  end

  if pullSuccess then
    lfs.chdir(andtools.path)
  
    -- 4. Merge (if splits exist) or Copy (if single file)
    if #packagePaths > 1 then
      synchronize(function()
        miAndroid.Caption='Android (Merging Split APKs)'
      end)

      

      -- Command: java -jar APKEditor.jar m -i <input_dir> -o <output_file>
      local r, c,err = runCommand(andtools.path..[[jre\bin\java.exe]], {
        '-jar', andtools.path..'APKEditor-1.4.6.jar',
        'm',
        '-f',
        '-i', splitsDir,
        '-o', localpath
      })

      if c ~= 0 or not fileExists(localpath) then
        synchronize(function()
          messageDialog("Failed to merge split APKs. Code: "..c.."\nOutput: "..r..err, mtError)
        end)
        return
      end

    else
      -- Single APK scenario: just move/copy the one file from splits folder to workpath
      local singleFile = splitsDir .. extractFileName(packagePaths[1])
      runCommand('cmd', {'/c', 'copy', '/y', singleFile, localpath})
    end

    -- =========================================================
    -- ORIGINAL PATCHING LOGIC RESUMES HERE
    -- =========================================================

    if fileExists(localpath) then
      synchronize(function()
        miAndroid.Caption='Android (Decoding APK)'
      end)

      local extractedPath = extractFileNameWithoutExt(localpath)

      -- Clean up previous extraction if it exists
      runCommand('cmd', {'/c', 'rmdir', '/s', '/q', andtools.workpath..extractedPath})

      -- Decode
      local r, c = runCommand(andtools.path..[[jre\bin\java.exe]],{'-jar', andtools.path..'apktool.jar','d','-s','-f', localpath, '-o', andtools.workpath..extractedPath})

      if (c==0) and fileExists(andtools.workpath..extractedPath..[[\AndroidManifest.xml]]) then
        synchronize(function()
          miAndroid.Caption='Android (Rebuilding APK)'
        end)

        -- Rebuild with debug flag (-d)
        -- Note: using extractedPath directly requires correct relative pathing,
        local buildDir = andtools.workpath..extractedPath
        local patchedApk = andtools.workpath..extractedPath..'_patched.apk'

        r,c,err=runCommand(andtools.path..[[jre\bin\java.exe]],{'-jar', andtools.path..'apktool.jar', 'b', buildDir, '-d', '-o', patchedApk})

        if fileExists(patchedApk) then
          if not fileExists(andtools.workpath..'ce.keystore') then
            synchronize(function()
              miAndroid.Caption='Android (Creating signature)'
            end)

            r,c=runCommand(andtools.path..[[jre\bin\keytool.exe]], {'-genkey', '-noprompt', '-v', '-alias', 'cheater', '-dname', 'L=MotherFuckingCheater','-keyalg', 'RSA','-keysize','2048','-validity','10000','-storepass','password123', '-keystore', andtools.workpath..[[ce.keystore]],'-keypass', 'password123'})

            if not fileExists(andtools.workpath..'ce.keystore') then
              synchronize(function()
                messageDialog("Failure creating signature: "..c..'('..r..')', mtError)
              end)
              return
            end
          end

          synchronize(function()
            miAndroid.Caption='Android (ZipAlign new APK)'
          end)

          local finalApk = andtools.workpath..extractedPath..'_patched_za.apk'
          local zaparam=[[-p -f -v 4 "%s" "%s"]]
          r,c=runCommand(andtools.path.."zipalign.exe", zaparam:format(patchedApk, finalApk))

          if c~=0 then
             -- Zipalign warning
             local userResp
             synchronize(function()
                userResp = messageDialog("Zipalign failed ("..r.."). Continue?", mtWarning, mbYes, mbNo)
             end)
             if userResp ~= mrYes then return end
          end

          synchronize(function()
            miAndroid.Caption='Android (Signing new APK)'
          end)

          local signedApk=andtools.workpath..extractedPath..'_patched_za_signed.apk'

          local r, c,err = runCommand([[jre\bin\java.exe]], {
  '-jar', 'apksigner.jar', 'sign',
  '--ks', keystore,
  '--ks-pass', 'pass:password123',
  '--out', signedApk,
  finalApk
})


          if c~=0 then
            synchronize(function()
              messageDialog("failure signing apk: "..r,mtError)
            end)
            return
          end


          synchronize(function()
            miAndroid.Caption='Android (Replacing APK)'
          end)

          -- Uninstall Original
          r,c=runCommand(andtools.path..'adb.exe',{andtools.deviceidselectioncommand, 'uninstall', packagename})

          if c~=0 then
             local userResp
             synchronize(function()
                userResp = messageDialog("Failed uninstalling "..packagename..': '..r.."\n\ncontinue installing the new package?", mtWarning, mbYes, mbNo)
             end)
             if userResp ~= mrYes then return end
          end

           -- Install New
          r,c,err=runCommand(andtools.path..'adb.exe',{andtools.deviceidselectioncommand, 'install', signedApk})
          if c~=0 then
            synchronize(function()
              messageDialog("APK install failed: "..r..err, mtError)
            end)
          else
            synchronize(function()
               messageDialog("APK patch successful", mtInformation)
            end)
          end

        else
          synchronize(function()
            messageDialog("Failed finding "..patchedApk.." after rebuilding. Rebuild failure (code "..c..")", mtError)
          end)
        end

      else
        synchronize(function()
          messageDialog("Failed finding AndroidManifest.xml after extract. Extraction failed.", mtError)
        end)
      end

    else
      synchronize(function()
        messageDialog("Failed to locate local merged APK file.", mtError)
      end)
    end

  else
    synchronize(function()
      messageDialog('Failure pulling the apk segments from the device.', mtError)
    end)
  end
end


if miAndroid then
  miAndroid.destroy()
  miAndroid=nil

  miPatchAPK=nil
end

miAndroid=createMenuItem(MainForm.Menu)
miAndroid.Caption='Android'
MainForm.Menu.Items.insert(MainForm.miHelp.MenuIndex, miAndroid)

miPatchAPK=createMenuItem(miAndroid)
miPatchAPK.Caption='Patch APK to debuggable'
miAndroid.add(miPatchAPK)

--miLaunchCEServer=createMenuItem(miAndroid)
--miLaunchCEServer.Caption='Launch ceserver'
--miAndroid.add(miLaunchCEServer)
--run-as gamepackage
--cp /sdcard/ceserver .
--chmod a+x ceserver
--./ceserver



miPatchAPK.OnClick=function(s)
  local i,r

  local devicelist=andtools.getDeviceList()
  if #devicelist==0 then
    messageDialog('There is no android device available', mtError, mbOK)
    return
  end

  if #devicelist>1 then
    s=createStringlist()
    for i=1,#devicelist do
      s.add(devicelist[i].id..'   -   '..devicelist[i].desc)
    end
    i,r=showSelectionList('APK Unlock', 'Select the android device that contains the APK you wish to unlock', s, false, 'AndroidDeviceSelectionList')

    s.destroy()

    if i==-1 then return end

    andtools.deviceidselectioncommand='-s '..devicelist[i+1].id..' '
  end

  s=createStringlist()
  for _, pkgName in pairs(andtools.getAndroidPackages()) do
    s.add(pkgName)
  end

  i,r=showSelectionList('APK Unlock', 'Select the APK to unlock', s, false, 'AndroidPackageSelectionList')
  if i~=-1 then
    
    if messageDialog('Are you sure you wish to unlock '..r..'?  All data regarding this apk will be lost', mtWarning, mbYes,mbNo)==mrYes then
      miAndroid.Caption='Android (Working)'
      miAndroid.Enabled=false
      miPatchAPK.Enabled=false
      --miLaunchCEServer.Enabled=false
      createThread(function(t)
        t.Name='Android APK Patcher Thread'
        andtools.patchAPK(r)

        synchronize(function()
          miAndroid.Caption='Android'
          miAndroid.Enabled=true
          miPatchAPK.Enabled=true
          --miLaunchCEServer.Enabled=true
        end)
      end)
    end
  end

end


--miLaunchCEServer.OnClick=function()
  --
--end


--return andtools.getDeviceList()
