<?php

/**
 * PhotoStorage allows to attach images for any kind of items on some scope
 */
class PhotoStorage {

    /**
     * Contains system photostorage.ini config as key=>value
     *
     * @var array
     */
    protected $photoCfg = array();

    /**
     * Contains system alter config as key=>value
     *
     * @var array
     */
    protected $altCfg = array();

    /**
     * Contains array of available images in database as id=>imagedata
     *
     * @var array
     */
    protected $allimages = array();

    /**
     * Contains loaded images count for each item in some scope as scope=>itemid=>count
     *
     * @var array
     */
    protected $imagesCount = array();

    /**
     * Contains loaded images paths for each item in some scope as scope=>itemid=>imagesList
     * 
     * @var array
     */
    protected $imagesList = array();

    /**
     * Contains current photostorage items scope
     *
     * @var string
     */
    protected $scope = '';

    /**
     * Contains current instance item ID in the current scope
     *
     * @var string
     */
    protected $itemId = '';

    /**
     * Contains current administrator login
     *
     * @var string
     */
    protected $myLogin = '';

    /**
     * Flag for preventing multiple database requests
     *
     * @var bool
     */
    protected $imagesLoadedFlag = false;

    /**
     * Use photostorage as image proxy flag
     *
     * @var bool
     */
    protected $proxyMode = false;

    /**
     * Contains images storage path. May be specified in PHOTOSTORAGE_DIRECTORY option.
     *
     * @var string
     */
    protected $storagePath = 'content/documents/photostorage/';

    /**
     * Custom, optional images display prefix URL. Configurable via PHOTOSTORAGE_URL_PREFIX option.
     *
     * @var string
     */
    protected $storageUrlPrefix = '';

    /**
     * Allowed image file extensions for uploads (lowercase, without dot)
     *
     * @var array
     */
    protected $allowedExtensions = array("jpg", "gif", "png", "jpeg");

    /**
     * Some predefined paths and URLs
     */
    const UPLOAD_URL_WEBC = '?module=photostorage&uploadcamphoto=true';
    const UPLOAD_URL_FILE = '?module=photostorage&uploadfilephoto=true';
    const MODULE_URL = '?module=photostorage';
    const ROUTE_PROXY = 'getimg';
    const EX_NOSCOPE = 'NO_OBJECT_SCOPE_SET';
    const EX_WRONG_EXT = 'WRONG_FILE_EXTENSION';
    const WATRERMARK_PATH = 'content/documents/watermark.png';

    /**
     * Initializes photostorage engine for some scope/item id
     * 
     * @param string $scope
     * @param string $itemid
     * 
     * @return void
     */
    public function __construct($scope = '', $itemid = '') {
        $this->loadConfig();
        $this->loadAlter();
        $this->setOptions();
        $this->setScope($scope);
        $this->setItemid($itemid);
        $this->setLogin();
    }

    /**
     * Object scope setter
     * 
     * @param string $scope Object actual scope
     * 
     * @return void
     */
    protected function setScope($scope) {
        $this->scope = ubRouting::filters($scope, 'mres');
    }

    /**
     * Object scope item Id setter
     * 
     * @param string $itemid Object actual id in current scope
     * 
     * @return void
     */
    protected function setItemid($itemid) {
        $this->itemId = ubRouting::filters($itemid, 'mres');
    }

    /**
     * Loads system photostorage config into private prop
     * 
     * @return void
     */
    protected function loadConfig() {
        global $ubillingConfig;
        $this->photoCfg = $ubillingConfig->getPhoto();
    }

    /**
     * Sets some current instance specific options.
     * 
     * @return void
     */
    protected function setOptions() {
        if (@$this->altCfg['PHOTOSTORAGE_DIRECTORY']) {
            $this->storagePath = $this->altCfg['PHOTOSTORAGE_DIRECTORY'];
        }

        if (@$this->altCfg['PHOTOSTORAGE_URL_PREFIX']) {
            $this->storageUrlPrefix = $this->altCfg['PHOTOSTORAGE_URL_PREFIX'];
        }

        if (@$this->altCfg['PHOTOSTORAGE_PROXY_MODE']) {
            $this->proxyMode = true;
        }
    }

    /**
     * Loads system alter config into private prop
     * 
     * @return void
     */
    protected function loadAlter() {
        global $ubillingConfig;
        $this->altCfg = $ubillingConfig->getAlter();
    }

    /**
     * Administrator login setter
     * 
     * @return void
     */
    protected function setLogin() {
        $this->myLogin = whoami();
    }

    /**
     * Loads images list from database into private prop
     * 
     * @return void
     */
    protected function loadAllImages() {
        if ((!empty($this->scope)) and (!empty($this->itemId))) {
            $query = "SELECT * from `photostorage` ORDER by `id` ASC;";
            $all = simple_queryall($query);
            $this->imagesLoadedFlag = true;
            if (!empty($all)) {
                foreach ($all as $io => $each) {
                    $this->allimages[$each['id']] = $each;
                    $this->imagesList[$each['scope']][$each['item']][] = $each['filename'];
                    if (isset($this->imagesCount[$each['scope']][$each['item']])) {
                        $this->imagesCount[$each['scope']][$each['item']]++;
                    } else {
                        $this->imagesCount[$each['scope']][$each['item']] = 1;
                    }
                }
            }
        }
    }

    /**
     * Registers uploaded image in database
     * 
     * @param string $filename
     */
    protected function registerImage($filename) {
        if ((!empty($this->scope)) and (!empty($this->itemId))) {
            $filename = mysql_real_escape_string($filename);
            $date = curdatetime();
            $query = "INSERT INTO `photostorage` (`id`, `scope`, `item`, `date`, `admin`, `filename`) "
                . "VALUES (NULL, '" . $this->scope . "', '" . $this->itemId . "', '" . $date . "', '" . $this->myLogin . "', '" . $filename . "'); ";
            nr_query($query);
            log_register('PHOTOSTORAGE CREATE SCOPE `' . $this->scope . '` ITEM [' . $this->itemId . ']');
        }
    }

    /**
     * Deletes uploaded image from database
     * 
     * @param int $imageid
     */
    protected function unregisterImage($imageid) {
        if ((!empty($this->scope)) and (!empty($this->itemId))) {
            $imageid = vf($imageid, 3);
            $date = curdatetime();
            $query = "DELETE from `photostorage` WHERE `id`='" . $imageid . "';";
            nr_query($query);
            log_register('PHOTOSTORAGE DELETE SCOPE `' . $this->scope . '` ITEM [' . $this->itemId . ']');
        }
    }

    /**
     * Returns basic image controls
     * 
     * @param int $imageId existing image ID
     * @return string
     */
    protected function imageControls($imageId) {
        $result = wf_tag('br');
        $downloadUrl = self::MODULE_URL . '&scope=' . $this->scope . '&itemid=' . $this->itemId . '&download=' . $imageId;
        $result .= wf_Link($downloadUrl, wf_img('skins/icon_download.png') . ' ' . __('Download'), false, 'ubButton') . ' ';
        if (cfr('PHOTOSTORAGEDELETE')) {
            $deleteUrl = self::MODULE_URL . '&scope=' . $this->scope . '&itemid=' . $this->itemId . '&delete=' . $imageId;
            $result .= wf_AjaxLink($deleteUrl, web_delete_icon() . ' ' . __('Delete'), 'ajRefCont_' . $imageId, false, 'ubButton') . ' ';
        }
        return ($result);
    }

    /**
     * Returns image upload controls
     * 
     * @return string
     */
    public function uploadControlsPanel() {
        $result = '';
        if ((!empty($this->scope)) and (!empty($this->itemId))) {
            $result .= wf_Link(self::MODULE_URL . '&scope=' . $this->scope . '&itemid=' . $this->itemId . '&mode=cam', wf_img('skins/photostorage.png') . ' ' . __('Webcamera snapshot'), false, 'ubButton');
            $result .= wf_Link(self::MODULE_URL . '&scope=' . $this->scope . '&itemid=' . $this->itemId . '&mode=loader', wf_img('skins/photostorage_upload.png') . ' ' . __('Upload file from HDD'), false, 'ubButton');
        }

        return ($result);
    }

    /**
     * Returns custom module backlinks for some scopes
     * 
     * @return string
     */
    protected function backUrlHelper() {
        $result = '';
        if ($this->scope == 'USERPROFILE') {
            $result = web_UserControls($this->itemId);
        }
        if ($this->scope == 'CUSTMAPMARKERS') {
            $result = wf_BackLink('?module=custmaps&edititem=' . $this->itemId);
        }
        if ($this->scope == 'CUSTMAPLINES') {
            $result = wf_BackLink('?module=custmaps&editline=' . $this->itemId);
        }
        if ($this->scope == 'WAREHOUSEITEMTYPE') {
            $result = wf_BackLink('?module=warehouse&itemtypes=true');
        }
        if ($this->scope == 'TASKMAN') {
            $result = wf_BackLink('?module=taskman&edittask=' . $this->itemId);
        }

        if ($this->scope == 'UKVUSERPROFILE') {
            $result = wf_BackLink('?module=ukv&users=true&showuser=' . $this->itemId);
        }
        if ($this->scope == 'CFITEMS') {
            $cleanUserLogin = explode(CustomFields::PHOTOSTORAGE_ITEMID_DELIMITER, $this->itemId);
            $cleanUserLogin = $cleanUserLogin[0];
            $result = wf_BackLink(CustomFields::URL_EDIT_BACK . $cleanUserLogin);
        }
        if ($this->scope == 'CRMACTIVITY') {
            $result = wf_BackLink(PseudoCRM::URL_ME . '&' . PseudoCRM::ROUTE_ACTIVITY_PROFILE . '=' . $this->itemId);
        }

        return ($result);
    }

    /**
     * Returns count of loaded images for some itemid in some scope
     * 
     * @param string $itemId
     * 
     * @return int
     */
    public function getImagesCount($itemId) {
        $result = 0;
        $this->itemId = $itemId;
        if (!$this->imagesLoadedFlag) {
            $this->loadAllImages();
        }

        if (isset($this->imagesCount[$this->scope][$itemId])) {
            $result = $this->imagesCount[$this->scope][$itemId];
        }
        return ($result);
    }

    /**
     * Returns indicator of images count for some itemId in current scope
     * 
     * @param string $itemId
     * 
     * @return string
     */
    public function getImagesIndicator($itemId, $size = '') {
        $result = '';
        $imagesCount = $this->getImagesCount($itemId);
        if ($imagesCount > 0) {
            $size = (!$size) ? 16 : $size;
            $result = wf_img_sized('skins/photostorage_green.png', __('Photostorage') . ' (' . $imagesCount . ')', $size, $size);
        } 
        return ($result);
    }

    /**
     * Returns array of links of images for some itemId in current scope if it exists
     * 
     * @param string $itemId
     * 
     * @return array
     */
    public function getImagesList($itemId) {
        $result = array();
        $this->itemId = $itemId;
        if (!$this->imagesLoadedFlag) {
            $this->loadAllImages();
        }

        if (isset($this->imagesList[$this->scope])) {
            if (isset($this->imagesList[$this->scope][$itemId])) {
                $imageFiles = $this->imagesList[$this->scope][$itemId];
                if (!empty($imageFiles)) {
                    foreach ($imageFiles as $io => $eachFilename) {
                        $result[] = $this->getImageUrl($eachFilename);
                    }
                }
            }
        }

        return ($result);
    }

    /**
     * Retuns all available scopes and images count in it as scope=>count
     * 
     * @return array
     */
    public function getAvailScopes() {
        $result = array();
        if (!$this->imagesLoadedFlag) {
            $this->loadAllImages();
        }

        if (!empty($this->allimages)) {
            foreach ($this->allimages as $io => $each) {
                if (isset($result[$each['scope']])) {
                    $result[$each['scope']]++;
                } else {
                    $result[$each['scope']] = 1;
                }
            }
        }
        return ($result);
    }

    /**
     * Returns image HTTP accessable URL
     * 
     * @param string $filename
     * 
     * @return string
     */
    protected function getImageUrl($filename) {
        $result = '';
        //Raw HTTP images access
        if (!$this->proxyMode) {
            if (empty($this->storageUrlPrefix)) {
                //seems its local storage
                $result = $this->storagePath . $filename;
            } else {
                //separate images CDN
                $result = $this->storageUrlPrefix . $filename;
            }
        } else {
            //Access to images in storage via proxy-engine
            $result = self::MODULE_URL . '&' . self::ROUTE_PROXY . '=' . $filename;
        }
        return ($result);
    }

    /**
     * Returns list of all available images for all scopes
     * 
     * @param int $perPage
     * @param bool $checkRights
     * 
     * @return string
     */
    public function renderScopesGallery($perPage = 12, $checkRights = true) {
        $result = '';
        $paginator = '';
        $scopeImages = array();
        $messages = new UbillingMessageHelper();
        if (!$this->imagesLoadedFlag) {
            $this->loadAllImages();
        }

        if (!empty($this->allimages)) {
            $imgTmp = array_reverse($this->allimages);
            if ($checkRights) {
                $myImages = array();
                if (!cfr('ROOT')) {
                    if (!empty($imgTmp)) {
                        foreach ($imgTmp as $io => $each) {
                            if ($each['admin'] == $this->myLogin) {
                                $myImages[$io] = $each;
                            }
                        }
                        $imgTmp = $myImages;
                    }
                }
            }

            $renderImages = array();
            $totalCount = sizeof($imgTmp);
            $currentPage = (ubRouting::get('page')) ? ubRouting::get('page') : 1;

            //pagination
            if ($totalCount > $perPage) {
                $paginator = wf_pagination($totalCount, $perPage, $currentPage, self::MODULE_URL, 'ubButton', 14);
                $lowLimit = ($perPage * ($currentPage - 1));

                $upperLimit = $lowLimit + $perPage;
                $i = 0;
                foreach ($imgTmp as $io => $each) {
                    if ($i >= $lowLimit and $i < $upperLimit) {
                        $renderImages[$io] = $each;
                    }
                    $i++;
                }
            } else {
                $renderImages = $imgTmp;
            }

            $galleryRel = 'photostoragegallery';
            $previewStyle = 'style="float:left; margin:1px;"';

            $result .= wf_tag('link', false, '', 'rel="stylesheet" href="modules/jsc/image-gallery-lightjs/src/jquery.light.css"');
            $result .= wf_tag('script', false, '', 'src="modules/jsc/image-gallery-lightjs/src/jquery.light.js"') . wf_tag('script', true);

            if (!empty($renderImages)) {
                foreach ($renderImages as $io => $eachimage) {
                    $imgPreview = wf_img_sized($this->getImageUrl($eachimage['filename']), __('Preview'), $this->photoCfg['IMGLIST_PREV_W'], $this->photoCfg['IMGLIST_PREV_H']);
                    $imgFull = wf_img_sized($this->getImageUrl($eachimage['filename']), '', '100%');
                    $imgCaption = __('Date') . ': ' . $eachimage['date'] . ' ' . __('Admin') . ': ' . $eachimage['admin'];
                    $mngUrl = self::MODULE_URL . '&scope=' . $eachimage['scope'] . '&mode=list&itemid=' . $eachimage['item'];
                    $mngLink = ' ' . wf_Link($mngUrl, __('Show'), false, '', 'target=_blank');
                    $mngLink = str_replace('"', '', $mngLink);
                    $imgCaption .= $mngLink;

                    $galleryOptions = 'data-caption="' . $imgCaption . '" data-gallery="1" rel="' . $galleryRel . '" ' . $previewStyle . '"';
                    $imgGallery = wf_Link($this->getImageUrl($eachimage['filename']), $imgPreview, false, '', $galleryOptions);
                    $result .= $imgGallery;
                }
            } else {
                $result .= $messages->getStyledMessage(__('Nothing to show'), 'info');
            }

            //init gallery
            $jsGallery = wf_tag('script');
            $jsGallery .= " $('a[rel=" . $galleryRel . "]').light({
                            unbind:true,
                            prevText:'" . __('Previous') . "', 
                            nextText:'" . __('Next') . "',
                            loadText:'" . __('Loading') . "...',
                            keyboard:true
                        });
                        ";
            $jsGallery .= wf_tag('script', true);
            $result .= $jsGallery;
            $result .= wf_CleanDiv();
            $result .= $paginator;
        } else {
            $result .= $messages->getStyledMessage(__('Nothing to show'), 'warning');
        }



        return ($result);
    }

    /**
     * Returns current scope/item images list
     * 
     * @return string
     */
    public function renderImagesList() {
        if (!$this->imagesLoadedFlag) {
            $this->loadAllImages();
        }

        $result = wf_AjaxLoader();

        if (ubRouting::checkGet('batchok')) {
            $batchCnt = vf(ubRouting::get('batchok'), 3);
            if (!empty($batchCnt) and $batchCnt != '0') {
                $result .= wf_tag('span', false, 'alert_success') . __('Photos uploaded successfully') . ': ' . $batchCnt . wf_tag('span', true);
                $result .= wf_delimiter();
            }
        }

        if (!empty($this->allimages)) {
            foreach ($this->allimages as $io => $eachimage) {
                if (($eachimage['scope'] == $this->scope) and ($eachimage['item'] == $this->itemId)) {
                    $imgPreview = wf_img_sized($this->getImageUrl($eachimage['filename']), __('Show'), $this->photoCfg['IMGLIST_PREV_W'], $this->photoCfg['IMGLIST_PREV_H']);
                    $imgFull = wf_img_sized($this->getImageUrl($eachimage['filename']), '', '100%');
                    $imgFull .= wf_tag('br');
                    $imgFull .= __('Date') . ': ' . $eachimage['date'] . ' / ';
                    $imgFull .= __('Admin') . ': ' . $eachimage['admin'] . ' ';

                    $dimensions = 'width:' . ($this->photoCfg['IMGLIST_PREV_W'] + 10) . 'px;';
                    $dimensions .= 'height:' . ($this->photoCfg['IMGLIST_PREV_H'] + 10) . 'px;';
                    $result .= wf_tag('div', false, '', 'style="float:left;  ' . $dimensions . ' padding:15px;" id="ajRefCont_' . $eachimage['id'] . '"');
                    $result .= wf_modalAuto($imgPreview, __('Image') . ' ' . $eachimage['id'], $imgFull, '');
                    $result .= $this->imageControls($eachimage['id']);
                    $result .= wf_tag('div', true);
                }
            }
        }


        $result .= wf_tag('div', false, '', 'style="clear:both;"') . wf_tag('div', true);
        $result .= wf_delimiter();
        $result .= $this->backUrlHelper();
        return ($result);
    }

    /**
     * Returns list of available images for current scope/item
     * 
     * @return string
     */
    public function renderImagesRaw() {
        $result = '';
        $galleryFlag = ($this->altCfg['PHOTOSTORAGE_GALLERY']) ? true : false;

        if (!$this->imagesLoadedFlag) {
            $this->loadAllImages();
        }

        if (!empty($this->allimages)) {
            $galleryRel = 'photostoragegallery';
            $previewStyle = 'style="float:left; margin:1px;"';

            $result .= wf_tag('link', false, '', 'rel="stylesheet" href="modules/jsc/image-gallery-lightjs/src/jquery.light.css"');
            $result .= wf_tag('script', false, '', 'src="modules/jsc/image-gallery-lightjs/src/jquery.light.js"') . wf_tag('script', true);

            foreach ($this->allimages as $io => $eachimage) {
                if (($eachimage['scope'] == $this->scope) and ($eachimage['item'] == $this->itemId)) {
                    $imgPreview = wf_img_sized($this->getImageUrl($eachimage['filename']), __('Show'), $this->photoCfg['IMGLIST_PREV_W'], $this->photoCfg['IMGLIST_PREV_H']);
                    $imgFull = wf_img_sized($this->getImageUrl($eachimage['filename']), '', '100%');
                    $imgCaption = __('Date') . ': ' . $eachimage['date'] . ' ' . __('Admin') . ': ' . $eachimage['admin'];

                    if ($galleryFlag) {
                        $galleryOptions = 'data-caption="' . $imgCaption . '" data-gallery="1" rel="' . $galleryRel . '" ' . $previewStyle . '"';
                        $imgGallery = wf_Link($this->getImageUrl($eachimage['filename']), $imgPreview, false, '', $galleryOptions);
                        $result .= $imgGallery;
                    } else {
                        $result .= wf_modalAuto($imgPreview, __('Image') . ' ' . $eachimage['id'], $imgFull . $imgCaption, '');
                    }
                }
            }

            //init gallery
            $jsGallery = wf_tag('script');
            $jsGallery .= " $('a[rel=" . $galleryRel . "]').light({
                            unbind:true,
                            prevText:'" . __('Previous') . "', 
                            nextText:'" . __('Next') . "',
                            loadText:'" . __('Loading') . "...',
                            keyboard:true
                        });
                        ";
            $jsGallery .= wf_tag('script', true);
            $result .= $jsGallery;
        }


        $result .= wf_CleanDiv();
        return ($result);
    }

    /**
     * Downloads image file by its id
     * 
     * @param int $id database image ID
     */
    public function catchDownloadImage($id) {
        $id = vf($id, 3);
        if (!$this->imagesLoadedFlag) {
            $this->loadAllImages();
        }
        if (!empty($id)) {
            @$filename = $this->allimages[$id]['filename'];
            if (file_exists($this->storagePath . $filename)) {
                zb_DownloadFile($this->storagePath . $filename, 'jpg');
            } else {
                show_error(__('File not exist'));
            }
        } else {
            show_error(__('Image not exists'));
        }
    }

    /**
     * Returns some image content as is if proxy mode enabled \
     * and image file exists in storage path
     * 
     * @param string $filename
     */
    public function proxyImage($filename) {
        if ($this->proxyMode) {
            if (file_exists($this->storagePath . $filename)) {
                $imageContent = file_get_contents($this->storagePath . $filename);
                die($imageContent);
            } else {
                $noImage = file_get_contents('skins/noimage.jpg');
                die($noImage);
            }
        } else {
            $noImage = file_get_contents('skins/noimage.jpg');
            die($noImage);
        }
    }

    /**
     * deletes image from database and FS by its ID
     * 
     * @param int $id database image ID
     */
    public function catchDeleteImage($id) {
        $id = vf($id, 3);
        if (!$this->imagesLoadedFlag) {
            $this->loadAllImages();
        }
        if (!empty($id)) {
            @$filename = $this->allimages[$id]['filename'];
            if (file_exists($this->storagePath . $filename)) {
                if (cfr('PHOTOSTORAGEDELETE')) {
                    unlink($this->storagePath . $filename);
                    $this->unregisterImage($id);
                    $deleteResult = wf_tag('span', false, 'alert_warning') . __('Deleted') . wf_tag('span', true);
                } else {
                    $deleteResult = wf_tag('span', false, 'alert_error') . __('Access denied') . wf_tag('span', true);
                }
            } else {
                $deleteResult = wf_tag('span', false, 'alert_error') . __('File not exist') . wf_tag('span', true);
            }
        } else {
            $deleteResult = wf_tag('span', false, 'alert_error') . __('Image not exists') . wf_tag('span', true);
        }
        die($deleteResult);
    }

    /**
     * Catches webcam snapshot upload in background
     * 
     * @return void
     */
    public function catchWebcamUpload() {
        if (wf_CheckGet(array('uploadcamphoto'))) {
            if (!empty($this->scope)) {
                $newWebcamFilename = date("Y_m_d_His") . '_' . zb_rand_string(8) . '_webcam.jpg';
                $newWebcamSavePath = $this->storagePath . $newWebcamFilename;
                //get image data
                $dataRaw = ubRouting::post('image');
                //remove the prefix
                $uri = substr($dataRaw, strpos($dataRaw, ","));

                //decode the image data and save it to file
                file_put_contents($newWebcamSavePath, base64_decode($uri));
                if (file_exists($newWebcamSavePath)) {
                    $uploadResult = wf_tag('span', false, 'alert_success') . __('Photo upload complete') . wf_tag('span', true);
                    $this->registerImage($newWebcamFilename);
                } else {
                    $uploadResult = wf_tag('span', false, 'alert_error') . __('Photo upload failed') . wf_tag('span', true);
                }
            } else {
                $uploadResult = wf_tag('span', false, 'alert_error') . __('Strange exeption') . ': ' . self::EX_NOSCOPE . wf_tag('span', true);
            }
            die($uploadResult);
        }
    }

    /**
     * Performs some image postprocessing on images uploads
     * 
     * @param string $filePath
     * 
     * @return string
     */
    public function imagePostProcessing($filePath) {
        $result = '';
        $pixelCraft = new PixelCraft();

        $pixelCraft->loadImage($filePath);
        $imageWidth = $pixelCraft->getImageWidth();
        $imageHeight = $pixelCraft->getImageHeight();
        $originalType = $pixelCraft->getImageType();

        $result .= wf_tag('span', false, 'alert_info') . __('Original image dimensions') . ' :' . $imageWidth . 'x' . $imageHeight . wf_tag('span', true);
        $fileSizeOrig = filesize($filePath);
        $result .= wf_tag('span', false, 'alert_info') . __('Uploaded file size') . ': ' . zb_convertSize($fileSizeOrig) . wf_tag('span', true);

        $imgInfo = '';
        $imgInfo .= whoami() . ' Date: ' . date("Y-m-d H:i:s") . ' ';
        $imgInfo .= 'Orig: ' . $imageWidth . 'x' . $imageHeight . ' ' . $originalType . ', ' . zb_convertSize($fileSizeOrig) . ' ';

        //recompressing image
        if ($this->altCfg['PHOTOSTORAGE_RECOMPRESS']) {
            $quality = -1;
            if ($originalType == 'jpeg') {
                $quality = 70;
                $pixelCraft->setQuality($quality);
            }

            if ($originalType == 'png') {
                $quality = 9;
                $pixelCraft->setQuality($quality);
            }
            $result .= wf_tag('span', false, 'alert_info') . __('Image compression') . ': ' . $quality . wf_tag('span', true);
            $imgInfo .= 'Recompress: ' . $quality . ' ';
        }


        //appending watermark
        if ($this->altCfg['PHOTOSTORAGE_WATERMARK']) {
            $pixelCraft->loadWatermark(self::WATRERMARK_PATH);
            $pixelCraft->drawWatermark(false, $imageWidth - 120, 20);
            $result .= wf_tag('span', false, 'alert_info') . __('Watermark applied') . wf_tag('span', true);
        }

        //automatic downscale of huge images
        if ($this->altCfg['PHOTOSTORAGE_AUTORESIZE']) {
            $scale = 1;
            if ($imageWidth >= 4000 or $imageHeight >= 4000) {
                $scale = 0.5;
                $pixelCraft->scale($scale);
                $imageWidth = $pixelCraft->getImageWidth();
                $imageHeight = $pixelCraft->getImageHeight();
            } else {
                if ($imageWidth >= 2000 or $imageHeight >= 2000) {
                    $scale = 0.8;
                    $pixelCraft->scale($scale);
                    $imageWidth = $pixelCraft->getImageWidth();
                    $imageHeight = $pixelCraft->getImageHeight();
                }
            }

            if ($scale != 1) {
                $result .= wf_tag('span', false, 'alert_info') . __('New image dimensions') . ' :' . $imageWidth . 'x' . $imageHeight . wf_tag('span', true);
                $imgInfo .= 'Rescale x' . $scale . ': ' . $imageWidth . 'x' . $imageHeight . ' ';
            }
            $result .= wf_tag('span', false, 'alert_info') . __('Image scale') . ': ' . $scale . wf_tag('span', true);
        }

        if ($this->altCfg['PHOTOSTORAGE_DRAWIMGINFO']) {
            $infoX = 5;
            $infoY = $imageHeight - 10;
            $pixelCraft->drawString($infoX, $infoY, $imgInfo, 'yellow', 1, false);
        }

        //saving post-processed image
        $pixelCraft->saveImage($filePath, $originalType);

        //updating filesize
        clearstatcache(true, $filePath);
        $fileSizeProcessed = filesize($filePath);
        $result .= wf_tag('span', false, 'alert_info') . __('Saved file size') . ': ' . zb_convertSize($fileSizeProcessed) . wf_tag('span', true);

        return ($result);
    }

    /**
     * Normalizes $_FILES['photostorageFileUpload'] for single or multiple (name="photostorageFileUpload[]") uploads.
     *
     * @return array list of array('tmp_name'=>string,'name'=>string,'error'=>int)
     */
    protected function normalizePhotostorageFileUploads() {
        $result = array();
        if (isset($_FILES['photostorageFileUpload'])) {
            $f = $_FILES['photostorageFileUpload'];
            if (isset($f['tmp_name']) and is_array($f['tmp_name'])) {
                foreach ($f['tmp_name'] as $idx => $tmp) {
                    $oneName = '';
                    if (isset($f['name'][$idx])) {
                        $oneName = $f['name'][$idx];
                    }
                    $oneErr = UPLOAD_ERR_NO_FILE;
                    if (isset($f['error'][$idx])) {
                        $oneErr = $f['error'][$idx];
                    }
                    $result[] = array('tmp_name' => $tmp, 'name' => $oneName, 'error' => $oneErr);
                }
            } else {
                if (isset($f['tmp_name'])) {
                    $oneErr = UPLOAD_ERR_NO_FILE;
                    if (isset($f['error'])) {
                        $oneErr = $f['error'];
                    }
                    $oneName = '';
                    if (isset($f['name'])) {
                        $oneName = $f['name'];
                    }
                    $result[] = array('tmp_name' => $f['tmp_name'], 'name' => $oneName, 'error' => $oneErr);
                }
            }
        }
        return ($result);
    }

    /**
     * Converts php.ini size shorthand (e.g. 8M, 512K) to bytes.
     *
     * @param string $sizeStr
     *
     * @return int
     */
    protected function parseIniSizeToBytes($sizeStr) {
        $result = 0;
        $sizeStr = trim($sizeStr);
        if ($sizeStr != '') {
            $last = strtolower(substr($sizeStr, -1));
            $numericPart = $sizeStr;
            $multiplier = 1;
            if ($last == 'g') {
                $multiplier = 1073741824;
                $numericPart = substr($sizeStr, 0, -1);
            } else {
                if ($last == 'm') {
                    $multiplier = 1048576;
                    $numericPart = substr($sizeStr, 0, -1);
                } else {
                    if ($last == 'k') {
                        $multiplier = 1024;
                        $numericPart = substr($sizeStr, 0, -1);
                    }
                }
            }
            $num = (float) $numericPart;
            if ($num >= 0) {
                $result = (int) round($num * $multiplier);
            }
        }
        return ($result);
    }

    /**
     * Catches file upload in background
     *
     * @param string $customBackLink
     * 
     * @return void
     */
    public function catchFileUpload($customBackLink = '') {
        if (wf_CheckGet(array('uploadfilephoto'))) {
            $uploadResult = '';
            if (!empty($this->scope)) {
                $normalizedFiles = $this->normalizePhotostorageFileUploads();
                $toProcess = array();
                if (!empty($normalizedFiles)) {
                    foreach ($normalizedFiles as $io => $eachNorm) {
                        if (isset($eachNorm['error']) and $eachNorm['error'] === UPLOAD_ERR_OK and !empty($eachNorm['tmp_name'])) {
                            $toProcess[] = $eachNorm;
                        }
                    }
                }

                if (empty($toProcess)) {
                    $uploadResult = wf_tag('span', false, 'alert_error') . __('Photo upload failed') . ': ' . __('File not found') . wf_tag('span', true);
                } else {
                    $pixelCraft = new PixelCraft();
                    $successCount = 0;
                    $savedFilenames = array();
                    $errorMessages = array();
                    foreach ($toProcess as $io => $eachFile) {
                        $dispName = $eachFile['name'];
                        if ($dispName == '') {
                            $dispName = __('File');
                        }
                        $nameLower = strtolower($eachFile['name']);
                        $nameParts = explode(".", $nameLower);
                        $ext = end($nameParts);
                        if (!in_array($ext, $this->allowedExtensions)) {
                            $errorMessages[] = $dispName . ': ' . __('Photo upload failed') . ': ' . self::EX_WRONG_EXT;
                        } else {
                            if (!file_exists($eachFile['tmp_name'])) {
                                $errorMessages[] = $dispName . ': ' . __('Photo upload failed') . ': ' . __('File not found');
                            } else {
                                if (!$pixelCraft->isImageValid($eachFile['tmp_name'])) {
                                    $errorMessages[] = $dispName . ': ' . __('Photo upload failed') . ': ' . __('File') . ' ' . __('is corrupted');
                                } else {
                                    $newFilename = date("Y_m_d_His") . '_' . zb_rand_string(8) . '_upload.jpg';
                                    $newSavePath = $this->storagePath . $newFilename;
                                    @move_uploaded_file($eachFile['tmp_name'], $newSavePath);
                                    if (file_exists($newSavePath)) {
                                        if (@$this->altCfg['PHOTOSTORAGE_POSTPROCESSING']) {
                                            $this->imagePostProcessing($newSavePath);
                                        }
                                        $this->registerImage($newFilename);
                                        $successCount++;
                                        $savedFilenames[] = $newFilename;
                                    } else {
                                        $errorMessages[] = $dispName . ': ' . __('Photo upload failed');
                                    }
                                }
                            }
                        }
                    }

                    $totalAttempted = count($toProcess);
                    if ($successCount == $totalAttempted and $successCount > 0) {
                        if (empty($customBackLink)) {
                            $customBackLinkParam = '';
                        } else {
                            $customBackLinkParam = '&custombacklink=' . $customBackLink;
                        }
                        if ($successCount == 1) {
                            ubRouting::nav(self::MODULE_URL . '&scope=' . $this->scope . '&itemid=' . $this->itemId . '&mode=loader&preview=' . $savedFilenames[0] . $customBackLinkParam);
                        } else {
                            ubRouting::nav(self::MODULE_URL . '&scope=' . $this->scope . '&itemid=' . $this->itemId . '&mode=list&batchok=' . $successCount . $customBackLinkParam);
                        }
                    } else {
                        if ($successCount > 0) {
                            $uploadResult .= wf_tag('span', false, 'alert_success') . __('Photos uploaded successfully') . ': ' . $successCount . wf_tag('span', true);
                            if (!empty($errorMessages)) {
                                $uploadResult .= wf_tag('span', false, 'alert_warning') . ' ' . __('Some photos failed to upload') . wf_tag('span', true);
                            }
                        }
                        if (!empty($errorMessages)) {
                            foreach ($errorMessages as $ei => $errLine) {
                                $uploadResult .= wf_tag('br');
                                $uploadResult .= wf_tag('span', false, 'alert_error') . $errLine . wf_tag('span', true);
                            }
                        } else {
                            if ($successCount == 0) {
                                $uploadResult .= wf_tag('span', false, 'alert_error') . __('Photo upload failed') . wf_tag('span', true);
                            }
                        }
                    }
                }
            } else {
                $uploadResult = wf_tag('span', false, 'alert_error') . __('Strange exeption') . ': ' . self::EX_NOSCOPE . wf_tag('span', true);
            }

            if ($uploadResult != '') {
                show_window('', $uploadResult);
                show_window('', wf_BackLink(self::MODULE_URL . '&scope=' . $this->scope . '&itemid=' . $this->itemId . '&mode=loader'));
            }
        }
    }

    /**
     * Returns file upload form
     *
     * @param bool $embedded
     * @param string $customBackLink
     *
     * @return string
     */
    public function renderUploadForm($embedded = false, $customBackLink = '') {
        // forwarding $customBackLink to catchFileUpload() routine
        if (!empty($customBackLink)) {
            $customBackLink = '&custombacklink=' . $customBackLink;
        }

        $postUrl = self::UPLOAD_URL_FILE . '&scope=' . $this->scope . '&itemid=' . $this->itemId . $customBackLink;
        $inputs = wf_tag('form', false, 'photostorageuploadform', 'action="' . $postUrl . '" enctype="multipart/form-data" method="POST"');
        $inputs .= wf_tag('input', false, '', 'type="file" name="photostorageFileUpload[]" accept="image/*" multiple required');
        $inputs .= wf_Submit(__('Upload images'));
        $inputs .= wf_tag('form', true);

        $result = '';
        $maxPerFileBytes = $this->parseIniSizeToBytes(ini_get('upload_max_filesize'));
        $maxPostBytes = $this->parseIniSizeToBytes(ini_get('post_max_size'));
        $result .= $inputs;

        if ($maxPerFileBytes > 0 and $maxPostBytes > 0) {
            $msgSorry = __('Photostorage upload limit sorry') . zb_convertSize($maxPostBytes) . '.';
            $uploadJs = wf_tag('script', false, '', 'type="text/javascript"');
            $uploadJs .= '(function(){var forms=document.getElementsByClassName("photostorageuploadform");var maxPer=parseInt(' . (int) $maxPerFileBytes . ',10);var maxPost=parseInt(' . (int) $maxPostBytes . ',10);var mSorry=' . json_encode($msgSorry) . ';var j=0;for(j=0;j<forms.length;j++){(function(form){form.onsubmit=function(ev){var inp=form.querySelector("input[type=file]");if(!inp||!inp.files||!inp.files.length){return true;}var i=0;var total=0;for(i=0;i<inp.files.length;i++){total+=inp.files[i].size;}for(i=0;i<inp.files.length;i++){if(inp.files[i].size>maxPer){if(ev&&ev.preventDefault){ev.preventDefault();}alert("\""+inp.files[i].name+"\":\\n"+mSorry);return false;}}if(total>maxPost){if(ev&&ev.preventDefault){ev.preventDefault();}alert(mSorry);return false;}return true;};})(forms[j]);}})();';
            $uploadJs .= wf_tag('script', true);
            $result .= $uploadJs;
            $result .= wf_delimiter();
        }
        $result .= wf_delimiter(2);
        if (wf_CheckGet(array('preview'))) {
            $result .= wf_img_sized($this->getImageUrl(ubRouting::get('preview')), __('Preview'), $this->photoCfg['IMGLIST_PREV_W'], $this->photoCfg['IMGLIST_PREV_H']);
            $result .= wf_delimiter();
            $result .= wf_tag('span', false, 'alert_success') . __('Photo upload complete') . wf_tag('span', true);
            $result .= wf_delimiter();
        }

        if (!$embedded) {
            // checking for forwarded $customBackLink from catchFileUpload() routine
            if (ubRouting::checkGet('custombacklink')) {
                $result .= wf_BackLink(base64_decode(ubRouting::get('custombacklink')));
            } else {
                $result .= wf_BackLink(self::MODULE_URL . '&scope=' . $this->scope . '&itemid=' . $this->itemId . '&mode=list');
            }
        }

        return ($result);
    }

    /**
     * Returns webcamera snapshot form
     * 
     * @return string
     */
    public function renderWebcamForm() {
        $result = '';

        $init = wf_tag('link', false, '', 'href="modules/jsc/webcamlib/style.css" rel="stylesheet"') . wf_tag('link', true);
        $prev_w = $this->photoCfg['WEBCAM_PREV_W'];
        $prev_h = $this->photoCfg['WEBCAM_PREV_H'];
        $dest_w = $this->photoCfg['WEBCAM_RESULT_W'];
        $dest_h = $this->photoCfg['WEBCAM_RESULT_H'];
 
        $uploadUrl = self::UPLOAD_URL_WEBC . '&scope=' . $this->scope . '&itemid=' . $this->itemId;

        $labelCapture = wf_img('skins/photostorage.png') . ' ' . __('Take snapshot');
        $labelCaptureF = str_replace('"', '', $labelCapture);

        $labelReCapture = wf_img('skins/photostorage.png') . ' ' . __('Retake photo');
        $labelReCaptureF = str_replace('"', '', $labelReCapture);
        $labelSave = wf_img('skins/save.png') . ' ' . __('Save');
        $labelSaveF = str_replace('"', '', $labelSave);

        $container = wf_tag('div', false, 'content');
        $container .= wf_tag('div', false, 'webcamholder');
        $container .= wf_tag('video', false, '', 'autoplay id="webcamvideo"') . wf_tag('video', true);
        $container .= wf_tag('canvas', false, '', 'id="webcamcanvas"') . wf_tag('canvas', true);
        $container .= wf_tag('div', true);
        $container .= wf_tag('div', true);
        $container .= wf_tag('div', false, 'buttons');
        $container .= wf_tag('button', false, 'ubButton', 'id="buttonCapture" disabled') . $labelCapture . wf_tag('button', true) . ' ';
        $container .= wf_tag('button', false, 'ubButton', 'id="buttonSave" disabled') . $labelSave . wf_tag('button', true);
        $container .= wf_tag('div', true);
        $container .= wf_tag('div', false, '', 'id="savedImages"');
        $container .= wf_tag('div', true);

        $uploadJs = wf_tag('script', false, '', 'language="JavaScript"');
        $jScript = file_get_contents('modules/jsc/webcamlib/script.js');
        eval($jScript);

        $uploadJs .= wf_tag('script', true);

        $result .= $init;
        $result .= $container;
        $result .= $uploadJs;
        $result .= wf_delimiter(0);
        $result .= wf_BackLink(self::MODULE_URL . '&scope=' . $this->scope . '&itemid=' . $this->itemId . '&mode=list');

        return ($result);
    }
}
