<?php


namespace CubiStore\Web\Utils;

use SimpleXMLElement;

class Apk
{
    /**
     * @var string
     */
    private $file;
    /**
     * @var SimpleXMLElement|null
     */
    private $manifest;

    /**
     * Apk constructor.
     * @param string $file
     */
    public function __construct(string $file)
    {
        $this->file = $file;
    }

    public function isValid(): bool
    {
        // Get first 4 bytes
        $fh = fopen($this->file, 'r');

        if ($fh === false) {
            throw new \RuntimeException("Failed to open file '{$this->file}'");
        }

        $header = fread($fh, 4);

        if ($header === false) {
            throw new \RuntimeException("Failed to read file '{$this->file}'");
        }

        fclose($fh);

        // Check if uploaded file is a valid ZIP file
        if ($header !== "PK\x03\x04") {
            return false;
        }

        $zip = new \ZipArchive();
        $zip->open($this->file);
        $valid = $zip->statName("AndroidManifest.xml") !== false && $zip->statName("resources.arsc");
        $zip->close();

        return $valid;
    }

    public function getManifest(): SimpleXMLElement
    {
        if ($this->manifest === null) {
            $xml = $this->cmd('androguard', 'axml', '-o', '/dev/stdout', $this->file);
            $simpleXml = simplexml_load_string($xml);

            if ($simpleXml === false) {
                throw new \RuntimeException("Failed to parse AndroidManifest.xml from '{$this->file}'");
            }

            $this->manifest = $simpleXml;
        }

        return $this->manifest;
    }

    private function getAttribute(string $name, ?string $namespace = null): string
    {
        if ($namespace !== null) {
            return (string)$this->getManifest()->attributes($namespace, true)[$name];
        }

        return (string)$this->getManifest()[$name];
    }

    public function getPackageName(): string
    {
        return $this->getAttribute('package');
    }

    public function getVersionCode(): int
    {
        return intval($this->getAttribute('versionCode', 'android'), 10);
    }

    public function getVersionName(): string
    {
        return $this->getAttribute('versionName', 'android');
    }

    public function getLabel(): string
    {
        $code = (string)$this->getManifest()->application->attributes('android', true)['label'];
        $output = $this->cmd('androguard', 'arsc', '--id', $code, $this->file);
        preg_match(':\<default\> = \'(.*)\'$:m', $output, $matches);
        return stripslashes($matches[1]);
    }

    private function cmd(string $cmd, string... $args): string
    {
        $fullCommand = $cmd . ' ' . implode(' ', array_map('escapeshellarg', $args));
        $proc = popen($fullCommand, 'r');
        if ($proc === false) {
            throw new \RuntimeException("Failed running: $fullCommand");
        }

        $data = '';
        while ($text = fread($proc, 1024)) {
            $data .= $text;
        }
        pclose($proc);
        return $data;
    }
}
