<?php

class Chunk {

    public $UPLOAD_PATH;

    private $_lastChunk = null;
    private $_chunkFolder = '';
    private $_lockFile = '';
    private $_isLast = false;

    public function __construct() {
        $this->UPLOAD_PATH = dirname(__FILE__) . '/chunks/';
        $this->_chunkFolder = $this->UPLOAD_PATH . 'default';
    }

    public function save($info) {
        $this->validate($info);

        $this->_lastChunk = $info;
        $this->_chunkFolder = $this->UPLOAD_PATH . $info['id'];
        $this->_lockFile = $this->_chunkFolder . '/lock';

        if (!is_dir($this->_chunkFolder)) {
            @@mkdir($this->_chunkFolder, 0777, true);
        }

        $f = fopen($this->_lockFile, 'c');

        if (flock($f, LOCK_EX)) {
            $chunks = array_diff(scandir($this->_chunkFolder), array('.', '..', 'lock'));

            if ((int) $this->_lastChunk['total'] === count($chunks) + 1) {
                $this->_isLast = true;
            } else {
                file_put_contents($this->_chunkFolder . '/' . (int) $info['current'], $info['content']);
            }
            flock($f, LOCK_UN);
        } else {
            exit("Couldn't lock the file");
        }
    }

    public function last() {
        return $this->_isLast;
    }

    public function complete() {
        $content = '';
        for ($i = 1, $count = (int) $this->_lastChunk['total']; $i <= $count; $i++) {
            if ((int) $this->_lastChunk['current'] === $i) {
                $content .=  $this->_lastChunk['content'];
            } else {
                if (!file_exists($this->_chunkFolder . "/$i")) {
                    $this->clear_chunk_directory();
                    exit('Missing chunk #' . $i . ' : ' . implode(' / ', scandir($this->_chunkFolder)));
                }
                $content .= file_get_contents($this->_chunkFolder . "/$i");
            }
        }
        $this->clear_chunk_directory();
        return $content;
    }

    private function validate($info) {
        if (!isset($info['id']) || !$info['id'])
            throw new Exception('Invalid id');
        if (!isset($info['total']) || (int) $info['total'] < 1)
            throw new Exception('Invalid chunks total');
        if (!isset($info['current']) || (int) $info['current'] < 1)
            throw new Exception('Invalid current chunk number');
        if (!isset($info['content']) || !strlen($info['content']))
            throw new Exception('Invalid chunk content');
    }

    public function clear_chunk_directory() {
        $dir = $this->UPLOAD_PATH;
        if (is_dir($dir)) {
            $it = new RecursiveDirectoryIterator($dir);
            $files = new RecursiveIteratorIterator($it,
                RecursiveIteratorIterator::CHILD_FIRST);
            foreach($files as $file) {
                if ($file->getFilename() === '.' || $file->getFilename() === '..') {
                    continue;
                }
                if ($file->isDir()){
                    rmdir($file->getRealPath());
                } else {
                    unlink($file->getRealPath());
                }
            }
            rmdir($dir);
        }
    }
}