<?php
if (!defined('FILE_CACHE_MAX_FILE_AGE')) define('FILE_CACHE_MAX_FILE_AGE', 21600);
/**
 * @property string html
 * @property string httpUserAgent
 * @property DOMDocument doc
 */
class vkParser
{
    private $httpUserAgent = 'Mozilla/5.0 Windows NT 6.1; Win64; x64 AppleWebKit/537.36 KHTML, like Gecko Chrome/61.0.3163.91 Safari/537.36';
    private $html, $doc;

    /**
     * vkParser constructor.
     * @param string $strUrl
     */
    public function __construct($strUrl)
    {
        if (is_readable(__DIR__ . '/cookie/cookieUserVk.cook') === false) {
            header('Location: /audio/auth');
            exit();
        }
        if (is_dir(__DIR__ . '/cookie') === false) {
            mkdir(__DIR__ . '/cookie');
            file_put_contents(__DIR__ . '/cookie/.htaccess', 'Deny From All');
        }
        if (is_dir(__DIR__ . '/response') === false) {
            mkdir(__DIR__ . '/response');
            file_put_contents(__DIR__ . '/response/.htaccess', 'Deny From All');
        }
        $this->doc = new DOMDocument();
        $this->cleanDirFile();
    }
    /**
     * @param string $strUrl
     * @param string $nameFile
     */
    public function fetchMulti($strUrl, $nameFile)
    {
        $Url = array(
            $strUrl . '&offset=0',
            $strUrl . '&offset=50',
            $strUrl . '&offset=100',
            $strUrl . '&offset=150',
        );
        $multi = curl_multi_init();
        $ch = array();
        foreach ($Url As $key => $item) {
            $ch[$key] = curl_init();
            $options = array(
                CURLOPT_URL => $item,
                CURLOPT_USERAGENT => $this->httpUserAgent,
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_SSL_VERIFYHOST => false,
                CURLOPT_SSL_VERIFYPEER => false,
                CURLOPT_TIMEOUT => 15,
                CURLOPT_FAILONERROR => true,
                CURLOPT_REFERER => 'https://m.vk.com/audio',
                CURLOPT_COOKIEFILE => __DIR__ . '/cookie/cookieUserVk.cook',
            );
            curl_setopt_array($ch[$key], $options);
            curl_multi_add_handle($multi, $ch[$key]);
        }
        do {
            $status = curl_multi_exec($multi, $active);
            $info = curl_multi_info_read($multi);

            if ($info === false)
                continue;

            if ($info['result'] !== CURLM_OK) {
                curl_multi_remove_handle($multi, $info['handle']);
                unset($ch[array_search($info['handle'], $ch)]);
            }
        } while ($status === CURLM_CALL_MULTI_PERFORM || $active);
        foreach($ch as $key => $chItem) {
            $this->html = curl_multi_getcontent($chItem);
            $this->_newHtmlDoc();
            $domXPath = new DOMXPath($this->doc);
            $elements = $domXPath->query("//div[@class='audios_block audios_list _si_container']");
            if($elements->length <= 0)
                continue;
            $elements = $elements->item(0)->childNodes;
            if($elements->length <= 0)
                continue;
            $this->parse($elements, $nameFile);
            curl_multi_remove_handle($multi, $chItem);
        }
        $ch = null;
        curl_multi_close($multi);
    }

    /**
     * @param DOMNodeList $elements
     * @param string $nameFile
     */
    private function parse(DOMNodeList $elements, $nameFile)
    {
        $response = array();
        foreach ($elements AS $item) {
            if ($item instanceof DOMElement) {
                $i = substr($item->getAttribute('data-id'), 1); // data-id
                $ai_info = $item->childNodes->item(1);
                $cover = '';
                if ($ai_info instanceof DOMElement) // ai_info
                {
                    $cover = $ai_info->getElementsByTagName('div')->item(0)->getAttribute('style');
                    $cover = preg_replace('/background\-image\:url\((.*)\)/i', '$1', $cover);
                    $ai_info = $ai_info->childNodes->item(5);
                    if ($ai_info instanceof DOMElement) // ai_body
                    {
                        $ai_body = $ai_info->childNodes;
                        $ai_file = $ai_body->item(5)->getAttribute('value');
                        if (empty($ai_file))
                            continue;
                        $response[$i] = array();
                        $response[$i]['dur'] = $ai_body->item(1)->getAttribute('data-dur'); // ai_dur
                        $response[$i]['cDur'] = $ai_body->item(1)->textContent; // convert_dur

                        // ai_label
                        $response[$i]['title'] = $ai_body->item(3)->childNodes->item(1)->textContent; // ai_title
                        $response[$i]['artist'] = $ai_body->item(3)->childNodes->item(5)->textContent; // ai_artist
                        //file
                        $response[$i]['url'] = $ai_file; // ai_file
                        $response[$i]['file'] = mt_rand() . '-' . $i . '.mp3'; // ai_file

                        $response[$i]['cover'] =  parse_url($cover, PHP_URL_PATH); // cover
                        if(empty($response[$i]['cover']) === false) {
                            $response[$i]['cover'] = "background-image: url('/audio/get/" . $this->genNameFile($nameFile) . '/' . $i . '/cover' . $response[$i]['cover'] . "')";
                            $response[$i]['coverServer'] = $cover;
                        }
                    }
                }
            }
        }
        $this->writeDataResponse($response, $nameFile);
        unset($response);
    }

    /**
     * @return bool
     */
    private function isAjax()
    {
        $server = getenv('HTTP_X_REQUESTED_WITH');
        $server = ($server !== false ? strtolower($server) : null);
        return $server === 'xmlhttprequest';
    }

    /**
     * Clear dir
     */
    private function cleanDirFile()
    {
        $fileDIR = $_SERVER['DOCUMENT_ROOT'] . '/audio/file/';
        if ($this->isAjax() === false) {
            foreach (glob($fileDIR . $_SESSION['audioSession'] . '*.mp3') AS $item)
                unlink($item);
        }
        if (is_readable($fileDIR . 'lastCleanTime.touch') === false)
            touch($fileDIR . 'lastCleanTime.touch');
        $timeAgo = time() - 4000;
        if (filemtime($fileDIR . 'lastCleanTime.touch') < $timeAgo) {
            foreach (glob($fileDIR . '*.mp3') AS $item)
                if (filemtime($item) < $timeAgo)
                    unlink($item);
            touch($fileDIR . 'lastCleanTime.touch');
        }
    }

    /**
     * @param string $file
     * @return bool
     */
    public function isDataResponse($file)
    {
        $file = $this->genNameFile($file);
        $cacheFile = __DIR__ . '/response/' . $file . '.data.r';
        $data = array();
        if (is_readable($cacheFile))
            $data = include $cacheFile;
        if (empty($data) === true || (is_readable($cacheFile) === true && (filemtime($cacheFile) + FILE_CACHE_MAX_FILE_AGE) < time()))
            return true;
        return false;
    }

    /**
     * @param string $file
     * @return array
     */
    public function getDataResponse($file)
    {
        $file = $this->genNameFile($file);
        $cacheFile = __DIR__ . '/response/' . $file . '.data.r';
        $r = array();
        $r['nameFile'] = $file;
        $r['response'] = array();
        if (is_readable($cacheFile) === true)
            $r['response'] = include $cacheFile;
        return $r;
    }

    private function genNameFile($name) {
        return md5(strtolower($name));
    }

    /**
     * @param array $response
     * @param string $file
     * @return bool
     */
    private function writeDataResponse(array $response, $file)
    {
        $file = $this->genNameFile($file);
        $cacheFile = __DIR__ . '/response/' . $file . '.data.r';
        if ($this->isDataResponse($file))
            file_put_contents($cacheFile, '<?php return ' . var_export($response, true) . ';', LOCK_EX);
        return true;
    }

    /**
     * Generate html document
     */
    private function _newHtmlDoc()
    {
        if (!empty($this->html)) {
            $old_libxml_error = libxml_use_internal_errors(true);
            $this->doc->loadHTML($this->html);
            libxml_use_internal_errors($old_libxml_error);
        }
    }

    public function __destruct()
    {
        $this->doc = null;
    }
}