<?php

namespace Velis\Mutex;

/**
 * Shared memory handling object
 * @author Olek Procki <olo@velis.pl>
 */
class Semaphore
{
    const MAX_LIFETIME = '900'; // number of seconds for mutex lifetime

    private $_id;
    private $_semId;

    private $_isAcquired          = false;
    private $_isWindows           = false;
    private $_sharedMemoryEnabled = false;

    private $_filename = '';
    private $_filepointer;


    /**
     * Constructor
     */
    public function __construct()
    {
        if (substr(PHP_OS, 0, 3) == 'WIN') {
            $this->_isWindows = true;
        }

        $this->_sharedMemoryEnabled = false;
    }


    /**
     * Initialize semaphore
     *
     * @param int $id
     * @param string $filename
     *
     * @return bool
     */
    public function init($id)
    {
        $this->_id = $id;
        $this->_filename = DATA_PATH . 'semaphore' . DIRECTORY_SEPARATOR . str_replace('/', '-', $id) . '.sem';

        if ($this->_sharedMemoryEnabled) {
            if (!($this->_semId = sem_get($this->_id, 1, null, true))) {
                throw new Exception("Error getting semaphore " . $this->_id);
            }
        }

        return true;
    }



    /**
     * Try to acquire semaphore
     *
     * @return bool
     * @throws Exception
     */
    public function acquire()
    {
        if ($this->_isWindows || !$this->_sharedMemoryEnabled) {
            if (file_exists($this->_filename)) {
                if (time() - filemtime($this->_filename) > self::MAX_LIFETIME) {
                    unlink($this->_filename);
                } else {
                    throw new Exception("Error creating mutex file (" . $this->_filename . ") - FILE ALREADY EXISTS!");
                }
            }

            if (false == ($this->_filepointer = fopen($this->_filename, "w+"))) {
                throw new Exception("Error opening mutex file " . $this->_filename);
            }

            if (false == flock($this->_filepointer, LOCK_EX)) {
                throw new Exception("Error locking mutex file" . $this->_filename);
            }
        } else {
            if (!sem_acquire($this->_semId)) {
                throw new Exception("Error acquiring semaphore " . $this->_filename);
            }
        }

        $this->_isAcquired = true;
        return true;
    }


    /**
     * Releases semaphore
     * @return bool
     *
     * @throws Exception
     */
    public function release()
    {
        if (!$this->_isAcquired) {
            return true;
        }

        if ($this->_isWindows || !$this->_sharedMemoryEnabled) {
            if (false == flock($this->_filepointer, LOCK_UN)) {
                throw new Exception("Error unlocking mutex file " . $this->_filename);
            }
            fclose($this->_filepointer);

            if (!unlink($this->_filename)) {
                throw new Exception("Error unlinking mutex file " . $this->_filename);
            }
        } else {
            if (!sem_release($this->_semId)) {
                throw new Exception("Error releasing semaphore " . $this->_id);
                return false;
            }
        }

        $this->_isAcquired = false;
        return true;
    }



    /**
     * Returns semaphore id
     * @return int
     */
    public function id()
    {
        return $this->_semId;
    }
}
