<?php

/**
 * Handles Blended Filesystem , manage users and owner of blended themes.
 * Also manages WordPress and blended theme settings.
 *
 * @blended
 */

namespace Blended\hostlib;

// Manage blended theme configuration settings.
use Blended\hostlib\Blended_ini_Parser;

//use Amp\Parallel\Worker;
//use Amp\Promise;
//use Amp\Parallel\Worker\CallableTask;
//use Amp\Parallel\Worker\DefaultWorkerFactory;

// All path setting defined here.
global $blendedConfig;
$blendedConfig = new Blended_ini_Parser(dirname(__FILE__) . "/config.ini");

// React App theme Settings are stored here.
global $THEME_SETTINGS;
$THEME_SETTINGS = new Blended_ini_Parser(dirname(__FILE__) . "/theme_settings.ini");

// Path to theme directory.
global $THEME_DIRECTORY;
$THEME_DIRECTORY = dirname(__FILE__) . '/../..';

// Current active theme.
global $ACTIVE_THEME;
$ACTIVE_THEME = $blendedConfig->get("setup_configuration", "ACTIVE_THEME");

// Anonymous user works without login.
global $DEFAULT_USER;
$DEFAULT_USER = $blendedConfig->get("setup_configuration", "DEFAULT_USER");

// Theme directory where all themes files reside.
global $ROOT_DIRECTORY;
$ROOT_DIRECTORY = $blendedConfig->get("setup_configuration", "ROOT_DIRECTORY");

// Path to the theme that is to be loaded into memory.
global $ACTIVE_ROOT_DIRECTORY;
$ACTIVE_ROOT_DIRECTORY = $ROOT_DIRECTORY . '/' . $ACTIVE_THEME;

// Path used to save dependencies locally.
global $DEPENDENCY_PATH;
$DEPENDENCY_PATH = $blendedConfig->get("dependency_loader", "DEPENDENCY_PATH");

// Path used to save dependencies locally.
global $SOURCE_DIRECTORY;
$SOURCE_DIRECTORY = $blendedConfig->get("setup_configuration", "SOURCE_DIRECTORY");

// Path to css directory.
global $CSS_DIRECTORY;
$CSS_DIRECTORY = $blendedConfig->get("backend_multimedia_directory", "CSS_DIRECTORY");

// Path to image cachce directory.
global $CACHE_IMAGE_DIRECTORY;
$CACHE_IMAGE_DIRECTORY = $THEME_DIRECTORY . '/' . $blendedConfig->get("backend_multimedia_directory", "IMAGE_DIRECTORY");

// Path to media directory.
global $MEDIA_DIRECTORY;
$MEDIA_DIRECTORY = $THEME_DIRECTORY . '/' . $blendedConfig->get("backend_multimedia_directory", "MEDIA_DIRECTORY");

// Path to javascript directory.
global $JS_DIRECTORY;
$JS_DIRECTORY = $blendedConfig->get("backend_multimedia_directory", "JS_DIRECTORY");

// Path to javascript directory.
global $AUTO_INSTALL_ACQUISITION;
$AUTO_INSTALL_ACQUISITION = $blendedConfig->get("setup_configuration", "FIRSTLOGIN");

// Following extension will be treated as media type.
global $ALLOWED_IMAGE_TYPES;
$ALLOWED_IMAGE_TYPES = array(
    'jpg',
    'jpeg',
    'png'
    
);

global $ALLOWED_FILE_TYPES;
$ALLOWED_FILE_TYPES = array(
    'bd',
    'js',
    'json',
    'html',
    'css',
    'txt',
    'svg',
    'part',
    'hash'
);

// Following are invalid character list which can't be used for file/directory naming convention.
global $invalidCharacters;
$invalidCharacters = array(
   ' ',
   '!',
   '@',
   '#',
   '$',
   '%',
   '^',
   '&',
   '*',
   '(',
   ')',
   '?',
   '<',
   '>',
   '{',
   '}',
   ':',
   ';',
   ',',
   '|',
   '/',
   '`',
   '+',
   '='
);

global $invalidFileList;
$invalidFileList = array();

/**
 * File system Backend Class.
 * Retrieve packages from local file system.
 * Save user's theme specific data.
 * Save user account info
 *
 * @return packages need to render.
 */
class Backend extends \hostAuthenticator {

    /**
     * theme directory path
     *
     * @var string
     */
    public $ROOT_DIRECTORY;

    /**
     * invalid directory path
     *
     * @var string
     */
    public $invalidMediaFiles = array();

    /**
     * This is a public class, sets theme directory need to get package to render.
     *
     * @param string directory path used to set project directory.
     */
    function __construct($ROOT_DIRECTORY = null) {
        $this->ROOT_DIRECTORY = $ROOT_DIRECTORY;
    }
    
    public function __get($name) {
       return $this->$name;
    }

    /**
     * This is a public class, sets invalid theme directory.
     *
     * @param string invalid directory paths.
     */
    function invalidFileDirectories($invalidList = array(), $clearList = false) {

       /**
        * theme directory path
        *
        * @var string
        */
        global $invalidFileList;

        $invalidFileList = array_merge($invalidFileList, $invalidList);
        if($clearList == true) {
           $invalidFileList = array();
        }

        return $invalidFileList;
    }

    /**
     * Output project object from local file system.
     *
     * @param string directory path used to get project object.
     */
    function get_package($dir, $mediaError = false) {
        $packageIntermediary = new DirectoryNode($dir, null, null, $mediaError);
        return $packageIntermediary;
    }

    /**
     * Get user theme directory path.
     *
     * @param  string $user default or logged-in user name
     * @return string userLocation location to the user theme directory.
     */
    function blended_theme_dir($user = null) {
        /**
         * Blended default account name.
         *
         * @access global
         * @var    string
         */
        global $DEFAULT_USER;

        /**
         * Blended theme root directory.
         *
         * @access global
         * @var    string
         */
        global $ROOT_DIRECTORY;

        /**
         * Blended theme path upto root directory.
         *
         * @access global
         * @var    string
         */
        global $THEME_DIRECTORY;

        if ($DEFAULT_USER === $user) {
            $userLocation = $THEME_DIRECTORY . '/' . $ROOT_DIRECTORY . '/' . $DEFAULT_USER;
        } else {
            $accountSlug = $this->getCurrentAccount();
            $byPass      = $this->get_session_key();
            if(empty($byPass) && !empty($user))
		$accountSlug = $user;
            $userLocation = $THEME_DIRECTORY . '/' . $ROOT_DIRECTORY . '/' . $accountSlug;
        }

        return $userLocation;
    }

    /**
     * Check package existence in local file system.
     *
     * @param  string $package_name of project stored locally eg. accountSlug + package slug.
     * @return string|boolean package location if stored locally else false.
     */
    function package_exists($packageSlug, $label = "draft", $render = false, $mainAccount = null) {
        /**
         * Blended theme src directory.
         *
         * @access global
         * @var    string
         */
        global $SOURCE_DIRECTORY;

        /**
         * Blended theme lib directory.
         *
         * @access global
         * @var    array
         */
        global $DEPENDENCY_PATH;

        /**
         * Blended default account name.
         *
         * @access global
         * @var    string
         */
        global $DEFAULT_USER;
        $accountSlug    = $this->getCurrentAccount();
        // Do not create empty directories for other accounts if user is anonymous.
        $anonymousTrack = $accountSlug;
        $userLocation   = $this->blended_theme_dir();
        if (strpos($packageSlug, '/')) {
            $slug = basename($packageSlug);
            $ownerIdentifier = explode('/', $packageSlug);
            if ($ownerIdentifier[0] !== $accountSlug) {
                $accountSlug = $ownerIdentifier[0];
                $slug = $ownerIdentifier[1];
                if($anonymousTrack != $DEFAULT_USER || $render === true) {
                   $userLocation = $this->blended_theme_dir($accountSlug);
                }
                if(isset($mainAccount)) {
                   $userLocation = $this->blended_theme_dir($mainAccount);
                }
            }
            if (!file_exists($userLocation . '/' . $SOURCE_DIRECTORY)) {
                mkdir($userLocation . '/' . $SOURCE_DIRECTORY, 0777, true);
            }
            if (!file_exists($userLocation . '/' . $DEPENDENCY_PATH)) {
                mkdir($userLocation . '/' . $DEPENDENCY_PATH, 0777, true);
            }
            // Building project path of current account src directory ..
            $draftSrc = array_diff(scandir($userLocation . '/' . $SOURCE_DIRECTORY), array('..', '.'));
            $srcCaseTrack = array_map('strtolower', $draftSrc);
            $draftLib = array_diff(scandir($userLocation . '/' . $DEPENDENCY_PATH), array('..', '.'));
            $libCaseTrack = array_map('strtolower', $draftLib);
            $versionLib = array_diff(scandir($userLocation . '/' . $DEPENDENCY_PATH), array('..', '.'));
            $versionCaseTrack = array_map('strtolower', $versionLib);
            if (!empty($draftSrc) && $this->_in_arrayi($slug, $draftSrc) && $label === "draft") {
                $key = array_search($slug, $srcCaseTrack);
                @rename(str_replace("\\", "/", $userLocation . '/' . $SOURCE_DIRECTORY . '/' . $draftSrc[$key]), str_replace("\\", "/", $userLocation . '/' .$SOURCE_DIRECTORY . '/' . $slug));
                $packageLocation = $userLocation . '/' . $SOURCE_DIRECTORY . '/' . $slug;
            } elseif (!empty($draftLib) && $this->_in_arrayi($accountSlug, $draftLib) && $label === "draft") {
                $key = array_search($accountSlug, $libCaseTrack);
                @rename(str_replace("\\", "/", $userLocation . '/' . $DEPENDENCY_PATH . '/' . $draftLib[$key]), str_replace("\\", "/", $userLocation . '/' . $DEPENDENCY_PATH . '/' . $accountSlug));
                $libaccountDirectory = array_diff(scandir($userLocation . '/' . $DEPENDENCY_PATH . '/' . $accountSlug), array('..', '.'));
                $libCaseTrack = array_map('strtolower', $libaccountDirectory);
            } elseif (!empty($versionLib) && $this->_in_arrayi($accountSlug, $versionLib) && $label != "draft" && $label != "canonical") {
                $key = array_search($accountSlug, $versionCaseTrack);
                @rename(str_replace("\\", "/", $userLocation . '/' . $DEPENDENCY_PATH . '/' . $versionLib[$key]), str_replace("\\", "/", $userLocation . '/' . $DEPENDENCY_PATH . '/' . $accountSlug));
                $versionaccountDirectory = array_diff(scandir($userLocation . '/' . $DEPENDENCY_PATH . '/' . $accountSlug), array('..', '.'));
                $versionCaseTrack = array_map('strtolower', $versionaccountDirectory);
            } elseif (!empty($versionLib) && $this->_in_arrayi($accountSlug, $versionLib) && $label === "canonical") {
                $key = array_search($accountSlug, $versionCaseTrack);
                @rename(str_replace("\\", "/", $userLocation . '/' . $DEPENDENCY_PATH . '/' . $versionLib[$key]), str_replace("\\", "/", $userLocation . '/' . $DEPENDENCY_PATH . '/' . $accountSlug));
                $versionaccountDirectory = array_diff(scandir($userLocation . '/' . $DEPENDENCY_PATH . '/' . $accountSlug), array('..', '.'));
                $versionCaseTrack = array_map('strtolower', $versionaccountDirectory);
            }
            if (!empty($libaccountDirectory) && isset($libaccountDirectory) && $this->_in_arrayi($slug, $libaccountDirectory)) {
                $key = array_search($slug, $libCaseTrack);
                @rename(str_replace("\\", "/", $userLocation . '/' . $DEPENDENCY_PATH . '/' . $accountSlug . '/' . $libCaseTrack[$key]), str_replace("\\", "/", $userLocation . '/' . $DEPENDENCY_PATH . '/' . $accountSlug . '/' . $slug));
                $packageLocation = $userLocation . '/' . $DEPENDENCY_PATH . '/' . $accountSlug . '/' . $slug;
            } elseif (!empty($versionaccountDirectory) && isset($versionaccountDirectory) && $this->_in_arrayi($slug, $versionaccountDirectory)) {
                $key = array_search($slug, $versionCaseTrack);
                @rename(str_replace("\\", "/", $userLocation . '/' . $DEPENDENCY_PATH . '/' . $accountSlug . '/' . $versionCaseTrack[$key]), str_replace("\\", "/", $userLocation . '/' . $DEPENDENCY_PATH . '/' . $accountSlug . '/' . $slug));
                if($label == "canonical") {
                   $findCanonical = json_decode(file_get_contents($userLocation . '/' . $DEPENDENCY_PATH . '/' . $accountSlug . '/' . $slug . '/Canonical.txt'), true);
                   $label         = $findCanonical['Canonical'];
                }
                $packageLocation = $userLocation . '/' . $DEPENDENCY_PATH . '/' . $accountSlug . '/' . $slug . '/' . $label;
            }
            if (!isset($packageLocation)) {
                $packageLocation = false;
            } elseif (!file_exists($packageLocation)) {
                $packageLocation = false;
            }
        } else {
            throw new \Exception('{"status_code":"500", "message":"Invalid Package Slug passed."}');
        }
        $packageLocation = is_string($packageLocation) ? str_replace("\\", "/", $packageLocation) : $packageLocation;

        //Check if package is valid
        $packageLocation = ( false !== $packageLocation && !file_exists($packageLocation . '/_package.json') ? false : $packageLocation );
        return $packageLocation;
    }

    /**
     * Save project packageHash.
     *
     * @param string $packageSlug         project whose packageHash need to be updated.
     * @param object $packageIntermediary project object to get packageHash from.
     */
    function save_packageHash($packageSlug, $packageIntermediary) {
        $themeLocation = $this->package_exists($packageSlug);
        // update package hash.
        if ($themeLocation !== false && file_exists($themeLocation)) {
            if (is_string($packageIntermediary)) {
                $packageHash = json_encode(array('packageHash' => $packageIntermediary));
                $file = fopen($themeLocation . "/.hash", "wb");
                fwrite($file, $packageHash);
                fclose($file);
            } elseif (isset($packageIntermediary->data) && isset($packageIntermediary->hash)) {
                $packageHash = json_encode(array('packageHash' => $packageIntermediary->hash));
                $file = fopen($themeLocation . "/.hash", "wb");
                fwrite($file, $packageHash);
                fclose($file);
            }
        }
    }

    /**
     * Delete local files and empty directories from package.
     *
     * @param string $packageSlug  which need to be checked.
     * @param string $pathToDelete directories or files need to be truncated.
     */
    function delete_local_to_syncHub($packageSlug, $pathToDelete) {
        global $ALLOWED_IMAGE_TYPES;
        $themeLocation = $this->package_exists($packageSlug);
        // Building directory path to delete project file from user direcotry.
        $deleteFile = $themeLocation . '/' . $pathToDelete;
        // If file exist locally, delete it now.
        if (is_dir($deleteFile)) {
            rmdir($deleteFile);
        } else {

            //Delete dot hash media file
            $this->deleteDotFile($deleteFile);

            if (file_exists($deleteFile)) {
                $status = unlink($deleteFile);

                $deletedfileDirectory = explode('/', $deleteFile);
                unset($deletedfileDirectory[count($deletedfileDirectory) - 1]);
                $deletedfileDirectory = implode('/', $deletedfileDirectory);
                $packageDirectory = array_diff(scandir($deletedfileDirectory), array('..', '.'));
                // Delete imediate directory of $pathToDelete if empty found.
                if ($status && empty($packageDirectory)) {
                    rmdir($deletedfileDirectory);
                }
                // Delete the very first directory(? depends on the $pathToDelete request) if it gets empty 
                $firstNode = explode("/", $pathToDelete, -1);
                if (!empty($firstNode)) {
                    $firstNodeDirectory = $themeLocation . '/' . $firstNode[0];
                    $firstNodeDirectoryContents = array_diff(scandir($firstNodeDirectory), array('..', '.'));
                    if (is_dir($firstNodeDirectory) && empty($firstNodeDirectoryContents)) {
                        rmdir($firstNodeDirectory);
                    }
                }
            }
        }
    }

    function check_fileExist($packageSlug, $path, $label = 'draft') {
        global $ALLOWED_IMAGE_TYPES;
        $status = false;
        $themeLocation = $this->package_exists($packageSlug, $label);
        // Building directory path to check package file from user direcotry.
        $checkFile = $themeLocation . '/' . $path;
        $absFile = $this->getLastString($checkFile, "/");
        $fileExt = $this->getLastString($absFile);
        if(file_exists($checkFile)) {
           $status = true;
        }
        //Binary file
        if (in_array(strtolower($fileExt), $ALLOWED_IMAGE_TYPES)) {
            $hashFile = "." . $absFile . ".hash";
            $partFile = "." . $absFile . ".part";
            $tempHashFilePath = explode($absFile, $checkFile);
            $hashFilePath = $tempHashFilePath[0] . $hashFile;
            $partFilePath = $tempHashFilePath[0] . $partFile;
            if (file_exists($hashFilePath) && file_exists($partFilePath)) {
                $status = true;
            }
        }
        return $status;
    }

    /**
     * Save editor changes to local file system.
     *
     * @param  string  $packageSlug         which need to be checked.
     * @param  object  $packageIntermediary project object to get packageHash from.
     * @param  boolean $trim                used to distiguish lib or src directory of package.
     * @param  string  $themeLocation       used to locate package in local filesystem.
     * @return string|null themeLocation if directory internal path is in process else null.
     */
    function localUpdate($packageSlug, $packageIntermediary, $label, $trim, $themeLocation = null) {
        /**
         * Blended theme src directory.
         *
         * @access global
         * @var    string
         */
        global $SOURCE_DIRECTORY;

        /**
         * Blended theme lib directory.
         *
         * @access global
         * @var    array
         */
        global $DEPENDENCY_PATH;

        $accountSlug = $this->getCurrentAccount();
        $userLocation = $this->blended_theme_dir();
        // Getting current user directory.
        $ownerIdentifier = explode('/', $packageSlug);
        $slug = $ownerIdentifier[1];
        $ownerIdentifier = $ownerIdentifier[0];
        if (($trim == false || $accountSlug !== $ownerIdentifier) && $label != "draft") {
            $sourcePath = $DEPENDENCY_PATH . '/' . $ownerIdentifier . '/' . $slug . '/' . $label;
        } else if ($trim == false || $accountSlug !== $ownerIdentifier) {
            $sourcePath = $DEPENDENCY_PATH . '/' . $ownerIdentifier;
        } else {
            $sourcePath = $SOURCE_DIRECTORY;
        }
        // Building direcotry path to save project object to  the user direcotry.
        $proposedLocation = $userLocation . '/' . $sourcePath;
        // If user direcotry does not exist locally, create it now.
        if (!file_exists($proposedLocation)) {
            mkdir($proposedLocation, 0777, true);
        }
        // If theme direcotry does not exist locally, create it now.
        if (!isset($themeLocation) && $label == "draft") {
            $themeLocation = $proposedLocation . '/' . $slug;
            if (!file_exists($themeLocation)) {
                !mkdir($themeLocation, 0777, true) ? die('Failed to Create Theme Directory ...') : false;
            }
        } else if (!isset($themeLocation) && $label != "draft") {
            $themeLocation = $proposedLocation;
            if (!file_exists($themeLocation)) {
                !mkdir($themeLocation, 0777, true) ? die('Failed to Create Theme Directory ...') : false;
            }
        }
        // Create theme Internal folder direcotories and files locally.
        if ($themeLocation != "themes" || $themeLocation != "themes/" && isset($themeLocation)) {
            if (isset($packageIntermediary->data) && isset($packageIntermediary->hash)) {
                $packageIntermediary = $packageIntermediary->data;
            }

            foreach ($packageIntermediary as $key => $value) {
                $temp = $key;
                if ($value instanceof DirectoryNode) {
                    $themeLocation = $themeLocation . "/" . $temp;
                    file_exists($themeLocation) ? false : mkdir($themeLocation, 0777, true);
                    $themeLocation = $this->localUpdate($packageSlug, $value->data, $label, $trim, $themeLocation);
                } else {
                    $themeLocation = $this->save_files($themeLocation, $temp, $value);
                }
            }
            // Iterating backward and forward to save files into their respective directories.
            $themeLocation = explode("/", $themeLocation);
            $count = count($themeLocation);
            if ($count > 2) {
                unset($themeLocation[$count - 1]);
            }
            $themeLocation = implode("/", $themeLocation);
            return $themeLocation;
        }
    }

    /**
     * Update packageHash.
     *
     * @param string $packageLocation used to locate package in local filesystem.    
     * @param string $packageHash     is package packageHash.
     */
    function updatePackageHash($packageLocation, $packageHash) {
        $packageHash = array('packageHash' => $packageHash);
        $packageHash = json_encode($packageHash);
        $file = fopen($packageLocation . "/.hash", "w+");
        fwrite($file, $packageHash);
        fclose($file);
    }

    /**
     * Save project object to local file system.
     *
     * @param  string  $packageSlug         which need to be checked.
     * @param  object  $packageIntermediary project object to get packageHash from.
     * @param  boolean $trim                used to distiguish lib or src directory of package.
     * @param  string  $themeLocation       used to locate package in local filesystem.
     * @return string|null themeLocation if directory internal path is in process else null.
     */
    function save_package($packageSlug, $packageIntermediary, $label, $trim, $themeLocation = null) {
        /**
         * Blended theme src directory.
         *
         * @access global
         * @var    string
         */
        global $SOURCE_DIRECTORY;

        /**
         * Blended theme lib directory.
         *
         * @access global
         * @var    array
         */
        global $DEPENDENCY_PATH;

        $accountSlug = $this->getCurrentAccount();
        $userLocation = $this->blended_theme_dir();
        // Getting current user directory.
        $ownerIdentifier = explode('/', $packageSlug);
        $slug = $ownerIdentifier[1];
        $ownerIdentifier = $ownerIdentifier[0];
        if($label == "canonical") {
          $findCanonical = json_decode(file_get_contents($userLocation . '/' .$DEPENDENCY_PATH . '/' . $ownerIdentifier . '/' . $slug . '/Canonical.txt'), true);
          $label         = $findCanonical['Canonical'];
        }
        if (($trim == false || $accountSlug !== $ownerIdentifier) && $label != "draft") {
            $sourcePath = $DEPENDENCY_PATH . '/' . $ownerIdentifier . '/' . $slug . '/' . $label;
        } else if ($trim == false || $accountSlug !== $ownerIdentifier) {
            $sourcePath = $DEPENDENCY_PATH . '/' . $ownerIdentifier;
        } else {
            $sourcePath = $SOURCE_DIRECTORY;
        }
        // Building direcotry path to save project object to  the user direcotry.
        $proposedLocation = $userLocation . '/' . $sourcePath;
        // If user direcotry does not exist locally, create it now.
        if (!file_exists($proposedLocation)) {
            mkdir($proposedLocation, 0777, true);
        }
        // If theme direcotry does not exist locally, create it now.
        if (!isset($themeLocation) && $label == "draft") {
            $themeLocation = $proposedLocation . '/' . $slug;
            if (!file_exists($themeLocation)) {
                !mkdir($themeLocation, 0777, true) ? die('Failed to Create Theme Directory ...') : false;
            }
        } else if (!isset($themeLocation) && $label != "draft") {
            $themeLocation = $proposedLocation;
            if (!file_exists($themeLocation)) {
                !mkdir($themeLocation, 0777, true) ? die('Failed to Create Theme Directory ...') : false;
            }
        }
        // Create theme Internal folder direcotories and files locally.
        if ($themeLocation != "themes" || $themeLocation != "themes/" && isset($themeLocation)) {
            if (isset($packageIntermediary->data) && isset($packageIntermediary->hash)) {
                $packageHash = json_encode(array('packageHash' => $packageIntermediary->hash));
                $packageIntermediary = $packageIntermediary->data;
                $file = fopen($themeLocation . "/.hash", "w+");
                fwrite($file, $packageHash);
                fclose($file);
            }
            foreach ($packageIntermediary as $key => $value) {
                $temp = $key;
                if ($value instanceof DirectoryNode) {
                    $themeLocation = $themeLocation . "/" . $temp;
                    file_exists($themeLocation) ? false : mkdir($themeLocation, 0777, true);
                    $themeLocation = $this->save_package($packageSlug, $value->data, $label, $trim, $themeLocation);
                } else {
                    $themeLocation = $this->save_files($themeLocation, $temp, $value);
                }
            }

            // Iterating backward and forward to save files into their respective directories.
            $themeLocation = explode("/", $themeLocation);
            $count = count($themeLocation);
            if ($count > 2) {
                unset($themeLocation[$count - 1]);
            }
            $themeLocation = implode("/", $themeLocation);
            return $themeLocation;
        }
    }

    /**
     * Create Empty directories in partial pull.
     *
     * @param  string $proposedLocation is package location used to locate package in local filesystem.
     */
    function createEmptydirectory($packagePath, $path) {
           $filePath = $packagePath . '/' . $path;
           $filePath = str_replace("\\", "/", $filePath);
           if (!file_exists($filePath)) {
              mkdir($filePath, 0777, true);
           }   
    }
    
    /**
     * Delete or add empty directory
     * @param string $packagePath
     * @param string $path
     * @param array $hubItems
     */
    function handleEmptyDirectory($packagePath, $path, $hubItems) {
        $splitPath = explode("/", $path);
        if (false !== $splitPath && count($splitPath) > 1):
            for ($key = 0; $key <= count($splitPath) - 1; $key++) :
                if ($key !== 0)
                    $needle = $needle . "/" . $splitPath[$key];
                else
                    $needle = $splitPath[$key];

                if (in_array($needle, $hubItems)) {
                    if ($key == (count($splitPath) - 1)) {
                        $this->createEmptydirectory($packagePath, $needle);
                    }
                } else {
                    $this->delete_dir(str_replace("\\", "/", $packagePath . '/' . $needle));
                }


            endfor;
        else:
            $needle = $path;
            if (in_array($needle, $hubItems)) :
                $this->createEmptydirectory($packagePath, $needle);
            else :
                $this->delete_dir(str_replace("\\", "/", $packagePath . '/' . $needle));
            endif;
        endif;
    }

    /**
     * Save project files to local file system.
     *
     * @param  string $themeLocation used to locate package in local filesystem.
     * @param  string $fileName      which need to be saved.
     * @param  string $fileContent   is a content of file or image url in case binary files.
     * @return string package location.
     */
    function save_files($themeLocation, $fileName, $fileContent) {
        $flag = null;
        // Loading file from remote URL's.
        if ($fileContent instanceof BinaryFile) {
            // Checking if files already exist in local file system.
            if (file_exists($themeLocation . "/" . $fileName)) {
                $localFile = md5_file($themeLocation . "/" . $fileName);
                $remoteFile = md5(filter_var($fileContent->data, FILTER_VALIDATE_URL) ? $this->get_image_content($themeLocation . "/" . $fileName) : $fileContent->data);
                $localFile == $remoteFile ? $flag = true : $flag = null;
            }
            if (!file_exists($themeLocation)) {
                !mkdir($themeLocation, 0777, true) ? die('Failed to Create Theme Directory ...') : false;
            }
            if (is_null($flag)) {
                  
                if (filter_var($fileContent->data, FILTER_VALIDATE_URL)) {
                    //If content is a url
                    //$promise = [];
                    //$promise[$fileContent->data] = Worker\enqueueCallable(array(&$this, 'downloadFile'), $fileContent->data, $themeLocation . "/" . $fileName);
                    //Promise\wait(Promise\all($promise));
                    //\Amp\Loop::run(function () {
                    //$factory = new DefaultWorkerFactory();
                    //$worker = $factory->create();
                   // $result = yield $worker->enqueue(new CallableTask(array(&$this, 'downloadFile'), [$fileContent->data, $themeLocation . "/" . $fileName]));
                    
                    //$code = yield $worker->shutdown();
                    
                    //});
                    //$this->downloadFile($fileContent->data, $themeLocation . "/" . $fileName);
                } else {
                    $data = base64_decode(preg_replace('#^data:image/\w+;base64,#i', '', $fileContent->data));
                    file_put_contents($themeLocation . "/" . $fileName, $data);
                }
                //Save file hash
                $this->save_media_hash($themeLocation, $fileContent, $fileName);
                
            }
        } else if ($fileContent instanceof JsonFile) {
            $content = str_replace('\/', '/', $fileContent->data);
            $content = $this->_indent($content);
            if (!file_exists($themeLocation)) {
                !mkdir($themeLocation, 0777, true) ? die('Failed to Create Theme Directory ...') : false;
            }
            if (file_exists($themeLocation . "/" . $fileName) && !is_writable($themeLocation . "/" . $fileName)) {
                throw new \Exception('{"status_code":"500", "message":"Failed to create file : ' . $fileName . '. Permission denied in location = ' . $themeLocation . '"}');
            }
            $file = fopen($themeLocation . "/" . $fileName, "wb");
            fwrite($file, $content);
            fclose($file);
        } else if ($fileContent instanceof TextFile) {
            $content = $fileContent->data;
            if (!file_exists($themeLocation)) {
                !mkdir($themeLocation, 0777, true) ? die('Failed to Create Theme Directory ...') : false;
            }
            if (file_exists($themeLocation . "/" . $fileName) && !is_writable($themeLocation . "/" . $fileName)) {
                throw new \Exception('{"status_code":"500", "message":"Failed to create file : ' . $fileName . '. Permission denied in location = ' . $themeLocation . '"}');
            }
            $file = fopen($themeLocation . "/" . $fileName, "wb");
            fwrite($file, $content);
            fclose($file);
        }

        return $themeLocation;
    }

    /**
     * Save media hash to a hidden file 
     * @param string $themeLocation
     * @param string $fileContent
     * @param string $filename
     */
    function save_media_hash($themeLocation, $fileContent, $filename) {
        if (isset($fileContent->hash) && !empty($fileContent->hash)):
            $hash_file = $themeLocation . '/' . '.' . $filename . ".hash";
            $uri = "";
            if (filter_var($fileContent->data, FILTER_VALIDATE_URL)) {
                $uri = $fileContent->data;
                $part_file = $themeLocation . '/' . '.'. $filename . ".part";
            }
            if ($uri && !file_exists($themeLocation."/".$filename)) {
                file_put_contents($part_file, $uri);
                $this->make_hidden_file($part_file);
            }
            file_put_contents($hash_file, $fileContent->hash);
            $this->make_hidden_file($hash_file);
        endif;
    }

    /**
     * Make a hidden file
     * @param string $file
     * @return string
     */
    function make_hidden_file($file) {
        $system_info = php_uname('s');
        $file = str_replace('\\', '/', $file);
        if (strtoupper(substr($system_info, 0, 3)) === 'WIN') {
            system('attrib +H ' . escapeshellarg($file));
        }
    }
    
    /**
     * Check for hidden file
     * @param string $fn
     * @return boolean
     */
    function is_hidden_file($fn) {
        $attr = trim(exec('FOR %A IN ("'.$fn.'") DO @ECHO %~aA'));
        if($attr[3] === 'h')
            return true;
        return false;
    }

    /**
     * Get/Download package media file with respect of
     * corresponding hash file
     * @param string $packageSlug
     * @param string $filePath
     * @param string $label
     */
    function getPackageMedia($packageSlug, $filePath, $label = "draft") {
        $packagePath = $this->package_exists($packageSlug, $label);
        if (false !== $packagePath) {
            $packagePath = str_replace("\\", "/", $packagePath);
            $mediaPath = $packagePath . '/' . $filePath;
            //Check if hash file exists
            $filename = $this->getLastString($mediaPath, "/");
            $hashFile = "." . $filename . ".hash";
            $partFile = "." . $filename . ".part";
            //Construct $hashFilePath
            $tempHashFilePath = explode("/", $filePath);
            if(count($tempHashFilePath) > 1) {
                array_pop($tempHashFilePath);
                $absFilePath = implode("/", $tempHashFilePath);
                $hashFilePath = $packagePath . "/". $absFilePath . '/' . $hashFile;
                $partFilePath = $packagePath . "/". $absFilePath . "/" . $partFile;
            } else {
                $hashFilePath = $packagePath . "/" . $hashFile;
                $partFilePath = $packagePath . "/" . $partFile;
            }
                        
            if (file_exists($hashFilePath)) {
                if (file_exists($mediaPath)) {
                    //if ($this->getFileMTime($mediaPath) > $this->getFileMTime($hashFilePath)) {
                    //    unlink($mediaPath);
                    //    $this->getDotFile($partFilePath, $mediaPath);
                    //}
                } else {
                    $this->getDotFile($partFilePath, $mediaPath);
                    unlink($partFilePath);
                }
            }
        }
    }

    /**
     * Update dot/hash file content
     * @param string $hashFile
     * @param string $mediaFile
     */
    function getDotFile($partFile, $mediaFile) {
        if (file_exists($partFile)) {
            $url = file_get_contents($partFile);
            $this->downloadFile($url, $mediaFile);
        }
    }

    /**
     * Get the very last string by splitting the given string
     * followed by a separator provide. Default is "."
     * @param string $string
     * @param string $separator
     * @return boolean|string
     */
    function getLastString($string, $separator = ".") {
        if (!$string)
            return false;
        $explode = explode($separator, $string);
        $count = count($explode);
        return $explode[$count - 1];
    }
    
    /**
     * Get the last modified time of a file
     * @param string $filePath
     * @return timestamp Last modified time of a file
     */
    function getFileMTime($filePath) {
        $time = filemtime($filePath);
        $isDST = (date('I', $time) == 1);
        $systemDST = (date('I') == 1);
        $adjustment = 0;
        if ($isDST == false && $systemDST == true)
            $adjustment = 3600;
        else if ($isDST == true && $systemDST == false)
            $adjustment = -3600;
        else
            $adjustment = 0;
        return ($time + $adjustment);
    }

    /**
     * Save file changes to the package in local file system.
     *
     * @param  string $themeLocation used to locate package in local filesystem.
     * @param  string $filePath which need to be saved.
     * @param  string $fileName which need to be saved.
     * @param  string $fileContent   is a content of file or image url in case binary files.
     * @return string package location.
     */
    function updateFiles($themeLocation, $filePath, $fileName, $fileContent) {
        $src = $themeLocation . '/' . $filePath;
        $themeLocation = $this->save_files($src, $fileName, $fileContent);

        return $themeLocation;
    }

    /**
     * Download remote file to local
     * @param string $remoteFile
     * @param string $localFile
     * @return type
     */
    function downloadFile($remoteFile, $localFile) {
        if (!$remoteFile || !$localFile) {
            return;
        }
        set_time_limit(0);
        $fp = fopen($localFile, 'w+');
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $remoteFile);
        curl_setopt($ch, CURLOPT_TIMEOUT, 50);
        curl_setopt($ch, CURLOPT_FILE, $fp);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        $result = curl_exec($ch);
        curl_close($ch);
        fclose($fp);

        return $result ? true : false;
    }

    /**
     * Update Canonical.txt file.
     * 
     * @param string $packageSlug is the package fully qualified name.
     * @param string $label       is the package version points to canonical.
     */
    function updateCanonical_txt($packageSlug, $label) {

        global $DEPENDENCY_PATH;

        $accountSlug     = $this->getCurrentAccount();
        $userLocation    = $this->blended_theme_dir();
        $slug            = basename($packageSlug);
        $ownerIdentifier = explode('/', $packageSlug);
        if ($ownerIdentifier[0] !== $accountSlug) {
          $accountSlug  = $ownerIdentifier[0];
          $slug         = $ownerIdentifier[1];
          $userLocation = $this->blended_theme_dir();
        }
        if (!file_exists($userLocation . '/' . $DEPENDENCY_PATH . '/' . $accountSlug . '/' . $slug)) {
          mkdir($userLocation . '/' . $DEPENDENCY_PATH . '/' . $accountSlug . '/' . $slug, 0777, true);
        }
        $packagePath      = $userLocation . '/' . $DEPENDENCY_PATH . '/' . $accountSlug . '/' . $slug;
        $canonicalUpdate  = $packagePath . '/Canonical.txt';
        $canonicalVersion = $this->_indent(json_encode(array('Canonical' => $label)));
        $file             = fopen($canonicalUpdate, "wb");
        fwrite($file, $canonicalVersion);
        fclose($file);
    }

    /**
     * Get draft packageHash.
     *
     * @param  string $packageLocation is location to the package in filesystem.
     * @return array stored local last package Hash.
     */
    function get_local_packageHash($packageLocation) {

        if (file_exists($packageLocation . '/.hash')) {
            $packageHash = json_decode(file_get_contents($packageLocation . '/.hash'), true);
        } elseif (file_exists($packageLocation . '/.hash.txt')) {
            $packageHash = json_decode(file_get_contents($packageLocation . '/.hash.txt'), true);
        } else {
            $packageHash = array('packageHash' => '');
        }

        return $packageHash;
    }

    /**
     * Save or updates project _package.json data files to local file system.
     *
     * @param  string $name        key that needs to be updated.
     * @param  string $packageSlug which need to be saved.
     * @param  string $value       is a data associted to the key.
     * @return boolean status.
     */
    function updatePackageJson($name, $value, $packageSlug) {
        $content = file_get_contents($packageSlug . '/_package.json');
        $content = json_decode($content, true);
        $status = false;
        if (array_key_exists($name, $content)) {
            $content[$name] = $value;
            $content = json_encode($content);
            $content = $this->_indent($content);
            file_put_contents($packageSlug . '/_package.json', $content);
            $status = true;
        }

        return $status;
    }

    /**
     * Get's media path location
     *
     * @param  string $accountSlug key that needs to be updated.
     * @param  string $slug        which need to be saved.
     * @param  string $mediaPath   path to the media file.
     * @return string image file location.
     */
    function get_mediaPath($accountSlug, $slug, $filePath) {
        
        $packageSlug = $accountSlug . '/' . $slug;
        $packagePath = $this->package_exists($packageSlug);
        $mediaPath = null;
        if ($packagePath !== false) {
            $packagePath = str_replace("\\", "/", $packagePath);
            $mediaPath = $packagePath . '/' . $filePath;
            $this->getPackageMedia($packageSlug, $filePath);
        }
        return $mediaPath;
    }

    /**
     * store css files locally.
     *
     * @param  string $fileName name of file that need to be stored.
     * @param  string $content  file data.
     * @return string|boolean file URL or false.
     */
    function save_css($fileName, $content) {
        /**
         * Blended theme css directory.
         *
         * @access global
         * @var    string
         */
        global $CSS_DIRECTORY;

        if (isset($content) && isset($fileName)) {
            $url = file_put_contents(dirname(__FILE__) . $CSS_DIRECTORY . '/' . $fileName, $content);
            return $url ? $url : false;
        }
    }

    /**
     * store images locally.
     *
     * @param  string $imgName file name need to be stored.
     * @param  object $img_obg Image Resource.
     * @return string file URL.
     */
    function save_image($imgName, $imgObj) {
        /**
         * Blended theme image cache directory.
         *
         * @access global
         * @var    string
         */
        global $CACHE_IMAGE_DIRECTORY;

        $fileExtension = explode('.', $imgName);
        $count = count($fileExtension);
        $fileExtension = strtolower($fileExtension[$count - 1]);
        $savePath = $CACHE_IMAGE_DIRECTORY . '/' . $imgName;
        // Based on filetype saving files differently.
        switch ($fileExtension) {
            case "jpg":
                imagejpeg($imgObj, $savePath, 100);
                break;
            case "jpeg":
                imagejpeg($imgObj, $savePath, 100);
                break;
            case "bmp":
                imagewbmp($imgObj, $savePath);
                break;
            case "png":
                imagesavealpha($imgObj, true);
                imagepng($imgObj, $savePath, 0);
                break;
            case "gif":
                imagegif($imgObj, $savePath);
                break;
            case "webp":
                imagewebp($imgObj, $savePath);
                break;
            case "default":
                imagejpeg($imgObj, $savePath, '100');
                break;
        }
        // Destroying image object from memory.
        imagedestroy($imgObj);

        return $savePath;
    }

    /**
     * Create image object with its meta values.
     * @param type $file filepath
     * @return boolean|array
     */
    function get_media($file) {
        if(isset($file->location)) {
           $file = $file->location;
        }
        if (!$file) {
            return false;
        }

        $file_meta = getimagesize($file);
        if (!$file_meta) {
            return false;
        }

        $image = array(
            "width" => $file_meta[0],
            "height" => $file_meta[1],
            "type" => $file_meta["mime"],
            "filename" => basename($file),
            "data" => $file
        );

        return $image;
    }
    
    /**
     * Get media from HashFile object
     * @param \Blended\hostlib\HashFile $hashFile
     * @return array 
     */
    function getMediaFomHash($hashFile) {
        if (!$hashFile instanceof HashFile) {
            return null;
        }
        $tempRefFilePath = explode("." . $hashFile->ref_file . ".hash", $hashFile->location);
        $refFilePath = $tempRefFilePath[0] . $hashFile->ref_file;
        $partFilePath = $tempRefFilePath[0] . "." . $hashFile->ref_file . ".part";
        
        return array(
            "width" => "",
            "height" => "",
            "type" => "",
            "filename" => $hashFile->ref_file,
            "data" => $hashFile->href,
            "ref_file" => $refFilePath,
            "ref_part" => $partFilePath
        );
    }

    /**
     * Get image stream.
     * @param type $file filepath
     * @return mediaContent
     */
    function get_image_content($packagePath, $packageSlug = null, $label = null) {
        $mediaContent = file_get_contents($packagePath);
        return $mediaContent;
    }

    /**
     * store JS files locally and return url.
     *
     * @param  string $fileName    is the name of the file.
     * @param  string $fileContent is the content of file to be stored.
     * @return string file URL.
     */
    function save_js($fileName, $fileContent) {
        /**
         * Blended theme js directory.
         *
         * @access global
         * @var    string
         */
        global $JS_DIRECTORY;

        $fileName = $JS_DIRECTORY . '/' . $fileName;
        if (isset($fileContent) && isset($fileName)) {
            $url = file_put_contents(dirname(__FILE__) . '/' . $fileName, $fileContent);
            return $url ? $url : false;
        }
    }

    /**
     * Clone package on local file system .
     * 
     * @param  string $sourceLocation is the path to source package.
     * @param  string $packageSlug    is the package to be cloned and uses accountSlug/slug as path.
     * @return boolean $loopFlag track recursive itself
     */
    function localClone($sourceLocation, $packageSlug, $accountSlug = null, $loopFlag = true, $packageTitle = null) {
        /**
         * Blended default account name.
         *
         * @access global
         * @var    string
         */
        global $DEFAULT_USER;

        /**
         * Blended theme src directory.
         *
         * @access global
         * @var    string
         */
        global $SOURCE_DIRECTORY;

        /**
         * Blended theme lib directory.
         *
         * @access global
         * @var    array
         */
        global $DEPENDENCY_PATH;

        $userLocation = $this->blended_theme_dir();
        $accountSlug = $this->getCurrentAccount();
        if ($loopFlag == true) {
            $path = $packageSlug;
            if (strpos($path, '/')) {
                $slug = basename($path);
                $ownerIdentifier = explode('/', $path);
                // Building project path of current account src directory .
                if ($ownerIdentifier[0] !== $accountSlug) {
                    $accountSlug = $ownerIdentifier[0];
                    $slug = $ownerIdentifier[1];
                    $path = $accountSlug . '/' . $slug;
                    $destinationLocation = $userLocation . '/' . $DEPENDENCY_PATH . '/' . $path;
                } else {
                    $destinationLocation = $userLocation . '/' . $SOURCE_DIRECTORY . '/' . $slug;
                }
            }
        } else {
            $destinationLocation = $packageSlug;
        }
        $dir = opendir($sourceLocation);
        if (!file_exists($destinationLocation) && mkdir($destinationLocation) !== true) {
            throw new \Exception('{"status_code":"500", "message":"Permission denied to create Package ' . $slug . '."}');
        }
        while (false !== ($file = readdir($dir))) {
            if (($file != '.') && ($file != '..') && ($file != '.hash')) {
                if (is_dir($sourceLocation . '/' . $file)) {
                    $this->localClone($sourceLocation . '/' . $file, $destinationLocation . '/' . $file, $accountSlug, false);
                } else {
                    copy($sourceLocation . '/' . $file, $destinationLocation . '/' . $file);
                    if(true === $this->is_hidden_file($sourceLocation . '/' . $file))
                        $this->make_hidden_file($destinationLocation . '/' . $file);
                    if ($file == "_package.json") {
                        $toUpdate = file_get_contents($destinationLocation . '/' . $file);
                        $toUpdate = json_decode($toUpdate, true);
                        $toUpdate['slug']  = $slug;
                        if(isset($packageTitle)) {
                          $toUpdate['title'] = $packageTitle;
                        }
                        $toUpdate['user']  = $accountSlug;
                        $toUpdate['name']  = $accountSlug . '/' . $slug;
                        $toUpdate = json_encode($toUpdate);
                        $toUpdate = $this->_indent($toUpdate);
                        file_put_contents($destinationLocation . '/' . $file, $toUpdate);
                    }
                }
            }
        }
        closedir($dir);

        return true;
    }

    /**
     * Search for an image in Cache directory.
     * 
     * @param  string $imageName is the image file name.
     * @return string image location.
     */
    function get_image($imageName) {
        /**
         * Blended imagecache directory location.
         *
         * @access global
         * @var    string
         */
        global $CACHE_IMAGE_DIRECTORY;

        if (isset($imageName)) {
            $search_path = dirname(__FILE__) . '/' . $CACHE_IMAGE_DIRECTORY . '/' . $imageName;
            return file_exists($search_path) ? $CACHE_IMAGE_DIRECTORY . '/' . $imageName : false;
        }
    }

    /**
     * Search for an image in Cache directory.
     * Creates image resource object.
     * 
     * @param  string $imageUrl is the image path location.
     * @return object image resource.
     */
    function load_image($imageUrl) {
        $fileExtension = explode('.', $imageUrl);
        $count = count($fileExtension);
        $fileExtension = strtolower($fileExtension[$count - 1]);
        $image = null;

        switch ($fileExtension) {
            case "jpg":
                $image = imagecreatefromjpeg($imageUrl);
                break;
            case "jpeg":
                $image = imagecreatefromjpeg($imageUrl);
                break;
            case "bmp":
                $image = imagecreatefromwbmp($imageUrl);
                break;
            case "png":
                $image = imagecreatefrompng($imageUrl);
                break;
            case "gif":
                $image = imagecreatefromgif($imageUrl);
                break;
            case "webp":
                $image = imagecreatefromwebp($imageUrl);
                break;
            case "default":
                $image = imagecreatefromjpeg($imageUrl);
                break;
        }

        return $image;
    }

    /**
     * Get current active theme from stored configuration.
     * 
     * @return string active package name.
     */
    function getActiveTheme($render = false) {
        /**
         * Blended current active theme name.
         *
         * @access global
         * @var    string
         */
        global $ACTIVE_THEME;
        $active = explode('/', $ACTIVE_THEME);
        $user = $this->getCurrentAccount();
        if (empty($ACTIVE_THEME)) {
            return false;
        }
        $path = $this->package_exists($ACTIVE_THEME, 'draft', $render);
        // Check whether theme physically exists in filesytem.
        if ($path !== false) {
            return $ACTIVE_THEME;
        }
    }

    /**
     * Activate theme on being request.
     * Save theme name into current configuration.
     * 
     * @param  string $packageSlug is the package name.
     * @return string package slug.
     */
    function make_active($packageSlug) {
        $blendedConfig = new Blended_ini_Parser(dirname(__FILE__) . "/" . "config.ini");
        // Makes theme name slugified if its not.
        if (!strpos($packageSlug, '/')) {
            $packageSlug = $this->getCurrentAccount() . '/' . $packageSlug;
        }
        $blendedConfig->setValue("setup_configuration", "ACTIVE_THEME", $packageSlug);
        $blendedConfig->save();

        return $packageSlug;
    }

    /**
     * Clear active theme if theme not found.
     * Save empty theme name into current configuration.
     */
    function clearActivetheme() {
	$blendedConfig = new Blended_ini_Parser(dirname(__FILE__) . "/" . "config.ini");
	$blendedConfig->setValue("setup_configuration", "ACTIVE_THEME", null);
	$blendedConfig->save();
    }

    /**
     * first login track to download auto acquisition package.
     * Save FIRSTLOGIN into current configuration.
     */
    function firstLogintrack() {
	$blendedConfig = new Blended_ini_Parser(dirname(__FILE__) . "/" . "config.ini");
	$blendedConfig->setValue('setup_configuration', 'FIRSTLOGIN', 'false');
	$blendedConfig->save();
    }

    /**
     * Get theme setting required to render theme.
     * 
     * @return array theme settings form stored configuration.
     */
    function get_theme_settings() {
        /**
         * Blended theme settings.
         *
         * @access global
         * @var    string
         */
        global $THEME_SETTINGS;

        $settings = $THEME_SETTINGS->get('theme_settings');

        foreach ($settings as $key => $value) {
            if (empty($value)) {
                $settings[$key] = false;
            } elseif ($value == 1) {
                $settings[$key] = true;
            }
        }

        return $settings;
    }

    /**
     * Save theme settings onto current configuration.
     * 
     * @param  array $newSettings is the set of theme settings need to be updated.
     * @return array saved theme settings.
     */
    function save_theme_settings($newSettings) {
        /**
         * Blended theme settings.
         *
         * @access global
         * @var    string
         */
        global $THEME_SETTINGS;

        $settings = $THEME_SETTINGS->setSection('theme_settings', $newSettings);
        $THEME_SETTINGS->save(dirname(__FILE__) . "/" . "theme_settings.ini");
        $settings = $this->get_theme_settings();

        return $settings;
    }

    /**
     * Get Wordpress specific setting required to render theme.
     * 
     * @return array wordpress settings.
     */
    function get_wordpress_settings() {
        /**
         * Blended theme settings.
         *
         * @access global
         * @var    string
         */
        global $THEME_SETTINGS;

        $settings = $THEME_SETTINGS->get('wordpress_settings');
        foreach ($settings as $key => $value) {
            if (empty($value) && $key != "footerColumns") {
                $settings[$key] = false;
            } elseif ($value == 1 && $key != "footerColumns") {
                $settings[$key] = true;
            } elseif ($key == "footerColumns") {
                $settings[$key] = (int) $value;
            }
        }
      
            $settings['footercolumnshortcode'] = json_decode($settings['footercolumnshortcode'], true);
        

        return $settings;
    }

    /**
     * Save wordpress settings onto current configuration.
     * 
     * @param  array c is the set of wordpress settings need to be updated.
     * @return array saved wordpress settings.
     */
    function save_wordpress_settings($newSettings) {
        /**
         * Blended theme settings.
         *
         * @access global
         * @var    string
         */
        global $THEME_SETTINGS;

        if ($newSettings['displayFooterWidgets'] == false) {
            unset($newSettings['footercolumnshortcode']);
            unset($newSettings['footerColumns']);
        } else {
            $shortcodes = $newSettings['footercolumnshortcode'];
            if (!empty($shortcodes) && $newSettings['footerColumns'] !== 0) {
                for ($i = 0; $i < $newSettings['footerColumns']; $i++) {
                    $newShortcodes[$i] = $shortcodes[$i];
                }
                $newSettings['footercolumnshortcode'] = json_encode($newShortcodes);
            } else {
                $newSettings['footercolumnshortcode'] = json_encode($shortcodes);
            }
        }
        $settings = $THEME_SETTINGS->setSection('wordpress_settings', $newSettings);
        $THEME_SETTINGS->save(dirname(__FILE__) . "/" . "theme_settings.ini");
        $settings = $this->get_wordpress_settings();

        return $settings;
    }

    /**
     * Get navigation specific setting required to render theme.
     * 
     * @return array navigation settings.
     */
    function get_navigation_settings() {
        /**
         * Blended theme settings.
         *
         * @access global
         * @var    string
         */
        global $THEME_SETTINGS;

        $settings = $THEME_SETTINGS->get('navigation_settings');

        foreach ($settings as $key => $value) {
            $settings[$key] = json_decode($value, true);
        }

        return $settings;
    }

    /**
     * Save navigation settings onto current configuration.
     * 
     * @param  array $newSettings is the set of navigation settings need to be updated.
     * @return array saved navigation settings.
     */
    function save_navigation_settings($newSettings) {
        /**
         * Blended theme settings.
         *
         * @access global
         * @var    string
         */
        global $THEME_SETTINGS;

        $newSettings['main'] = json_encode($newSettings['main']);
        $newSettings['header'] = json_encode($newSettings['header']);
        $newSettings['sub'] = json_encode($newSettings['sub']);
        $newSettings['footer'] = json_encode($newSettings['footer']);
        $settings = $THEME_SETTINGS->setSection('navigation_settings', $newSettings);
        $THEME_SETTINGS->save(dirname(__FILE__) . "/" . "theme_settings.ini");
        $settings = $this->get_navigation_settings();

        return $settings;
    }

    /**
     * Get Hub Specific setting for getting and putting updates to user.
     * 
     * @return array Hub settings.
     */
    function get_hub_settings() {
        /**
         * Blended theme settings.
         *
         * @access global
         * @var    string
         */
        global $THEME_SETTINGS;

        $settings = json_decode($THEME_SETTINGS->get('hub_settings', 'updates'));
        return $settings;
    }

    /**
     * Save Hub settings onto current configuration to manage local packages.
     * 
     * @param  array $newSettings is the set of Hub settings need to be updated.
     * @return array saved Hub settings.
     */
    function save_hub_settings($newSettings) {
        /**
         * Blended theme settings.
         *
         * @access global
         * @var    string
         */
        global $THEME_SETTINGS;

        $newSettings = json_encode($newSettings);
        $settings = $THEME_SETTINGS->setValue('hub_settings', 'updates', $newSettings);
        $THEME_SETTINGS->save(dirname(__FILE__) . "/" . "theme_settings.ini");
        $settings = $this->get_hub_settings();

        return $settings;
    }

    /**
     * Set Hub current account from Hub settings on to current stored configuration.
     * 
     * @param  string $slug is the current user account slug.
     * @return string saved user slug.
     */
    function setCurrentAccount($slug) {
        /**
         * Blended theme settings.
         *
         * @access global
         * @var    string
         */
        global $THEME_SETTINGS;

        /**
         * Blended default account name.
         *
         * @access global
         * @var    string
         */
        global $DEFAULT_USER;

        if ($slug == "anonymous") {
            $slug = $DEFAULT_USER;
        }
        $settings = $THEME_SETTINGS->setValue('hub_settings', 'current_accounts', $slug);
        $THEME_SETTINGS->save(dirname(__FILE__) . "/" . "theme_settings.ini");
        $slug = $this->getCurrentAccount();

        return $slug;
    }

    /**
     * Get current logged-in account settings on to current stored configuration.
     * 
     * @return string saved user slug.
     */
    function getCurrentAccount() {
        /**
         * Blended theme settings.
         *
         * @access global
         * @var    string
         */
        global $THEME_SETTINGS;

        $current_account = $THEME_SETTINGS->get('hub_settings', 'current_accounts');

        return $current_account;
    }

    /**
     * Get current logged-in account user settings on to current stored configuration.
     * 
     * @return string saved user slug.
     */
    function getCurrentUser() {
        /**
         * Blended theme settings.
         *
         * @access global
         * @var    string
         */
        global $THEME_SETTINGS;

        $current_account = $THEME_SETTINGS->get('hub_settings', 'current_user');

        return $current_account;
    }

    /**
     * Set Hub user account from Hub settings on to current stored configuration.
     * 
     * @param  string $slug is the current user account slug.
     * @return string saved user slug.
     */
    function setCurrentUser($slug) {
        /**
         * Blended theme settings.
         *
         * @access global
         * @var    string
         */
        global $THEME_SETTINGS;

        /**
         * Blended default account name.
         *
         * @access global
         * @var    string
         */
        global $DEFAULT_USER;

        if ($slug == "anonymous") {
            $slug = $DEFAULT_USER;
        }
        $settings = $THEME_SETTINGS->setValue('hub_settings', 'current_user', $slug);
        $THEME_SETTINGS->save(dirname(__FILE__) . "/" . "theme_settings.ini");
        $slug = $this->getCurrentUser();

        return $slug;
    }

    /**
     * Merge Hub and local stored packages details.
     * 
     * @param  array $hubPackages[optional] are the package details from hub current user account.
     * @return array local + hub package details.
     */
    function get_local_Packages($hubPackages = array('items' => array())) {
        /**
         * Blended theme src directory.
         *
         * @access global
         * @var    string
         */
        global $SOURCE_DIRECTORY;

        /**
         * Blended default account name.
         *
         * @access global
         * @var    string
         */
        global $DEFAULT_USER;

        $userLocation = $this->blended_theme_dir($DEFAULT_USER);
        $dir = $userLocation . '/' . $SOURCE_DIRECTORY;
        $localPackage = array();
        if (!file_exists($dir)) {
            mkdir($dir, 0777, true);
        }
        $filesystemPackages = array_filter(scandir($dir), function($item) use ($dir) {
            return (is_dir($dir . "/" . $item) && stripos($item, ".") === false);
        });

        if (!empty($filesystemPackages)) {
            $count = 0;

            foreach ($filesystemPackages as $key => $value) {
                if (!file_exists($dir . '/' . $value . '/' . '_package.json')) {
                    // Create a log for invalid package and continue with the process silently.
                    error_log('_package.json does not exist with the package ' . $value);
                    break;
                }
                $localPackage[$count] = json_decode(file_get_contents($dir . '/' . $value . '/' . '_package.json'), true);
                if (isset($localPackage[$count]['name'])) {
                    $owning = explode('/', $localPackage[$count]['name']);
                    $accountSlug = $owning[0];
                    $slug = $owning[1];
                    $localPackage[$count]['name'] = strtolower($DEFAULT_USER . '/' . $value);
                    $content = $localPackage[$count];
                    $content = $this->_indent(json_encode($content));
                    @file_put_contents($dir . '/' . $value . '/' . '_package.json', $content);
                } else {
                    $localPackage[$count]['name'] = strtolower($DEFAULT_USER . '/' . $value);
                }
                if (isset($localPackage[$count]['thumbnail_image']) && !empty($localPackage[$count]['thumbnail_image'])) {
                    $path = $this->get_mediaPath($accountSlug, $slug, $localPackage[$count]['thumbnail_image']);
                    $localPackage[$count]['thumbnail_image'] = $this->get_url($path);
                }
                if (isset($localPackage[$count]['grid_layout_image']) && !empty($localPackage[$count]['grid_layout_image'])) {
                    $path = $this->get_mediaPath($accountSlug, $slug, $localPackage[$count]['grid_layout_image']);
                    $localPackage[$count]['grid_layout_image'] = $this->get_url($path);
                }
                if (isset($localPackage[$count]['block_layout_image']) && !empty($localPackage[$count]['block_layout_image'])) {
                    $path = $this->get_mediaPath($accountSlug, $slug, $localPackage[$count]['block_layout_image']);
                    $localPackage[$count]['block_layout_image'] = $this->get_url($path);
                }
                if (isset($localPackage[$count]['details_image']) && !empty($localPackage[$count]['details_image'])) {
                    $path = $this->get_mediaPath($accountSlug, $slug, $localPackage[$count]['details_image']);
                    $localPackage[$count]['details_image'] = $this->get_url($path);
                }
                $count++;
            }
            $allPackages = array_merge($hubPackages['items'], $localPackage);
            $hubPackages['items'] = $allPackages;
        }

        return $hubPackages;
    }

    /**
     * Move anonymous package to user directory on Signup.
     * 
     * @param  array $hubPackages[optional] are the package details from hub current user account.
     * @param  array $login[optional] to differentiate login and signup.
     * @return array local + hub package details.
     */
    function sync_client_hub($hubPackages) {
        /**
         * Blended theme src directory.
         *
         * @access global
         * @var    string
         */
        global $SOURCE_DIRECTORY;

        /**
         * Blended default account name.
         *
         * @access global
         * @var    string
         */
        global $DEFAULT_USER;

        $syncList = array();
        $toSink = array();
        $conflictCheck = array();
        $loggedinUser = $this->getCurrentAccount();
        $localPackage = $this->get_local_Packages();
        $this->setCurrentAccount($loggedinUser);
        $userLocation = $this->blended_theme_dir($DEFAULT_USER);
        $src = $userLocation . '/' . $SOURCE_DIRECTORY;
        $userLocation = $this->blended_theme_dir();
        $dst = $userLocation . '/' . $SOURCE_DIRECTORY;
        if (!file_exists($dst)) {
            mkdir($dst, 0777, true);
        }
        $userPackages = array_diff(scandir($dst), array('..', '.'));

        foreach ($localPackage['items'] as $key => $value) {
            $syncList = array_merge($syncList, array(strtolower($value['name'])));
        }
        foreach ($hubPackages['items'] as $key => $value) {
            if (array_search(strtolower($value['name']), $syncList) !== false) {                
                $toSink = array_merge($toSink, array(strtolower($value['user'] . '/' . $DEFAULT_USER . '/' . $value['slug'])));
                unset($syncList[array_search(strtolower($value['user'] . '/' . $value['slug']), $syncList)]);

                foreach ($localPackage['items'] as $localKey => $localValue) {
                    if (strtolower($localValue['slug']) === strtolower($value['slug'])) {
                        unset($localPackage['items'][$localKey]);
                    }
                }
            } 
            foreach ($localPackage['items'] as $localKey => $localValue) {
               if (strtolower($localValue['slug']) === strtolower($value['slug'])) {
                  $conflictCheck[] = $value['slug'];
               }
            }
        }

        if(!empty($conflictCheck)){
           $hubPackages['conflicts'] = $conflictCheck;
        }

        $hubPackages['items'] = array_merge($hubPackages['items'], $localPackage['items']);
        $hubPackages['diffs'] = $toSink;
        $hubPackages['syncList'] = $syncList;

        return $hubPackages;
    }

    /**
     * Move anonymous package to user directory on login/Signup.
     * 
     * @param  array $checkList are the package which need to be copied over.
     */
    function movePackage($checkList) {
        /**
         * Blended theme src directory.
         *
         * @access global
         * @var    string
         */
        global $SOURCE_DIRECTORY;

        /**
         * Blended default account name.
         *
         * @access global
         * @var    string
         */
        global $DEFAULT_USER;

        $loggedinUser = $this->getCurrentAccount();
        $userLocation = $this->blended_theme_dir($DEFAULT_USER);
        $src = $userLocation . '/' . $SOURCE_DIRECTORY;
        $userLocation = $this->blended_theme_dir();
        $dst = $userLocation . '/' . $SOURCE_DIRECTORY;
        $syncList = $checkList['syncList'];
        if(empty($syncList)) {
           return true;
        }
        foreach ($syncList as $value) {
            $name = explode('/', $value);
            $slug = $name[1];
            if (file_exists($src . '/' . $slug . '/_package.json') && !in_array($slug, $checkList['dontMove'])) {
                if(file_exists($dst . '/' . $slug . '/_package.json')) {
                   $this->localDelete($dst . '/' . $slug, null, false);
                }
                $this->move_packages($src . '/' . $slug, $dst . '/' . strtolower($slug));
                $this->localDelete($src . '/' . $slug, null, false);
                $owningPackage = file_get_contents($dst . '/' . strtolower($slug) . '/' . '_package.json');
                if (json_decode($owningPackage, true) != null) {
                    $owningPackage = json_decode($owningPackage, true);
                    $owningPackage['name'] = $loggedinUser . '/' . $slug;
                    $owningPackage['slug'] = $slug;
                    $owningPackage['user'] = $loggedinUser;
                    $owningPackage = json_encode($owningPackage);
                    $owningPackage = $this->_indent($owningPackage);
                    file_put_contents($dst . '/' . strtolower($slug) . '/' . '_package.json', $owningPackage);
                    $this->get_package($dst . '/' . strtolower($slug));
                }
            }
        }
        $this->localDelete($src . '/..', null, false);
    }

    /**
     * Check for any package conflicts before moving package during login/Signup.
     * 
     * @param  array $slug is package name.
     * @return array packageIntermediary.
     */
    function conflictCompare($slug) {
        /**
         * Blended theme src directory.
         *
         * @access global
         * @var    string
         */
        global $SOURCE_DIRECTORY;

        /**
         * Blended default account name.
         *
         * @access global
         * @var    string
         */
        global $DEFAULT_USER;

        $userLocation = $this->blended_theme_dir($DEFAULT_USER);
        $src = $userLocation . '/' . $SOURCE_DIRECTORY;
        $packageIntermediary = $this->get_package($src . '/' . $slug);
        $oldPackagejson = json_decode($packageIntermediary->data['_package.json']->data, true);
        $packageUpdate  = $oldPackagejson;
        $loggedinUser = $this->getCurrentAccount();
        $packageUpdate['name'] = 'blended/' . $slug;
        $packageUpdate['user'] = 'blended';
        $packageUpdate = json_encode($packageUpdate);
        $packageUpdate = $this->_indent($packageUpdate);
        file_put_contents($src . '/' . strtolower($slug) . '/' . '_package.json', $packageUpdate);
        $packageIntermediary = $this->get_package($src . '/' . $slug);
        $oldPackagejson = json_encode($oldPackagejson);
        $oldPackagejson = $this->_indent($oldPackagejson);
        file_put_contents($src . '/' . strtolower($slug) . '/' . '_package.json', $oldPackagejson);
        
        return $packageIntermediary;
    }

    /**
     * Merge Hub and local stored packages details of logged-in user account.
     * 
     * @param  array $hubPackages[optional] are the package details from hub current user account.
     * @return array local + hub package details.
     */
    function get_account_Packages($hubPackages = array('items' => array())) {
        /**
         * Blended theme src directory.
         *
         * @access global
         * @var    string
         */
        global $SOURCE_DIRECTORY;

        /**
         * Blended theme lib directory.
         *
         * @access global
         * @var    array
         */
        global $DEPENDENCY_PATH;

        $userLocation = $this->blended_theme_dir();
        $dir = $userLocation . '/' . $SOURCE_DIRECTORY;
        $libDir = $userLocation . '/' . $DEPENDENCY_PATH;
        if (!file_exists($dir)) {
            mkdir($dir, 0777, true);
        }
        $filesystemPackages = array_diff(scandir($dir), array('..', '.', '.hash'));
        if (!file_exists($libDir)) {
            mkdir($libDir, 0777, true);
        }
        if (!empty($filesystemPackages)) {
            $count = 0;
            $localPackage = array();
            foreach ($filesystemPackages as $key => $value) {
                if (file_exists($dir . '/' . $value . '/' . '_package.json') && json_decode(file_get_contents($dir . '/' . $value . '/' . '_package.json'), true) != null) {
                    $info = json_decode(file_get_contents($dir . '/' . $value . '/' . '_package.json'), true);
                    if (isset($info['name']) && isset($info['user'])) {
                        $localPackage[$count] = $info;
                        $count++;
                    }
                }
            }
            $allPackages = array_merge($hubPackages['items'], $localPackage);
            $hubPackages['items'] = $allPackages;
        }

        return $hubPackages;
    }

    /**
     * Merge Hub and local stored packages details of logged-in user account.
     * 
     * @param  array $hubPackages are the package details from hub current user account.
     * @return array local + hub package details.
     */
    function sync_client_account($hubPackages) {
        /**
         * Blended default account name.
         *
         * @access global
         * @var    string
         */
        global $DEFAULT_USER;

        $syncList = array();
        $toSink = array();
        // Get package details from current logged-in user.
        $localPackages = $this->get_account_Packages();
        foreach ($localPackages['items'] as $key => $value) {
            $packageSlug = explode('/', $value['name']);
            $accountSlug = $packageSlug[0];
            $slug = $packageSlug[1];
            if (array_search(strtolower($value['name'] . '/' . $value['user']), $syncList) !== false) {
                unset($localPackages['items'][$key]);
            } else {
                $localPackages['items'][$key]['user'] = strtolower($value['user']);
                $localPackages['items'][$key]['name'] = strtolower($value['name']);
                $syncList = array_merge($syncList, array(strtolower($value['name'] . '/' . $value['user'])));
            }
            if (isset($value['thumbnail_image']) && !empty($value['thumbnail_image'])) {
                $path = $this->get_mediaPath($accountSlug, $slug, $value['thumbnail_image']);
                $localPackages['items'][$key]['thumbnail_image'] = $this->get_url($path);
            }
            if (isset($value['grid_layout_image']) && !empty($value['grid_layout_image'])) {
                $path = $this->get_mediaPath($accountSlug, $slug, $value['grid_layout_image']);
                $localPackages['items'][$key]['grid_layout_image'] = $this->get_url($path);
            }
            if (isset($value['block_layout_image']) && !empty($value['block_layout_image'])) {
                $path = $this->get_mediaPath($accountSlug, $slug, $value['block_layout_image']);
                $localPackages['items'][$key]['block_layout_image'] = $this->get_url($path);
            }
            if (isset($value['details_image']) && !empty($value['details_image'])) {
                $path = $this->get_mediaPath($accountSlug, $slug, $value['details_image']);
                $localPackages['items'][$key]['details_image'] = $this->get_url($path);
            }
        }

        foreach ($hubPackages['items'] as $key => $value) {
            if (array_search(strtolower($value['name'] . '/' . $value['user']), $syncList) !== false) {
                $toSink = array_merge($toSink, array($value['user'] . '/' . $DEFAULT_USER . '/' . $value['slug']));
                unset($syncList[array_search(strtolower($value['name'] . '/' . $value['user']), $syncList)]);
                foreach ($localPackages['items'] as $localKey => $localValue) {
                    if (strtolower($localValue['name'] . $localValue['user']) === strtolower($value['name'] . $value['user'])) {
                        // If package available to local filesystem then make its details prioritize.
                        unset($hubPackages['items'][$key]);
                    }
                }
            }
        }
        $hubPackages['items'] = array_merge($hubPackages['items'], $localPackages['items']);
        $hubPackages['diffs'] = $toSink;

        return $hubPackages;
    }

    /**
     * Retrieve Image URL.
     *
     * @param  string $imageLocation is the image location in filesystem.
     * @return string image url.
     */
    function get_url($imageLocation) {
        $URL = null;
        if(!is_null($imageLocation)) {
	    $serverName = $_SERVER['HTTP_HOST'];
            $documentRoot = $_SERVER['DOCUMENT_ROOT'];
            $documentRoot = str_replace('\\', '/', $documentRoot);
            $imageLocation = str_replace('\\', '/', $imageLocation);
            $findRoot = stripos($imageLocation, $documentRoot);
	    $basename = basename($documentRoot);
	    $basename = '/' . $basename . '/';
	    $findDomain = stripos($imageLocation, $basename);
	    if ($findRoot === false && $findDomain !== false) {
	        $imageLocation = explode($basename, $imageLocation);
	        $imageLocation = $imageLocation[1];
      	    } else {
	        $imageLocation = str_ireplace($documentRoot, "", $imageLocation);
	    }
	    $URL = '//' . $serverName . '/' . $imageLocation;
        }

        return $URL;
    }

    /**
     * Get home url
     * @return string
     */
    function home_base_url() {
        $base_url = (isset($_SERVER['HTTPS']) &&
                $_SERVER['HTTPS'] != 'off') ? 'https://' : 'http://';
        $tmpURL = dirname(__FILE__);
        $tmpURL = str_replace(chr(92), '/', $tmpURL);
        $tmpURL = str_replace($_SERVER['DOCUMENT_ROOT'], '', $tmpURL);
        $tmpURL = ltrim($tmpURL, '/');
        $tmpURL = rtrim($tmpURL, '/');

        if (strpos($tmpURL, '/')) {
            $tmpURL = explode('/', $tmpURL);
            $tmpURL = $tmpURL[0];
        }

        if ($tmpURL !== $_SERVER['HTTP_HOST'])
            $base_url .= $_SERVER['HTTP_HOST'] . '/' . $tmpURL . '/';
        else
            $base_url .= $tmpURL . '/';

        return $base_url;
    }

    /**
     * Check file exist with the package.
     *
     * @param  string $packagePath is the current account slug + package_name.
     * @return array|boolean as package details else false on failure.
     */
    function is_validPackage($packagePath) {
       $flag = false;
       if(file_exists($packagePath . '/_package.json')) {
          $flag = true;
       }

       return $flag;
    }

    /**
     * Check media exist in database.
     *
     * @param  string $mediaHash.
     * @return array|boolean as media else false on failure.
     */
    function get_imageByHash($mediaHash, $data = false) {
       return false;
    }

    /**
     * Get packageHash stored in file during last sync.
     *
     * @param  object $packageObject.
     * @return string as packageHash.
     */
    function get_oldHash($packagePath) {
       $packageHash = "";
       if(file_exists($packagePath . '/.hash')) {
          $packageHash = file_get_contents($packagePath . "/.hash");
          $packageHash = json_decode($packageHash, true);
          $packageHash = $packageHash["packageHash"];
       }

       return $packageHash;
    }

    /**
     * Get package details.
     *
     * @param  string $packageSlug is the current account slug + package_name.
     * @param  string $loginStatus is the sessionkey of the user if logged-in to Hub or string "offline".
     * @return array|boolean as package details else false on failure.
     */
    function localPackageGet($packagePath) {
        if (is_string($packagePath) && file_exists($packagePath . '/_package.json')) {
            $packageDetails = json_decode(file_get_contents($packagePath . '/_package.json'), true);

            $userIdentifier = explode('/', $packageDetails['name']);
            $accountSlug = $userIdentifier[0];
            $slug = $userIdentifier[1];
            if (isset($packageDetails['thumbnail_image']) && !empty($packageDetails['thumbnail_image'])) {
                $path = $this->get_mediaPath($accountSlug, $slug, $packageDetails['thumbnail_image']);
                $packageDetails['thumbnail_image'] = $this->get_url($path);
            }
            if (isset($packageDetails['grid_layout_image']) && !empty($packageDetails['grid_layout_image'])) {
                $path = $this->get_mediaPath($accountSlug, $slug, $packageDetails['grid_layout_image']);
                $packageDetails['grid_layout_image'] = $this->get_url($path);
            }
            if (isset($packageDetails['block_layout_image']) && !empty($packageDetails['block_layout_image'])) {
                $path = $this->get_mediaPath($accountSlug, $slug, $packageDetails['block_layout_image']);
                $packageDetails['block_layout_image'] = $this->get_url($path);
            }
            if (isset($packageDetails['details_image']) && !empty($packageDetails['details_image'])) {
                $path = $this->get_mediaPath($accountSlug, $slug, $packageDetails['details_image']);
                $packageDetails['details_image'] = $this->get_url($path);
            }
        } else if (is_array($packagePath)) {
            $package = $packagePath;

            foreach ($package as $value) {
                if (isset($value['_package.json'])) {
                    $packageDetails = json_decode(gzuncompress(base64_decode($value['_package.json']['content'])), true);
                    $userIdentifier = explode('/', $packageDetails['name']);
                    $accountSlug = $userIdentifier[0];
                    $slug = $userIdentifier[1];
                    if (isset($packageDetails['thumbnail_image']) && !empty($packageDetails['thumbnail_image'])) {
                        $path = $this->get_mediaPath($accountSlug, $slug, $packageDetails['thumbnail_image']);
                        $packageDetails['thumbnail_image'] = $this->get_url($path);
                    }
                    if (isset($packageDetails['grid_layout_image']) && !empty($packageDetails['grid_layout_image'])) {
                        $path = $this->get_mediaPath($accountSlug, $slug, $packageDetails['grid_layout_image']);
                        $packageDetails['grid_layout_image'] = $this->get_url($path);
                    }
                    if (isset($packageDetails['block_layout_image']) && !empty($packageDetails['block_layout_image'])) {
                        $path = $this->get_mediaPath($accountSlug, $slug, $packageDetails['block_layout_image']);
                        $packageDetails['block_layout_image'] = $this->get_url($path);
                    }
                    if (isset($packageDetails['details_image']) && !empty($packageDetails['details_image'])) {
                        $path = $this->get_mediaPath($accountSlug, $slug, $packageDetails['details_image']);
                        $packageDetails['details_image'] = $this->get_url($path);
                    }
                }
            }
        } else {
            $packageDetails = false;
        }

        return $packageDetails;
    }

    /**
     * Move packages from source directory to destination directory.
     * 
     * @param string $src is the source directory to be copied.
     * @param string $dst is the destination.
     */
    function move_packages($src, $dst, $sourcePath = "", $destinationPath = "") {
        global $ALLOWED_IMAGE_TYPES;
        $src = $sourcePath . $src;
        $dst = $destinationPath . $dst;
        $src = str_replace('"\"', '"/"', $src);
        $dst = str_replace('"\"', '"/"', $dst);
        if (is_dir($src)) {
            @mkdir($dst, 0777, true);
            $files = scandir($src);

            foreach ($files as $file) {
                if ($file != "." && $file != "..") {
                    $this->move_packages($src . '/' . $file, $dst . '/' . $file);
                }
            }
        } else {
            //Binary File
            $absFileSrc = $this->getLastString($src, "/");
            $absFileDst = $this->getLastString($dst, "/");
            $fileExt = $this->getLastString($absFileSrc);
            if (in_array(strtolower($fileExt), $ALLOWED_IMAGE_TYPES)) {
                $hashFileSrc = "." . $absFileSrc . ".hash";
                $partFileSrc = "." . $absFileSrc . ".part";
                $tempHashFilePathSrc = explode($absFileSrc, $src);
                $tempHashFilePathDst = explode($absFileDst, $dst);
                $hashFilePathSrc = $tempHashFilePathSrc[0] . $hashFileSrc;
                $partFilePathSrc = $tempHashFilePathSrc[0] . $partFileSrc;
                $hashFilePathDst = $tempHashFilePathDst[0] . $hashFileSrc;
                $partFilePathDst = $tempHashFilePathDst[0] . $partFileSrc;
                if (file_exists($hashFilePathSrc) && true === $this->is_hidden_file($hashFilePathSrc)) {
                    copy($hashFilePathSrc, $hashFilePathDst);
                    $this->make_hidden_file($hashFilePathDst);
                }
                    
                if (file_exists($partFilePathSrc) && true === $this->is_hidden_file($partFilePathSrc))
                    copy($partFilePathSrc, $partFilePathDst);
                    $this->make_hidden_file($partFilePathDst);
            }
            if (file_exists($src)) {
                $directory = explode('/', $dst);
                unset($directory[count($directory) - 1]);
                $directory = implode('/', $directory);
                if (!file_exists($directory)) {
                    if (@!mkdir($directory, 0777, true)) {
                        throw new \Exception('{"status_code":"500", "message":"Failed to create directory. Permission denied in location = ' . $directory . '"}');
                    }
                }
                if (basename($src) != '.hash') {
                    copy($src, $dst);
                }
            }
        }
    }

    /**
     * Delete dot media files
     * @global array $ALLOWED_IMAGE_TYPES
     * @param string $ref_file
     */
    function deleteDotFile($ref_file) {
        global $ALLOWED_IMAGE_TYPES;
        //Delete dot hash media file
        $absFile = $this->getLastString($ref_file, "/");
        $fileExt = $this->getLastString($absFile);
        if (in_array(strtolower($fileExt), $ALLOWED_IMAGE_TYPES)) {
            $hashFile = "." . $absFile . ".hash";
            $partFile = "." . $absFile . ".part";
            $tempHashFilePath = explode($absFile, $ref_file);
            $hashFilePath = $tempHashFilePath[0] . $hashFile;
            $partFilePath = $tempHashFilePath[0] . $partFile;
            if (file_exists($hashFilePath))
                unlink($hashFilePath);
            if (file_exists($partFilePath))
                unlink($partFilePath);
        }
    }

    /**
     * Delete target path with hub items check simultaneously
     * @param string $packagePath
     * @param string $deletePath
     * @param array $hubItems
     */
    function localDeleteSync($packagePath, $deletePath, $hubItems) {
        $splitPath = explode("/", $deletePath);
        if (false !== $splitPath && count($splitPath) > 1):
            for ($key = 0; $key <= count($splitPath) - 1; $key++) :
                if ($key !== 0)
                    $needle = $needle . "/" . $splitPath[$key];
                else
                    $needle = $splitPath[$key];

                if (in_array($needle, $hubItems))
                    continue;
                else
                    break;
            endfor;
        else:
            $needle = $deletePath;
        endif;
    
        //Build absolute path
        $absPath = $packagePath . '/' . $needle;
        $absPath = str_replace("\\", "/", $absPath);
        //Check type whether it is a directory or file
        if (is_dir($absPath)) :
            $this->delete_dir($absPath);
        else :
            //Delete dot hash media file
            $this->deleteDotFile($absPath);
            @unlink($absPath);
        endif;
    }
    
    /**
     * Delete a directory
     * @param string $dir
     * @return boolean
     */
    function delete_dir($dir) {
        $files = array_diff(scandir($dir), array('.', '..'));
        foreach ($files as $file) {
            (is_dir("$dir/$file")) ? $this->delete_dir("$dir/$file") : @unlink("$dir/$file");
        }
        return @rmdir($dir);
    }

    /**
     * Delete package or package files from package directory.
     * 
     * @param  string $dir path of the directory to be deleted.
     * @return string|null file location to be removed else null.
     */
    function localDelete($dir, $file, $deleteDirectory) {
        if (isset($file)) {
            $dir = $dir . '/' . $file;
        }
        $dir = str_replace("\\", "/", $dir);
        if (is_dir($dir)) {
            $files = array_diff(scandir($dir), array('.', '..'));
            foreach ($files as $single_file) {
                (is_dir("$dir/$single_file")) ? $this->localDelete("$dir/$single_file", $file, $deleteDirectory) : unlink("$dir/$single_file");
            }
            return @rmdir($dir);
        } else {
            @unlink($dir);
            //Delete dot hash media file
            $this->deleteDotFile($file);
            
            $dir = explode('/', $dir);
            unset($dir[count($dir) - 1]);
            $dir = implode('/', $dir);
            $files = array_diff(scandir($dir), array('.', '..'));
            if (true == $deleteDirectory && is_dir($dir) && empty($files)) {
                @rmdir($dir);
            }

            // Delete the very first directory(? depends on the $file request) if it gets empty 
            $firstNode = explode("/", $file, -1);
            if (!empty($firstNode)) {
                $firstNodeDirectory = $dir . '/' . $firstNode[0];
                $firstNodeDirectoryContents = array_diff(scandir($firstNodeDirectory), array('..', '.'));
                if (true == $deleteDirectory && is_dir($firstNodeDirectory) && empty($firstNodeDirectoryContents)) {
                    rmdir($firstNodeDirectory);
                }
            }
        }
    }

    /**
     * Create empty package.
     *
     * @param  string $accountSlug is the user account name.
     * @param  string $slug        is the package name.
     * @param  string $version     is the package version.
     * @param  string $user        is the package user.
     * @return array package jptf representation.
     */
    function localCreate($accountSlug, $slug, $version, $user, $loginStatus) {
        /**
         * Blended theme src directory.
         *
         * @access global
         * @var    string
         */
        global $SOURCE_DIRECTORY;

        /**
         * Blended theme lib directory.
         *
         * @access global
         * @var    array
         */
        global $DEPENDENCY_PATH;

        /**
         * Blended default account name.
         *
         * @access global
         * @var    string
         */
        global $DEFAULT_USER;

        $path = $accountSlug . '/' . $slug;
        $accountSlug = $this->getCurrentAccount();
        $userLocation = $this->blended_theme_dir();
        $defaultuserLocation = $this->blended_theme_dir($DEFAULT_USER);
        if (strpos($path, '/')) {
            $ownerIdentifier = explode('/', $path);
            if ($ownerIdentifier[0] !== $accountSlug) {
                $accountSlug = $ownerIdentifier[0];
                $path = $accountSlug . '/' . $slug;
            }
            // Building project path of current account src directory .
            if ($loginStatus == "offline" && $version == "draft") {
                $path_dir = $defaultuserLocation . '/' . $SOURCE_DIRECTORY . '/' . $slug;
                $themeLocation = $path_dir;
            } else if ($loginStatus == "offline" && $version != "draft") {
                $path_lib = $defaultuserLocation . '/' . $DEPENDENCY_PATH . '/' . $path;
                $themeLocation = $path_lib;
            } else if ($version == "draft") {
                $path_dir = $userLocation . '/' . $SOURCE_DIRECTORY . '/' . $slug;
                $themeLocation = $path_dir;
            } else {
                $path_lib = $userLocation . '/' . $DEPENDENCY_PATH . '/' . $accountSlug . '/' . $slug . '/' . $version;
                $themeLocation = $path_lib;
            }
            // Change project path project found to be in lib direcotory.
            if (file_exists($themeLocation . '/_package.json') && json_decode(file_get_contents($themeLocation . '/_package.json'), true)) {
                throw new \Exception('{"status_code":"500", "message":"Package ' . $slug . ' already exist."}');
            } else {
                $content = json_encode(array('name' => $path, 'user' => $user, 'slug' => $slug, 'version' => $version));
                mkdir($themeLocation, 0777, true);
                $file = fopen($themeLocation . "/_package.json", "wb");
                fwrite($file, $content);
                fclose($file);
            }
        }
    }

    /**
     * Get local stored package details of a pakcage.
     *
     * @param  string $slug                   is the package name.
     * @param  string $accountSlug(anonymous) is the default user account for offline.
     * @return array local package details.
     */
    function get_Offline_Package_Details($packageSlug, $loginStatus, $packagePath = null) {
        /**
         * Blended theme src directory.
         *
         * @access global
         * @var    string
         */
        global $SOURCE_DIRECTORY;

        /**
         * Blended theme lib directory.
         *
         * @access global
         * @var    array
         */
        global $DEPENDENCY_PATH;

        /**
         * Blended default account name.
         *
         * @access global
         * @var    string
         */
        global $DEFAULT_USER;

        $accountSlug = $this->getCurrentAccount();
        $packageDetails = array();
        if ($accountSlug == null) {
            $accountSlug = $DEFAULT_USER;
        }
        $path = $packageSlug;
        $userLocation = $this->blended_theme_dir();
        $defaultuserLocation = $this->blended_theme_dir($DEFAULT_USER);
        if (strpos($path, '/')) {
            $package_name = basename($path);
            $owner_check = explode('/', $path);
            if ($owner_check[0] !== $accountSlug) {
                $accountSlug = $owner_check[0];
                $slug = $owner_check[1];
                $path = $accountSlug . '/' . $slug;
            }
            // Building project path of current account src directory .
            if ($loginStatus == "offline") {
                $path_lib = $defaultuserLocation . '/' . $DEPENDENCY_PATH . '/' . $path . '/_package.json';
                $path_dir = $defaultuserLocation . '/' . $SOURCE_DIRECTORY . '/' . $package_name . '/_package.json';
            } else {
                $path_lib = $userLocation . '/' . $DEPENDENCY_PATH . '/' . $path . '/_package.json';
                $path_dir = $userLocation . '/' . $SOURCE_DIRECTORY . '/' . $package_name . '/_package.json';
            }
            // Change project path project found to be in lib direcotory.
            if (file_exists($path_lib)) {
                $packageDetails = json_decode(file_get_contents($path_lib), true);
            } else if (file_exists($path_dir)) {
                $packageDetails = json_decode(file_get_contents($path_dir), true);
            } else if (isset($packagePath) && file_exists($packagePath . '/_package.json')) {
                $packageDetails = json_decode(file_get_contents($packagePath . '/_package.json'), true);
            } else {
                throw new \Exception('{ "status_code":"500", "message":"Package ' . $packageSlug . ' is Invalid. File _package.json not found!"}');
            }
            if (isset($packageDetails['thumbnail_image']) && !empty($packageDetails['thumbnail_image'])) {
                $path = $this->get_mediaPath($accountSlug, $package_name, $packageDetails['thumbnail_image']);
                $packageDetails['thumbnail_image'] = $this->get_url($path);
            }
            if (isset($packageDetails['grid_layout_image']) && !empty($packageDetails['grid_layout_image'])) {
                $path = $this->get_mediaPath($accountSlug, $package_name, $packageDetails['grid_layout_image']);
                $packageDetails['grid_layout_image'] = $this->get_url($path);
            }
            if (isset($packageDetails['block_layout_image']) && !empty($packageDetails['block_layout_image'])) {
                $path = $this->get_mediaPath($accountSlug, $package_name, $packageDetails['block_layout_image']);
                $packageDetails['block_layout_image'] = $this->get_url($path);
            }
            if (isset($packageDetails['details_image']) && !empty($packageDetails['details_image'])) {
                $path = $this->get_mediaPath($accountSlug, $package_name, $packageDetails['details_image']);
                $packageDetails['details_image'] = $this->get_url($path);
            }
            if ($loginStatus == "offline") {
                $packageowner = explode('/', $packageDetails['name']);
                $packageDetails['name'] = $DEFAULT_USER . '/' . $packageowner[1];
            }

            return $packageDetails;
        }
    }

    /**
     * Indents a flat JSON string to make it more human-readable.
     *
     * @param  string $json The original JSON string to process.
     * @return string Indented version of the original JSON string.
     */
    function _indent($json) {
        $result = '';
        $pos = 0;
        $strLen = strlen($json);
        $indentStr = ' ';
        $newLine = "\n";
        $prevChar = '';
        $outOfQuotes = true;

        for ($i = 0; $i <= $strLen; $i++) {
            // Grab the next character in the string.
            $char = substr($json, $i, 1);
            // Are we inside a quoted string?
            if ($char == '"' && $prevChar != '\\') {
                $outOfQuotes = !$outOfQuotes;
                // If this character is the end of an element,
                // output a new line and indent the next line.
            } else if (($char == '}' || $char == ']') && $outOfQuotes) {
                $result .= $newLine;
                $pos --;
                for ($j = 0; $j < $pos; $j++) {
                    $result .= $indentStr;
                }
            }
            // Add the character to the result string.
            $result .= $char;
            // If the last character was the beginning of an element,
            // output a new line and indent the next line.
            if (($char == ',' || $char == '{' || $char == '[') && $outOfQuotes) {
                $result .= $newLine;
                if ($char == '{' || $char == '[') {
                    $pos ++;
                }
                for ($j = 0; $j < $pos; $j++) {
                    $result .= $indentStr;
                }
            }
            $prevChar = $char;
        }

        return $result;
    }

    /**
     * Case-insensitive in_array() wrapper.
     *
     * @param mixed $needle   Value to seek.
     * @param array $haystack Array to seek in.
     *
     * @return bool
     */
    function _in_arrayi($needle, $haystack) {
        return in_array(strtolower($needle), array_map('strtolower', $haystack));
    }

    /**
     * Get invalid file/folder path.
     *
     * @param string $path is absolute file/folder path.
     *
     * @return file/folder path
     */
    function getInvalidpaths($path) {
        $accountSlug = $this->getCurrentAccount();
        $path = str_replace("\\", "/", $path);
        $path = explode($accountSlug, $path);
        unset($path[0]);
        $path = implode('', $path);
        $path = explode('/', $path);
        unset($path[0]);
        unset($path[1]);
        unset($path[2]);
        $path = implode('/', $path);

        return $path;
    }
    
    function get_mime_types() {
        return array(
            // Image formats.
            'jpg|jpeg' => 'image/jpeg',
            'png' => 'image/png',
            'svg' => 'image/svg+xml',
            // Text formats.
            'txt|hash|part|bd' => 'text/plain',
            'css' => 'text/css',
            'htm|html' => 'text/html',
            // Misc application formats.
            'js' => 'application/javascript',
            'json' => 'application/json',
            
        );
    }
    
    function check_filetype($filename) {
        $mimes = $this->get_mime_types();

        $type = false;
        $ext = false;

        foreach ($mimes as $ext_preg => $mime_match) {
            $ext_preg = '!\.(' . $ext_preg . ')$!i';
            if (preg_match($ext_preg, $filename, $ext_matches)) {
                $type = $mime_match;
                $ext = $ext_matches[1];
                break;
            }
        }

        return compact('ext', 'type');
    }
    
    function validate_file($file, $filename) {
        $filetype = $this->check_filetype($filename);
        $ext = $filetype['ext'];
        $type = $filetype['type'];

        if (!file_exists($file)) {
            return compact('ext', 'type');
        }
        $real_mime = false;

        if ($type && !$real_mime && extension_loaded('fileinfo')) {
            $finfo = finfo_open(FILEINFO_MIME_TYPE);
            $real_mime = finfo_file($finfo, $file);
            finfo_close($finfo);
        }

        // fileinfo often misidentifies obscure files as one of these types
        $nonspecific_types = array(
            'application/octet-stream',
            'application/encrypted',
            'application/CDFV2-encrypted',
            'application/zip',
        );

        if (in_array($real_mime, $nonspecific_types, true)) {
            if (!in_array(substr($type, 0, strcspn($type, '/')), array('application', 'video', 'audio'))) {
                $type = $ext = false;
            }
    } elseif ('text/plain' === $real_mime || "inode/x-empty" === $real_mime || "text/x-asm" === $real_mime || 'text/html' === $real_mime) {
            // A few common file types are occasionally detected as text/plain; allow those.
            if (!in_array(
                            $type, array(
                        'text/plain',
                        'text/css',
                        'text/html',
                        'application/json',
                        'application/javascript',
                            )
                    )
            ) {
                $type = $ext = false;
            }
        } else {
            if ($type !== $real_mime) {
                /*
                 * Everything else including image/* and application/*:
                 * If the real content type doesn't match the file extension, assume it's dangerous.
                 */
                $type = $ext = false;
            }
        }
        // The mime type must be allowed
	if ( $type ) {
		$allowed = $this->get_mime_types();

		if ( ! in_array( $type, $allowed ) ) {
			$type = $ext = false;
		}
	}
        
        return compact( 'ext', 'type');
    }

}

/**
 * Build Intermediary format of package.
 *
 * @param  string $location is the package name.
 * @param  object[optional] $package is the package name.
 * @param  string $hash[optional] is the md5 package hash.
 * @return object Intermediary representation of a package.
 */
class IntermediaryNode {

    /**
     * What the package Hash.
     *
     * @access private
     * @var    string
     */
    private $hash;

    /**
     * What the package data.
     *
     * @access public
     * @var    object
     */
    public $data;

    /**
     * What the package location.
     *
     * @access public
     * @var    object
     */
    public $location;
    
    /**
     * Flag to check, whether to create exception on invalid media files
     * @var boolean
     */
    public $mediaError = false;

    /**
     * Retrieve stored package details of a pakcage.
     *
     * @param string $location of package.
     * @param string $hash[optional] is the md5 package hash.
     */
    function __construct($location, $package = null, $hash = null) {
        $this->data = $package;
        $this->location = $location;
        $this->hash = $hash;
    }

    /**
     * Retrieve file data as object before the elements are added.
     *
     * @access public
     * @param  file path or object $file_object Used to append data.
     */
    function getData($file_object) {
        if (!is_object($file_object) && file_exists($file_object)) {
            return file_get_contents($file_object);
        } else {
            return $file_object;
        }
    }

    /**
     * Retrieve Pakcage Hash.
     *
     * @access public
     * @param  fully qualified package name $packageSlug Used to append directory Hash.
     * @return string package hash.
     */
    function getHash($packageSlug) {
        if (file_exists($packageSlug . '/.hash')) {
            $package = file_get_contents($packageSlug . '/.hash');
            $packageHash = json_decode($package, true);
            if (isset($packageHash['packageHash']) && !empty($packageHash['packageHash'])) {
                return $packageHash['packageHash'];
            }
        }
    }

    /**
     * Retrieve package Intermediary representation.
     *
     * @access public
     * @param  string $dir as path to the package directory.
     * @return object package Intermediary representation.
     */
    function get_package($dir) {
        /**
         * Allowed image extensions.
         *
         * @access global
         * @var    array
         */
        global $ALLOWED_IMAGE_TYPES;

        /**
         * Allowed file extensions.
         */
        global $ALLOWED_FILE_TYPES;

        /**
         * Invalid characters
         *
         * @access global
         * @var    array
         */
        global $invalidCharacters;

        /**
         * theme directory path
         *
         * @var string
         */
        global $ROOT_DIRECTORY;

        $dir = new \DirectoryIterator($dir);
        $packageIntermediary = array();
        $backend = new Backend();
        $invalidMediaFiles = array();

        // Iterating local theme directory to form Intermediary theme object.
        foreach ($dir as $key => $node) {
            if ($node->isDir() && !$node->isDot() && substr($node->getFilename(), 0, 1) !== "_") {
                $fileName = $node->getFilename();
                if (true === $this->mediaError && array_filter($invalidCharacters, function($value) use($fileName) {
                            if (strpos($fileName, $value) !== FALSE)
                                return true;
                        })) {
                    $path = $backend->getInvalidpaths($node->getpath());
                    $invalidMediaFiles[$path . '/' . $fileName] = "Please enter a valid name. (White-spaces and special characters are not allowed except - and _).";
                }
                $isDirectoryEmpty = new DirectoryNode($node->getPathname(), null, null, $this->mediaError);
                $packageIntermediary[$node->getFilename()] = $isDirectoryEmpty;
            } else if ($node->isFile() && $node->getFilename() == "_index.json" || $node->isFile() && substr($node->getFilename(), 0, 1) !== "_" && substr($node->getFilename(), -1) !== "~" && substr($node->getFilename(), -4) !== ".swp" || $node->isFile() && $node->getFilename() == "_package.json") {
                $fileName = $node->getFilename();
                // Getting extension of a file.
                $fileExtension = explode('.', $fileName);
                $fileExtension = $fileExtension[count($fileExtension) - 1];
                $path = $backend->getInvalidpaths($node->getpath());
                if (true === $this->mediaError) {
                    //Invalid filename
                    if (array_filter($invalidCharacters, function($value) use($fileName) {
                                if (strpos($fileName, $value) !== FALSE)
                                    return true;
                            })) {
                        $invalidMediaFiles[$path . '/' . $fileName] = "Please enter a valid name. (White-spaces and special characters are not allowed except - and _).";
                    }

                    $validate = $backend->validate_file($node->getpath() . "/" . $fileName, $fileName);
                    if (!in_array(strtolower($fileExtension), array_merge($ALLOWED_IMAGE_TYPES, $ALLOWED_FILE_TYPES))) {
                        $invalidMediaFiles[$path . '/' . $fileName] = "Please enter a valid extension.(The allowed file extensions are .html, .css, .bd, .js, .json, .svg, .txt, .jpg, .jpeg, .png)";
                    } else if (false === $validate['type'] && !in_array(strtolower($fileExtension), $ALLOWED_IMAGE_TYPES)) {
                        $invalidMediaFiles[$path . '/' . $fileName] = "Upload a valid file. The file you uploaded was either not an file or a corrupted file.";
                    }
                }


                // Checking json or media type file else load content for all other.
                if (substr($fileName, -5) == ".part") {
                    $fileLocation = $node->getpath() . "/" .$fileName;
                    $fileContent = new PartFile($fileLocation);
                    $packageIntermediary[$fileName] = $fileContent;
                } elseif (substr($fileName, -5) == ".json") {
                    $fileLocation = $node->getpath() . "/" . $fileName;
                    $fileContent = new JsonFile($fileLocation);
                    $packageIntermediary[$fileName] = $fileContent;
                } elseif (substr($fileName, -5) == ".hash" && $fileName != ".hash") {
                    $fileLocation = $node->getpath() . "/" . $fileName;
                    $fileContent = new HashFile($fileLocation);
                    $packageIntermediary[$fileName] = $fileContent;
                } else if (in_array(strtolower($fileExtension), $ALLOWED_IMAGE_TYPES)) {
                    // If file name matches with any of the allowed binary extension then store "<theme_name>/path/to/media"   
                    $fileLocation = $node->getpath() . "/" . $fileName;
                    //Check if image size exceeded or a corrupt image
                    if (true === $this->mediaError) {
                        if (false === $backend->get_media($fileLocation)) {
                            $invalidMediaFiles[$path . '/' . $fileName] = "Upload a valid image. The file you uploaded was either not an image or a corrupted image.";
                        } elseif (5000000 < filesize($fileLocation)) {
                            $invalidMediaFiles[$path . '/' . $fileName] = "Media upload time expired. You can't upload media in size greater than 5MB.";
                        }
                    }

                    $packageIntermediary[$fileName] = new BinaryFile($fileLocation);
                } else if ($fileName != ".hash.txt" && $fileName != ".hash" && substr($fileName, -1) !== "~") {
                    // For all other type files, get file contents.
                    $packageIntermediary[$fileName] = new TextFile($node->getpath() . "/" . $fileName);
                }
            }
        }

        if (true === $this->mediaError && $invalidMediaFiles) {
            $backend->invalidFileDirectories($invalidMediaFiles);
        }

        return $packageIntermediary;
    }

}

/**
 * Build Intermediary format of package directories.
 *
 * @return object Intermediary representation of package directory.
 */
class DirectoryNode extends IntermediaryNode {

    /**
     * What the directory Hash.
     *
     * @access public
     * @var    string
     */
    public $hash;
    
    public function __get($property) {
        if (property_exists($this, $property)) {
            return $this->$property;
        }
    }

    public function __set($property, $value) {
        if (property_exists($this, $property)) {
            $this->$property = $value;
        }

        return $this;
    }

    /**
     * Retrieve Intermediary representation of package directory.
     *
     * @param string $dir is the subfolder of package directory.
     * @param object $package[optional].
     */
    function __construct($dir, $package = null, $deserialize = null, $mediaError = false) {
        if (!file_exists($dir) || $deserialize == "deserialize") {
            parent::__construct($dir, $package);
        } elseif ($deserialize != "deserialize") {
            $this->mediaError = $mediaError;
            $package = $this->get_package($dir);
            parent::__construct($dir, $package);
            $this->hash = $this->generateHash();
        }
    }

    /**
     * Calculate directory Hash.
     *
     * @access public
     * @return string package Hash.
     */
    function generateHash() {
        foreach ($this->data as $key => $value) {
            if($value instanceof PartFile || $value instanceof BinaryFile)
                continue;
            if ($key != ".blended" && $key != "blendedrc") {
                if($value instanceof HashFile)
                    $package_hash_list[] = $value->ref_file . ':' . $value->hash;
                else
                    $package_hash_list[] = basename($value->location) . ':' . $value->hash;
                
            }
        }
        if (isset($package_hash_list)) {
            sort($package_hash_list);
            $package_hash_string = implode('::', $package_hash_list);
            $package_hash = $package_hash_string;
            $this->hash = md5($package_hash);
        } else {
            $this->hash = md5("");
        }

        return $this->hash;
    }

}

/**
 * Build Intermediary format of package text files.
 *
 * @return object Intermediary representation of package files.
 */
class TextFile extends IntermediaryNode {

    /**
     * Retrieve Intermediary representation of package text files.
     *
     * @param string $file_location is the path to the package file.
     */
    function __construct($file_location) {
        $this->data = $this->getData($file_location);
        $this->location = basename($file_location);
        $this->hash = $this->generateHash();
    }

    /**
     * calculate file Hash.
     *
     * @access public
     * @return file hash.
     */
    function generateHash() {
        return md5($this->data);
    }

}

/**
 * Build Intermediary format of package Json files.
 *
 * @return object Intermediary representation of package Json files.
 */
class JsonFile extends IntermediaryNode {

    /**
     * Retrieve Intermediary representation of package Json files.
     *
     * @param string $file_location is the path to the package Json file.
     */
    function __construct($file_location) {
        $content = $this->getData($file_location);
        $content = json_decode($content, true);
        if (is_array($content))
            ksort($content);
        $content = json_encode($content, JSON_UNESCAPED_SLASHES);
        $this->data = $content;
        $this->location = basename($file_location);
        $this->hash = $this->generateHash();
    }

    /**
     * calculate Json file Hash.
     *
     * @access public
     * @return file hash.
     */
    function generateHash() {
        return md5($this->data);
    }

}

/**
 * Build Intermediary format of package media files.
 *
 * @return object Intermediary representation of package media files.
 */
class BinaryFile extends IntermediaryNode {

    /**
     * Retrieve Intermediary representation of package media files.
     *
     * @param string $file_location is the path to the package media file.
     */
    function __construct($file_location) {
        $file_path = $file_location;
        $this->hash = $this->generateHash($file_path);
        $this->location = $file_path;
    }

    /**
     * calculate media file Hash.
     *
     * @access public
     * @return file hash.
     */
    function generateHash($file_path) {
        if (file_exists($file_path)) {
            return sha1_file($file_path);
        }
        return $file_path;
    }

}

/**
 * Build Intermediary format of dot(.) files having .hash extension.
 *
 * @return object Intermediary representation of package dot files with .hash extension
 */
class HashFile extends IntermediaryNode {
    
    /**
     * Refers to the original file
     * @var string 
     */
    public $ref_file;
    
    /**
     * Refers to the original file URL
     * @var string
     */
    public $href;

    /**
     * Retrieve Intermediary representation of package dot hash files.
     *
     * @param string $file_location is the path to the package dot hash file.
     */
    public function __construct($file_location) {
        $content = $this->getData($file_location);
        $this->location = $file_location;
        $this->hash = $content;
        $content = json_encode($content, JSON_UNESCAPED_SLASHES);
        $this->data = $content;
        $this->set_vars();
        
    }
    
    /**
     *  Set properties of the class
     */    
    public function set_vars() {
        $backend = new Backend();
        $hashFilePath = str_replace("\\", "/", $this->location);
        $hashFile = $backend->getLastString($hashFilePath, "/");
        $refFile = rtrim(ltrim($hashFile, "."), ".hash");
        $partFile = "." . $refFile . ".part";
        $tempPartFilePath = explode($hashFile, $this->location);
        $partFilePath = $tempPartFilePath[0] . $partFile;
        $this->href = $this->getData($partFilePath);
        $this->ref_file =  $refFile;
    }

    /**
     * calculate dot hash file Hash.
     *
     * @access public
     * @return file hash.
     */
    public function generateHash() {
       return md5($this->data);
    }

}

/**
 * Build Intermediary format of dot(.) files having .part extension.
 *
 * @return object Intermediary representation of package dot files with .part extension
 */
class PartFile extends IntermediaryNode {

    /**
     * Retrieve Intermediary representation of package dot part files.
     *
     * @param string $file_location is the path to the package dot part file.
     */
    function __construct($file_location) {
        $file_path = $file_location;
        $this->hash = $this->generateHash($file_path);
        $this->location = $file_path;
        $content = $this->getData($file_path);
        $this->data = $content;
    }

    /**
     * calculate dot part file Hash.
     *
     * @access public
     * @return file hash.
     */
    function generateHash($file_path) {
        if (file_exists($file_path)) {
            return sha1_file($file_path);
        }
        return $file_path;
    }

}
