<?php

/**
 * Communicates Blended Filesystem and Hub for blended packages.
 * Also converts blended packages into required formats like 'jptf', 'context' and 'Intermediary' structure.
 *
 * @blended
 */

namespace Blended\hostlib;

// Backend and Network are used to Communicate with blended packages.
use Blended\hostlib\Network;
use Blended\hostlib\Backend;
use Blended\Orm\DB_Backend;
use Doctrine\Common\Util\Debug;

/**
 * Entry point for any request for transfering/sharing packages.
 * Communicate between Backend and Network.
 */
class Controller {

    var $backend;
    var $network;

    /*
     * ----------------------------------------------------------------------------------------------------------------------------------------
     * METHODS RELATED TO BUILDING PACKAGE OBJECT(LOADER)
     * ----------------------------------------------------------------------------------------------------------------------------------------
     */

    /**
     * Initialize and load Backend and Network.
     *
     * @param object $backend is the backend class object.
     * @param object $network is the network class object.
     */
    function __construct($backend, $network) {
        $this->backend = $backend;
        $this->network = $network;
    }

    /**
     * Retrieve Package object local file system or Hub.
     *
     * @param  string            $packageSlug is user account + package name.
     * @param  string[optional]  $format      are the available package formats.
     * @param  boolean[optional] $draft       is true if package is draft.
     * @param  string[optional]  $label       is the package details.
     * @return object package representation in either as Context, Intermediary or Jptf.
     */
    function get_package($packageSlug, $format = null, $draft = false, $label = null, $render = false, $mediaError = false) {
        $packageIntermediary = null;
        $backend = $this->backend;
        $packageName = $packageSlug;
        $slug = basename($packageSlug);
        $accountSlug = $backend->getCurrentAccount();
        if (strpos($packageSlug, '/')) {
            $ownerCheck = explode('/', $packageSlug);
            if ($ownerCheck[0] !== $accountSlug) {
                $accountSlug = $ownerCheck[0];
                $slug = $ownerCheck[1];
            }
        }
        if ($draft === true) {
            $label = 'draft';
        }
        $pathDir = $backend->package_exists($packageSlug, $label, $render);
        // Assigning package path to Backend constructor variable.
        $backend->ROOT_DIRECTORY = $pathDir;
        
        if(false === $pathDir && $format === 'context') {
            throw new \Exception('{"status_code":"500", "message":"Package does not exist"}');
        }
        // Retrieve package object if package exist in local file system else call network for package object.
        if ($pathDir != false) {
            if ($format === 'jptf') {
                $packageIntermediary = $backend->get_package($pathDir, $mediaError);
                $packageJptf = $this->as_jptf($packageIntermediary, $packageSlug, $label);
                if ($draft === false)
                    $packageJptf = $this->readOnly($packageJptf);
                return $packageJptf;
            } elseif ($format === 'context') {
                $packageIntermediary = $backend->get_package($pathDir);
                // Download dependencies to local file system.
                $this->download_dependencies($packageIntermediary->data, null, $slug, array(), array(), $render);
                // Retrieve recursive package dependencies.
                $dependencyDict = $this->get_package_dependencies($packageIntermediary->data, $slug, array(), $render);
                $packageContext = $this->as_context($packageIntermediary, $dependencyDict);
                return $packageContext;
            } else {
                $packageIntermediary = $backend->get_package($pathDir, $mediaError);
                $this->download_dependencies($packageIntermediary->data, null, $slug, array(), array(), false);
                // Retrieve recursive package dependencies.
                $dependencyDict = $this->get_package_dependencies($packageIntermediary->data, $slug, array(), false);
                return $packageIntermediary;
            }
        } elseif ($label === 'canonical') {
            try {
                $network = $this->network;
                $packageJptf = $network->downloadCanonical($accountSlug, $slug);
            } catch (\Exception $e) {
                $packageJptf = $this->pull_package($slug, null, true);
            }
            if (!empty($packageJptf)) {
                $packageIntermediary = $this->deserialize_jptf($packageJptf);
                $this->save_local($accountSlug . '/' . $slug, $packageIntermediary, $label, false);
            }
        } elseif (!isset($packageIntermediary)) {
            try {
                // Retrieve package object from network.
                $network = $this->network;
                if ($draft === true) {
                    $trim = true;
                    $packageJptf = $network->downloadDraft($accountSlug, $slug);
                } elseif (isset($label)) {
                    $trim = false;
                    $packageJptf = $network->download($accountSlug, $slug, $label);
                }
                $packageJptf = json_encode($packageJptf);
                $packageJptf = json_decode($packageJptf, true);
                $canonicalVersion = $packageJptf[0]['/']['canonical'];
                if (isset($canonicalVersion) && !is_null($canonicalVersion)) {
                    $this->updateCanonical_txt($accountSlug . '/' . $slug, $canonicalVersion);
                }
                $packageIntermediary = $this->deserialize_jptf($packageJptf);
                $url = $this->save_local($packageName, $packageIntermediary, $label, $trim);
            } catch (\Exception $e) {
                throw $e;
            }
        }

        return $packageIntermediary;
    }

    /**
     * Transforming Intermediary package object to context representation.
     *
     * @param  object $package        is the package object.
     * @param  array  $dependencyDict is the package dependency stack.
     * @return array package Context representation.
     */
    function as_context($packageIntermediary, $dependencyDict) {
        $currentNode = null;
        $currentjsonFile = null;
        $dependencyDict = $this->_string_to_obj($dependencyDict);
        $packageObject = $this->_string_to_obj($packageIntermediary->data);
        try {
            // $ref resolution takes place here.
            $this->resolve($currentNode, $packageObject, $dependencyDict, $currentjsonFile);
            $packageContext = $this->_trim_context($packageObject);
            $packageContext = $this->_index_shortcuts($packageContext);
        } catch (\Exception $e) {
            throw new \Exception('Blended Error while converting to Context');
        }

        return $packageContext;
    }

    /**
     * The resolve function uses a few helper functions (get_value_at_path, ref_get, resolve_pointer) in order to
     * resolve all Json Pointers used in the json files of the packageObject.
     *
     * @param object reference $currentNode     is the current pointer.
     * @param object reference $packageObject   is the package object.
     * @param object reference $dependencyDict  is the package dependency stack object.
     * @param object reference $currentjsonFile is the current file whose refrences are to be resolved.
     * @param array            $refPath         is a list that provides the path till the ref (list of keys that lead to the ref)
     */
    function resolve(&$currentNode, &$packageObject, &$dependencyDict, &$currentjsonFile, $refPath = null) {
        if (is_null($currentNode)) {
            $currentNode = &$packageObject;
        }
        if (is_null($refPath)) {
            $refPath = [];
        }

        foreach ($currentNode as $key => $value) {
            // Keep track of json files so that other objects can look here for refrence values.
            array_push($refPath, $key);
            if (substr($key, -5) === ".json") {
                $currentjsonFile = &$currentNode[$key];
            }
            $currentValue = &$currentNode[$key];
            // Look for the key '$ref' to resolve its pointing value.
            if (is_array($currentValue)) {
                if (array_key_exists('$ref', $currentValue)) {
                    $setVal = array();
                    $refPath_copy = $refPath;
                    $reflocationPtr = &$currentNode;
                    try {
                        $currentNode[$key] = $this->resolve_pointer(
                                $currentValue, $packageObject, $dependencyDict, $setVal, $currentjsonFile, $refPath_copy, $reflocationPtr, $key, null
                        );
                    } catch (\Exception $e) {
                        $currentNode[$key] = 'Caught exception: ' . $e->getMessage();
                    }
                } else {
                    if (is_null($currentValue)) {
                        continue;
                    }
                    $this->resolve($currentValue, $packageObject, $dependencyDict, $currentjsonFile, $refPath);
                }
            }
            array_pop($refPath);
        }
    }

    /**
     * This function receives the dict containing the ref, checks if the ref has already been encountered or not by use of the visited_set.
     * It then simply forwards the ref address for further processing
     * The set is used to determine if a cycle is being entered.
     *
     * @param  array reference $jsonRef         is the current pointer.
     * @param  array reference $packageObject   is the package object.
     * @param  array reference $dependencyDict  is the package dependency stack object.
     * @param  array           $visitedSet      is the list of refrences that has been resolved and help to track looping in resolve.
     * @param  array reference $currentjsonFile is the current file whose refrences are to be resolved.
     * @param  array           $refPath         is a list that provides the path till the ref (list of keys that lead to the ref)
     * @param  array           $refLocation     is the array/dict that contains the ref as the value of some key
     * @param  string          $reflocKey       is the key in the array/dict whose value is the ref
     * @param  string          $currAlias       is the type of ref : '#', '@' or '/' which led to the curr ref or null if this ref is found independently.
     * @return array|string as resolved reference value.
     */
    function resolve_pointer(&$jsonRef, &$packageObject, &$dependencyDict, $visitedSet, &$currentjsonFile, $refPath, &$refLocation, $reflocKey, $currAlias) {
        if (is_array($jsonRef) && array_key_exists('$ref', $jsonRef)) {
            $pointer = $jsonRef['$ref'];
            if (in_array($pointer, $visitedSet)) {
                // Entered into cycle.
                throw new \Exception('A Json Pointer cycle has been detected');
            } else {
                // Push current item to stack for backtrakking cycles in path.
                array_push($visitedSet, $pointer);
                $jsonRef = $this->_ref_get(
                        $pointer, $packageObject, $currentjsonFile, $dependencyDict, $visitedSet, $refPath, $refLocation, $reflocKey, $currAlias
                );
                if (is_null($jsonRef)) {
                    throw new \Exception('A null value has been returned');
                }
            }
        }
        if (is_null($jsonRef)) {
            throw new \Exception('A null value has been returned');
        }

        return $jsonRef;
    }

    /**
     * Get Package recursive dependency dictinory representation without resolving refrences.
     *
     * @param  object          $packageIntermediary package object.
     * @param  array           $dependentPackage    is a parent packageSlug.
     * @param  array[optional] $dependentPackage    is a dependency tree.
     * @return array $dependencyDict.
     */
    function get_package_dependencies($packageIntermediary, $dependentPackage, $dependencyDict = array(), $render) {
        if (isset($packageIntermediary['_package.json']->data)) {
            $packageInfo = $packageIntermediary['_package.json']->data;
            $packageInfo = json_decode($packageInfo, true);
        }
        if (isset($packageInfo['dependencies']) && !empty($packageInfo['dependencies']) && isset($packageInfo['name'])) {
            $backend = $this->backend;

            foreach ($packageInfo['dependencies'] as $key => $value) {
                $dep_slug = $value['name'];
                $dep_version = $value['version'];
                $dep_alias = $value['alias'];
                $package_name = $dep_slug;
                $mainpackageSlug = $backend->getActiveTheme($render);
                $accountName = explode('/', $mainpackageSlug);
                $mainAccount = $accountName[0];
                $packageStatus = $backend->package_exists($package_name, $dep_version, $render, $mainAccount);
                if ($packageStatus != false) {
                    $packagePath = $packageStatus;
                    $dependencyDict[$dep_alias] = $backend->get_package($packagePath);
                    $dependencyDict = $this->get_package_dependencies($dependencyDict[$dep_alias]->data, $dependentPackage, $dependencyDict, $render);
                } else if ($render == false) {
                    $mainpackageSlug = $backend->getActiveTheme($render);
                    $accountName = explode('/', $mainpackageSlug);
                    $mainAccount = $accountName[0];
                    $packageStatus = $backend->package_exists($package_name, $dep_version, $render, $mainAccount);
                    $packagePath = $packageStatus;
                    $dependencyDict[$dep_alias] = $backend->get_package($packagePath);
                    $dependencyDict = $this->get_package_dependencies($dependencyDict[$dep_alias]->data, $dependentPackage, $dependencyDict, $render);
                }
            }
        }

        return $dependencyDict;
    }

    /**
     * store package dependencies of a package locally.
     *
     * @param           object $packageIntermediary object of Intermediary type.
     * @param(optional) string $pullType for weak pull.
     * @param(optional) string $dependendentPackage is the parent package.
     * @param(optional) array $dependencyStack is the list of packages in hierarchy of dependencies.
     * @param(optional) array $aliasStack is a list of alias name in hierarchy.
     * @return          array dependencyStack is a list of dependency tree.
     */
    function download_dependencies($packageIntermediary, $pullType = null, $dependendentPackage = null, $dependencyStack = array(), $aliasStack = array(), $render = false) {
        if (isset($packageIntermediary['_package.json']) && !empty($packageIntermediary['_package.json'])) {
            $packageInfo = $packageIntermediary['_package.json']->data;
            $packageInfo = json_decode($packageInfo, true);
            if (empty($packageInfo)) {
                throw new \Exception('{"status_code":"500", "message":"Package ' . $dependendentPackage . ' has invalid Package.json! "}');
            }
            if (isset($packageInfo['dependencies']) && !empty($packageInfo['dependencies']) && isset($packageInfo['name'])) {
                $dependent_package = basename($packageInfo['name']);
                $dependency_not_found = array();
                $not_found_counter = 0;
                foreach ($packageInfo['dependencies'] as $key => $value) {
                    try {
                        if (!isset($value['name']) || empty($value['name'])) {
                            throw new \Exception('{"status_code":"500", "message":"Package ' . basename($dependendentPackage) . ' field name not defined with dependencies."}');
                        }
                        if (!isset($value['version']) || empty($value['version'])) {
                            throw new \Exception('{"status_code":"500", "message":"Package ' . basename($dependendentPackage) . ' field version not defined with dependencies."}');
                        }
                        if (empty($value['alias']) || !isset($value['alias'])) {
                            throw new \Exception('{"status_code":"500", "message":"Package ' . basename($dependendentPackage) . ' field alias not defined with dependencies."}');
                        }
                        $dep_slug = $value['name'];
                        $package_account = explode('/', $dep_slug);
                        $package_account = $package_account[0];
                        $dep_version = $value['version'];
                        $dep_alias = $value['alias'];
                        $package_name = $dep_slug;
                        // Call controller get_package to look for dependencies and return package object.
                        if (in_array($packageInfo['name'] . ':' . $dep_slug . ':' . $dep_version, $dependencyStack) || $packageInfo['name'] === $dep_slug) {
                            throw new \Exception('{"status_code":"500", "message":"Package ' . basename($package_name) . ' with version ' . $dep_version . ' from account ' . $package_account . ' has multiple dependencies of same package."}');
                            break;
                        } else {
                            array_push($dependencyStack, $packageInfo['name'] . ':' . $dep_slug . ':' . $dep_version);
                            $backend = $this->backend;
                            $mainpackageSlug = $backend->getActiveTheme($render);
                            $accountName = explode('/', $mainpackageSlug);
                            $mainAccount = $accountName[0];
                            $packageStatus = $backend->package_exists($package_name, $dep_version, $render, $mainAccount);
                            if ($packageStatus != false) {
                                $packagePath = $packageStatus;
                                $packageIntermediary = $backend->get_package($packagePath);
                            } else {
                                try {
                                    $packageIntermediary = $this->download_network_package($package_name, $dep_version, $pullType, $dependencyStack, $dependent_package);
                                } catch (\Exception $e) {
                                    $not_found = json_decode($e->getMessage(), true);
                                    $dependency_not_found[] = $not_found["data"][$not_found_counter];
                                    $not_found_counter++;
                                    continue;
                                }
                            }
                            if (in_array($value['alias'], $aliasStack) && $render == false) {
                                throw new \Exception('{"status_code":"500", "message":"Field alias : ' . $dep_alias . ' duplicated with package ' . basename($dep_slug) . ' version ' . $dep_version . ' from account ' . $package_account . '."}');
                                break;
                            } elseif ($render == false) {
                                array_push($aliasStack, $value['alias']);
                            }
                            $dependencyStack = $this->download_dependencies($packageIntermediary->data, $pullType, $dep_slug, $dependencyStack, $aliasStack, $render);
                        }
                    } catch (\Exception $e) {
                        throw $e;
                    }
                    $not_found_counter++;
                }
                /**
                 * @throws Exception Not found dependencies.
                 */
                if ($dependency_not_found) {
                    throw new \Exception(\json_encode(array("status_code" => "500", "message" => "Package  dependency does not exist.", "data" => $dependency_not_found)));
                }
            }
        } else {
            throw new \Exception('{"status_code":"500", "message":"Package ' . $dependendentPackage . ' is Invalid! "}');
        }

        return $dependencyStack;
    }

    /**
     * Download package dependencies from Hub if not stored in local filesystem.
     *
     * @param string           $packageSlug      is the user account + package name.
     * @param string           $label            is the package version.
     * @param string[optional] $pullType         is the package download way from Hub.
     * @param array[optional]  $dependencyStack  is the package dependency stack.
     * @param string[optional] $dependentPackage is the source package name.
     */
    function download_network_package($packageSlug, $label, $pullType = null, $dependencyStack = null, $dependentPackage = null) {
        $network = $this->network;
        if (strpos($packageSlug, '/')) {
            $ownerCheck = explode('/', $packageSlug);
            $accountSlug = $ownerCheck[0];
            $slug = $ownerCheck[1];
        } else {
            throw new \Exception('AccountSlug or PackageSlug not defined!');
        }

        try {
            if ($label === 'draft') {
                $packageJptf = $network->downloadDraft($accountSlug, $slug);
                $packageJptf = json_encode($packageJptf);
                $packageJptf = json_decode($packageJptf, true);
                $canonicalVersion = $packageJptf[0]['/']['canonical'];
                if (isset($canonicalVersion) && !is_null($canonicalVersion)) {
                    $this->updateCanonical_txt($packageSlug, $canonicalVersion);
                }
                $packageIntermediary = $this->deserialize_jptf($packageJptf);
                $this->save_local($accountSlug . '/' . $slug, $packageIntermediary, $label, true);
                return $packageIntermediary;
            } elseif ($label === 'canonical') {
                $packageJptf = $network->downloadCanonical($accountSlug, $slug);
                $packageIntermediary = $this->deserialize_jptf($packageJptf);
                $this->save_local($accountSlug . '/' . $slug, $packageIntermediary, $label, false);
                return $packageIntermediary;
            } else {
                $packageJptf = $network->download($accountSlug, $slug, $label);
                $packageJptf = json_encode($packageJptf);
                $packageJptf = json_decode($packageJptf, true);
                $canonicalVersion = $packageJptf[0]['/']['canonical'];
                if (isset($canonicalVersion) && !is_null($canonicalVersion)) {
                    $this->updateCanonical_txt($accountSlug . '/' . $slug, $canonicalVersion);
                }
                $packageIntermediary = $this->deserialize_jptf($packageJptf);
                $this->save_local($accountSlug . '/' . $slug, $packageIntermediary, $label, false);
                return $packageIntermediary;
            }
        } catch (\Exception $e) {
            $dependency = basename($packageSlug);
            $dependencyStack = json_encode($dependencyStack);

            throw new \Exception('{"status_code":"500", "message":"Package ' . $dependentPackage . ' dependency for ' . $dependency . ' with version ' . $label . ' does not exist.", "data" :  ' . $dependencyStack . '  }');
        }
    }

    /**
     * Converts Intermediary package object to jptf representation.
     *
     * @param  object $packageIntermediary object of Intermediary type.
     * @return array package jptf representation.
     */
    function as_jptf($packageIntermediary, $packageSlug, $label = null) {
        $jptfObject = array();
        $dataIntermediary = $packageIntermediary->data;
        $jptfObject = $this->_as_jptf_builder($dataIntermediary, $packageSlug, $label);
        $jptfObject = $this->_JptfList($jptfObject);
        $packageJptf[0]['/']['hash'] = $packageIntermediary->hash;
        $packageJptf[0]['/']['type'] = 'directory';
        $packageJptf[0]['/']['content'] = $jptfObject;

        return $packageJptf;
    }

    /**
     * Converts Intermediary package object to hash only jptf representation.
     *
     * @param  object $packageIntermediary object of Intermediary type.
     * @return array package jptf representation.
     */
    function as_jptf_hashOnly($packageIntermediary) {
        $jptfObject = array();
        $dataIntermediary = $packageIntermediary->data;
        $jptfObject = $this->_as_jptf_builder_hashOnly($dataIntermediary);
        $jptfObject = $this->_JptfList($jptfObject);
        $packageJptf[0]['/']['hash'] = $packageIntermediary->hash;
        $packageJptf[0]['/']['type'] = 'directory';
        $packageJptf[0]['/']['content'] = $jptfObject;

        return $packageJptf;
    }

    /**
     * Deserialize jptf package to Intermediary representation.
     *
     * @param  array   $packageJptf is the package jptf representation.
     * @param  boolean $content     decides to have package object as Hash only with no content.
     * @return object Intermediary package object representation.
     */
    function deserialize_jptf($packageJptf, $content = true) {
        $packageHash = $packageJptf[0]['/']['hash'];
        $packageJptf = $packageJptf[0]['/']['content'];
        if ($content) {
            $packageIntermediary = $this->_blended_deserialize_jptf($packageJptf);
        } else {
            $packageIntermediary = $this->_blended_deserialize_jptf_nocontent($packageJptf);
        }
        $packageIntermediary = $this->getDirectoryObject('', $packageIntermediary);
        $packageIntermediary->hash = $packageHash;
        $packageIntermediary->location = '/';

        return $packageIntermediary;
    }

    /*
     * ----------------------------------------------------------------------------------------------------------------------------------------
     * METHODS RELATED TO MISCELLANEOUS OPERATIONS LIKE CLONE, PACKAGE COMPARE, FILE COMPARE, UPDATE PACKAGE.
     * ----------------------------------------------------------------------------------------------------------------------------------------
     */

    /**
     * Get package details either from Hub or local filesystem.
     *
     * @param  string $accountSlug is the current account slug.
     * @param  string $slug        is the package name.
     * @param  string $loginStatus is the sessionkey of the user if logged-in to Hub or string "offline".
     * @return array package Details.
     */
    function localPackageGet($accountSlug, $slug, $loginStatus) {
        $backend = $this->backend;
        $packageSlug = $accountSlug . '/' . $slug;
        $path = $backend->package_exists($packageSlug);
        try {
            $packageDetails = $backend->localPackageGet($path);
            $currentUser = $this->getCurrentAccount();
            if ($packageDetails != false && $currentUser === $accountSlug) {
                return $packageDetails;
            } else {
                $network = $this->network;
                $packageDetails = $network->getPackageDetails($accountSlug, $slug, true);
                #$packageJptf = $this->pull_package($packageSlug, null, true, true);
                #$packageDetails = $backend->localPackageGet($packageJptf[0]['/']['content']);
            }
        } catch (\Exception $e) {
            throw $e;
        }

        return $packageDetails;
    }

    /**
     * Create package clone and push to Hub.
     *
     * @param  string $accountSlug is the current account slug.
     * @param  string $slug        is the package name.
     * @param  string $session     is the Hub sessionkey.
     * @return array package properties.
     */
    function createHubPackage($accountSlug, $slug, $session) {
        try {
            $backend = $this->backend;
            $properties = $this->createatHub($accountSlug, $slug);
            if (isset($properties['package_hash']) && !empty($properties['package_hash'])) {
                $this->save_packageHash($accountSlug . '/' . $slug, $properties['package_hash']);
            }
            if (isset($properties['tokens']) && !empty($properties['tokens'])) {
                $result = $this->uploadMedia($accountSlug, $slug, $properties['tokens']);
                if (isset($result['data'])) {
                    $error = json_encode($result);
                    throw new \Exception($error);
                }
                $this->save_packageHash($accountSlug . '/' . $slug, $result['packageHash']['package_hash']);
            }
        } catch (\Exception $e) {
            throw $e;
        }

        return $properties;
    }

    /**
     * Delete package from local filesystem.
     *
     * @param string $accountSlug is the current account slug.
     * @param string $slug        is the package name.
     */
    function localDelete($accountSlug, $slug, $file = null, $deleteDirectory = false) {
        try {
            $backend = $this->backend;
            $packageStatus = $backend->package_exists($accountSlug . '/' . $slug);
            if ($packageStatus === false) {
                throw new \Exception('{"status_code":"500", "message":"Package ' . $slug . ' does not exist."}');
            }
            $backend->localDelete($packageStatus, $file, $deleteDirectory);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Get package JPTF from filesystem or Hub.
     *
     * @param string $accountSlug is the current account slug.
     * @param string $slug        is the package name.
     *
     * @return package.
     */
    function localDownload($accountSlug, $slug, $asHash = false, $loginStatus = null) {
        $backend = $this->backend;
        $packageSlug = $accountSlug . '/' . $slug;
        try {
            $Path = $backend->package_exists($packageSlug);
            if ($asHash == true && false == $Path) {
                throw new \Exception('{"status_code":"500", "message":"Package ' . $slug . ' does not exist."}');
            }
            if (false != $Path && !$backend->is_validPackage($Path)) {
                $this->pull_package($packageSlug, null, true, true);
                $packageJptf = $this->get_package($packageSlug, "jptf", true);
            } else if ($Path != false) {
                $packageJptf = $this->get_package($accountSlug . '/' . $slug, 'jptf', true);
                $packageIntermediary = $backend->get_package($Path);
                $this->download_dependencies($packageIntermediary->data, null, $slug, array(), array(), true);
                // Retrieve recursive package dependencies.
                $dependencyDict = $this->get_package_dependencies($packageIntermediary->data, $slug, array(), false);
            } else {
                $this->pull_package($packageSlug, null, true, true);
                $packageJptf = $this->get_package($packageSlug, "jptf", true);
            }
            if ($asHash) {
                $packageIntermediary = $this->get_package($accountSlug . '/' . $slug, null, true);
                $packageJptf = $this->as_jptf_hashOnly($packageIntermediary);
            }
        } catch (\Exception $e) {
            throw $e;
        }
        return $packageJptf;
    }

    /**
     * Get file jptf package object.
     *
     * @param string $file_path   is the file location.
     * @param string $accountSlug is the user account name.
     * @param string $slug        is the package name.
     *
     * @return array file jptf representation.
     */
    function localDownloadFile($accountSlug, $slug, $filePath) {
        $backend = $this->backend;
        try {
            $packageSlug = $accountSlug . '/' . $slug;
            $package = $backend->package_exists($packageSlug);
            if ($package === false) {
                $this->localDownload($accountSlug, $slug);
            }
            //Process media
            $process_media = $backend->getPackageMedia($packageSlug, $filePath);
            $packageIntermediary = $this->get_package($packageSlug, null, true, null);
            $packageObject = $this->_string_to_obj_hash($packageIntermediary->data, $packageIntermediary->hash);
            $iterate_path = explode('/', $filePath);
            $fileData = $this->_find_fileData($iterate_path, $packageObject);
            if ($fileData['content'] === null) {
                throw new \Exception('{ "status_code": "500", "message" : "File ' . $filePath . ' not found in ' . $slug . '." }');
            }
            $localfileJptf = $this->createPartialJptf(
                    array(
                $filePath,
                    ), $packageIntermediary, $accountSlug . '/' . $slug
            );
            //$localfileJptf = $localfileJptf['0']['/']['content'];
        } catch (\Exception $e) {
            throw $e;
        }

        return $localfileJptf;
    }

    /**
     * Update local package.
     *
     * @param  string $accountSlug is the user account name.
     * @param  string $slug        is the package name.
     * @param  string $packageJptf is the file location.
     * @return array package update details
     */
    function localUpdate($accountSlug, $slug, $packageJptf, $packageHash = null, $newMediaFiles = array()) {
        try {
            $backend = $this->backend;
            $packageStatus = $backend->package_exists($accountSlug . '/' . $slug);
            if ($packageStatus === false) {
                throw new \Exception('{"status_code":"500", "message":"Package ' . $slug . ' does not exist."}');
            } else {
                $currentpackageHash = $this->_get_current_draft_packageHash($packageStatus);
                $currentHash = $backend->get_oldHash($packageStatus);
            }
            if ($currentpackageHash != $packageHash) {
                throw new \Exception('{"status_code":"500", "message":"Package ' . $slug . ' out of sync. Someone has made changes in this package. Please reload the page to sync last changes."}');
            }
            $label = 'draft';
            $sourcePackage = $backend->get_package($packageStatus);
            $localMediaFiles = array();
            $this->create_media_list_from_intermediary($sourcePackage->data, $localMediaFiles);
            try {
                $this->localDelete($accountSlug, $slug);
                $packageJptf[0]["/"]["content"] = $this->merge_new_media_in_jptf($packageJptf[0]["/"]["content"], $newMediaFiles, $localMediaFiles);
                $packageIntermediary = $this->deserialize_jptf($packageJptf);
                $backend->localUpdate($accountSlug . '/' . $slug, $packageIntermediary, $label, true);
                $packageStatus = $backend->package_exists($accountSlug . '/' . $slug);
                $backend->updatePackageHash($packageStatus, $currentHash);
                $packageIntermediary = $this->get_package($accountSlug . '/' . $slug, null, true);
            } catch (\Exception $e) {
                $this->save_local($accountSlug . '/' . $slug, $sourcePackage, $label, true);
                $packageStatus = $backend->package_exists($accountSlug . '/' . $slug);
                $backend->updatePackageHash($packageStatus, $currentHash);
                $packageIntermediary = $this->get_package($accountSlug . '/' . $slug, null, true);
                throw $e;
            }
            $packageHash = array('currentpackageHash' => $packageIntermediary->hash);
        } catch (\Exception $e) {
            throw $e;
        }

        return $packageHash;
    }

    /**
     * Update local package.
     *
     * @param  string $accountSlug is the user account name.
     * @param  string $slug        is the package name.
     * @param  string $packageJptf is the file location.
     * @return array package update details
     */
    function localUpdatefiles($accountSlug, $slug, $packageJptf, $packageHash = null) {
        try {
            $backend = $this->backend;
            $network = $this->network;
            $packageStatus = $backend->package_exists($accountSlug . '/' . $slug);
            if ($packageStatus === false) {
                throw new \Exception('{"status_code":"500", "message":"Package ' . $slug . ' does not exist."}');
            } else {
                $currentpackageHash = $this->_get_current_draft_packageHash($packageStatus);
            }
            if ($currentpackageHash != $packageHash) {
                throw new \Exception('{"status_code":"500", "message":"Package ' . $slug . ' out of sync. Someone has made changes in this package. Please reload the page to sync last changes."}');
            }

            $resolvedJptf = $this->_resolveJPTF(array($packageJptf[0]['/']));
            $hubItems = isset($resolvedJptf['on_hub']) ? $resolvedJptf['on_hub'] : [];
            if (isset($resolvedJptf['toRename']) && !empty($resolvedJptf['toRename'])) {
                foreach ($resolvedJptf['toRename'] as $key => $value) {
                    $rename = explode('/$ref@', $value);
                    $rename[1] = ltrim($rename[1], '/');
                    $dst = $rename[0];
                    $src = '/' . $rename[1];
                    $backend->move_packages($src, $dst, $packageStatus, $packageStatus);
                }
            }
            if (isset($resolvedJptf['file']) && !empty($resolvedJptf['file'])) {
                foreach ($resolvedJptf['file'] as $key => $value) {
                    $fileName = $value['name'];
                    $filePath = ltrim($value['path'], '/');
                    $partialJptf[0]['/'] = array(
                        'hash' => '',
                        'content' => $value['content'],
                    );
                    $packageIntermediary = $this->deserialize_jptf($partialJptf);
                    if ($fileName === "_package.json") {
                        $packageData = json_decode($packageIntermediary->data['_package.json']->data, true);
                        $packageData['name'] = $accountSlug . '/' . $slug;
                        $packageData['user'] = $accountSlug;
                        $packageIntermediary->data['_package.json']->data = json_encode($packageData);
                    }
                    $backend->updateFiles($packageStatus, $filePath, $fileName, $packageIntermediary->data[$fileName]);
                }
            }
            if (isset($resolvedJptf['toDelete']) && !empty($resolvedJptf['toDelete'])) {
                foreach ($resolvedJptf['toDelete'] as $key => $value) {
                    $delete = ltrim($value, '/');
                    $this->localDeleteSync($accountSlug, $slug, $delete, $hubItems);
                }
            }
            if (isset($resolvedJptf['emptyDirectory']) && !empty($resolvedJptf['emptyDirectory'])) {
                foreach ($resolvedJptf['emptyDirectory'] as $key => $value) {
                    $emptyDirectory = ltrim($value, '/');
                    $this->handleEmptyDirectory($accountSlug . '/' . $slug, $emptyDirectory, $hubItems);
                }
            }
            $currentpackageHash = $this->_get_current_draft_packageHash($packageStatus);
            $packageJptf = $network->downloadDraft($accountSlug, $slug, "1");
            $packageJptf = json_encode($packageJptf);
            $packageJptf = json_decode($packageJptf, true);
            $hubHash = $packageJptf[0]['/']['hash'];
            if ($currentpackageHash === $hubHash) {
                $backend->save_packageHash($accountSlug . '/' . $slug, $currentpackageHash);
            }
        } catch (\Exception $e) {
            throw $e;
        }

        return $resolvedJptf;
    }
    
    /**
     * Delete target path with hub items check simultaneously
     * @param string $accountSlug
     * @param string $slug
     * @param string $deletePath
     * @param array $hubItems
     * @throws \Exception
     */
    function localDeleteSync($accountSlug, $slug, $deletePath, $hubItems = array()) {
        try {
            $backend = $this->backend;
            $packagePath = $backend->package_exists($accountSlug . '/' . $slug);
            if ($packagePath === false) {
                throw new \Exception('{"status_code":"500", "message":"Package ' . $slug . ' does not exist."}');
            }
            $backend->localDeleteSync($packagePath, $deletePath, $hubItems);
        } catch (\Exception $e) {
            throw $e;
        }
    }
    
    /**
     * Delete or add empty directory
     * @param string $packagePath
     * @param string $deletePath
     * @param array $hubItems
     * @throws \Exception
     */
    function handleEmptyDirectory($packagePath, $deletePath, $hubItems = array()) {
        try {
            $backend = $this->backend;
            $packagePath = $backend->package_exists($packagePath);
            if ($packagePath === false) {
                throw new \Exception('{"status_code":"500", "message":"Package ' . $slug . ' does not exist."}');
            }
            $backend->handleEmptyDirectory($packagePath, $deletePath, $hubItems);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * 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.
     * @param  string $session     is the user sesion key.
     * @return array package jptf representation.
     */
    function localCreate($accountSlug, $slug, $version, $user, $session) {
        $backend = $this->backend;
        if (!isset($user)) {
            $user = $this->getCurrentAccount();
        }
        try {
            $backend->localCreate($accountSlug, $slug, $version, $user, $session);
            $packageJptf = $this->get_package($accountSlug . '/' . $slug, 'jptf', false, $version);
        } catch (\Exception $e) {
            throw $e;
        }

        return $packageJptf;
    }

    /**
     * Get version documents of a package in details..
     *
     * @param  string $accountSlug is the user account name.
     * @param  string $slug        is the package name.
     * @param  string $label       is the package version.
     * @param  string $filePath    is the path to the file.
     * @return array file jptf representation.
     */
    function versionDownloadFile($accountSlug, $slug, $label, $filePath) {
        $backend = $this->backend;
        try {
            $packageSlug = $accountSlug . '/' . $slug;
            $package = $backend->package_exists($packageSlug, $label);
            if ($package === false) {
                $this->versionDownload($accountSlug, $slug, $label);
            }
            //Process media file
            $process_media = $backend->getPackageMedia($packageSlug, $filePath, $label);

            $packageIntermediary = $this->get_package($packageSlug, null, false, $label);
            $packageObject = $this->_string_to_obj_hash($packageIntermediary->data, $packageIntermediary->hash);
            $iterate_path = explode('/', $filePath);
            $fileData = $this->_find_fileData($iterate_path, $packageObject);
            if (empty($fileData['content'])) {
                throw new \Exception('{ "status_code": "500", "message" : "File ' . $filePath . ' not found in ' . $slug . '." }');
            }
            $fileJptf = $this->createPartialJptf(array($filePath), $packageIntermediary, $accountSlug . '/' . $slug);
        } catch (\Exception $e) {
            throw $e;
        }

        return $fileJptf;
    }

    /**
     * Create a partial jptf package to push.
     *
     * @param  array  $list                is the list of file's.
     * @param  object $packageIntermediary is the file location.
     * @return array partial jptf of package.
     */
    function createPartialJptf($list, $packageIntermediary, $packageSlug, $label = null, $action = null) {
        $object_dictionary = $this->_string_to_obj_hash($packageIntermediary->data, $packageIntermediary->hash);
        $partial_dict = array();
        $addStack = array();
        $emptyDirectoryStack = array();
        foreach ($list as $key => $value) {
            $value = ltrim($value, '/');
            if (strpos($value, '::add') !== false) {
                $iterate_path = explode('::add', $value);
                $keepTrack = $iterate_path[0];
                if (!in_array($keepTrack, $addStack)) {
                    array_push($addStack, $keepTrack);
                }
                $iterate_path = $iterate_path[0] . $iterate_path[1];
                $file_name = basename($iterate_path);
                $iterate_path = explode('/', $iterate_path);
            } else {
                $iterate_path = explode('/', $value);
                $file_name = basename($value);
            }
            $level = count($iterate_path);
            $file_data = $this->_find_fileData($iterate_path, $object_dictionary);
            $data = $file_data['content'];
            $directory_hashes = $file_data['hash_list'];
            $partial_dict = array_merge_recursive($this->_createPartialDict($iterate_path, $file_name, $level, $data, $directory_hashes), $partial_dict);
            if (!empty($directory_hashes) && !empty($data) && strpos($value, '::add') !== false) {
                $emptyDirectory = $iterate_path;
                unset($emptyDirectory[count($emptyDirectory) - 1]);
                $emptyDirectory = implode('/', $emptyDirectory);
                if (!empty($emptyDirectory)) {
                    $emptyDirectoryStack[] = $emptyDirectory;
                }
            }
        }
        $partialJptfData = $this->_builtJptf($this->_JptfList($partial_dict), $packageSlug, $label);
        $partialJptf[0]['/']['type'] = 'directory';
        $partialJptf[0]['/']['hash'] = $packageIntermediary->hash;
        $partialJptf[0]['/']['content'] = array();

        foreach ($partialJptfData as $key => $value) {
            $partialJptf[0]['/']['content'] = array_merge($partialJptfData[$key], $partialJptf[0]['/']['content']);
        }
        $unsortedJptf = $this->_JptfList($partialJptf[0]['/']['content']);
        sort($unsortedJptf);
        $unsortedJptf = $this->addEmptydirectorydelete($unsortedJptf, $addStack);
        $partialJptf[0]['/']['content'] = $unsortedJptf;
        if ($action == 'push') {
            return array('list' => $emptyDirectoryStack, 'data' => $partialJptf);
        } else {
            return $partialJptf;
        }
    }

    function addEmptydirectorydelete($partialJptfData, $addStack, $finalPath = null) {

        foreach ($addStack as $value) {
            $iterate_path = explode('/', $value);
            if (!isset($finalPath)) {
                $finalPath = basename($value);
            }
            foreach ($partialJptfData as $destinationKey => $destinationValue) {
                if ($finalPath === $destinationKey) {
                    $partialJptfData[$destinationKey] = array('type' => 'directory');
                    break;
                } else {
                    if (is_int($destinationKey)) {
                        $partialJptfData[$destinationKey + 1] = $this->addEmptydirectorydelete($partialJptfData[$destinationKey], $addStack, $finalPath);
                    } elseif (isset($partialJptfData[$destinationKey]['content'])) {
                        $partialJptfData[$destinationKey]['content'][0] = $this->addEmptydirectorydelete($partialJptfData[$destinationKey]['content'][0], $addStack, $finalPath);
                    } else {
                        $partialJptfData[$destinationKey] = $this->addEmptydirectorydelete($partialJptfData[$destinationKey], $addStack, $finalPath);
                    }
                }
            }
        }

        return $partialJptfData;
    }

    /**
     * saves package to local file system.
     *
     * @param  string $packageSlug         is the value to be deleted from stack.
     * @param  string $packageIntermediary is Intermediary package object.
     * @param  string $trim                is maintained if package has dependencies.
     * @return string package location i file system.
     */
    function save_local($packageSlug, $packageIntermediary, $label, $trim) {
        try {
            if (empty($packageIntermediary)) {
                throw new \Exception('{"status_code":"500", "message":"Package ' . $packageSlug . ' is Invalid."}');
            }
            $backend = $this->backend;
            $url = $backend->save_package($packageSlug, $packageIntermediary, $label, $trim);
        } catch (\Exception $e) {
            throw $e;
        }

        return $url;
    }

    /**
     * saves css files to local file system.
     *
     * @param string $fileName is file name.
     * @param string $content  is file data to be stored.
     *               
     * @return string file location.
     */
    function save_css($fileName, $content) {
        try {
            $backend = $this->backend;
            $url = $backend->save_css($fileName, $content);
        } catch (\Exception $e) {
            throw new \Exception('{"status_code":"500", "message":"BlendedError with save_css."}');
        }

        return $url;
    }

    /**
     * saves image file to local file system.
     *
     * @param  string $fileName    is file name.
     * @param  object $imageObject is image file object.
     * @return string file location.
     */
    function save_image($fileName, $imageObject) {
        try {
            $backend = $this->backend;
            $url = $backend->save_image($fileName, $imageObject);
        } catch (\Exception $e) {
            throw new \Exception('{"status_code":"500", "message":"BlendedError with save_image."}');
        }

        return $url;
    }

    /**
     * saves js file to local file system.
     *
     * @param string $fileName  is file name.
     * @param string $jsContent is file data to be stored.
     *    
     * @return string file location.
     */
    function save_js($fileName, $jsContent) {
        try {
            $backend = $this->backend;
            $url = $backend->save_js($fileName, $jsContent);
        } catch (\Exception $e) {
            throw new \Exception('{"status_code":"500", "message":"BlendedError with save_js."}');
        }

        return $url;
    }

    /**
     * Clone package on local file system.
     *
     * @param  string $accountSlug1 is the path to source package.
     * @param  string $slug1        is package name.
     * @param  string $accountSlug2 is the path to source package.
     * @param  string $slug2        is package name.
     * @param  string $sessionKey   is user session key.
     * @return array package jptf representation.
     */
    function localClone($accountSlug1, $slug1, $accountSlug2, $slug2, $packageTitle) {
        try {
            $backend = $this->backend;
            $sourceExist = $backend->package_exists($accountSlug1 . '/' . $slug1);
            $destinationExist = $backend->package_exists($accountSlug2 . '/' . $slug2);
            if ($sourceExist === false) {
                $this->pull_package($accountSlug1 . '/' . $slug1, null, true, true);
                $sourceExist = $backend->package_exists($accountSlug1 . '/' . $slug1);
                $status = $backend->localClone($sourceExist, $accountSlug2 . '/' . $slug2, null, true, $packageTitle);
                if ($status === true) {
                    return $this->get_package($accountSlug2 . '/' . $slug2, 'jptf', true, null, false, true);
                }
            } elseif (!$backend->is_validPackage($sourceExist)) {
                throw new \Exception('{"status_code":"500", "message":"Invalid package. _package.json file not found. This file must be present on root directory."}');
            } elseif ($destinationExist != false) {
                throw new \Exception('{"status_code":"500", "message":"Package name ' . $slug2 . ' already in use."}');
            } else {
                $status = $backend->localClone($sourceExist, $accountSlug2 . '/' . $slug2, null, true, $packageTitle);
            }
            if ($status === true) {
                $packageJptf = $this->get_package($accountSlug2 . '/' . $slug2, 'jptf', true, null, false, true);
                $invalidList = $backend->invalidFileDirectories();
                if (!empty($invalidList)) {
                    $backend->invalidFileDirectories(array(), true);
                    throw new \Exception(json_encode(array_merge(array("code" => 4035, "message" => "Invalid File/Directory"), array("data" => $invalidList))));
                }
            }
        } catch (\Exception $e) {
            throw $e;
        }

        return $packageJptf;
    }

    /**
     * Search image in local filesystem.
     *
     * @param  string $imageName is file name.
     * @return string file location.
     */
    function get_image($imageName) {
        try {
            $backend = $this->backend;
            $url = $backend->get_image($imageName);
        } catch (\Exception $e) {
            throw new \Exception('{"status_code":"500", "message":"BlendedError with get_image."}');
        }

        return $url;
    }

    /**
     * Retrieve image resource object.
     *
     * @param  string $imageUrl is location to the file in file system.
     * @return object file object.
     */
    function load_image($imageUrl) {
        try {
            $backend = $this->backend;
            $imageObject = $backend->load_image($imageUrl);
        } catch (\Exception $e) {
            throw new \Exception('{"status_code":"500", "message":"BlendedError with save_js."}');
        }

        return $imageObject;
    }

    /**
     * Compare Hub and local Package for pull & push to show any conflicts
     *
     * @param  string $packageSlug              is user account name + package name.
     * @param  array  $hubpackageIntermediary   is hub package details.
     * @param  array  $localpackageIntermediary are packages stored in local filesystem.
     * @param  string $action                   decides to either push or pull a package.
     * @return array package differences.
     */
    function compare_package($packageSlug, $hubpackageIntermediary, $localpackageIntermediary, $action = null) {
        $backend = $this->backend;
        $packageIntermediary = $localpackageIntermediary;
        $packageStatus = $backend->package_exists($packageSlug);
        $locallastpackageHash = $packageIntermediary->getHash($packageStatus);
        $localcurrentpackageHash = $packageIntermediary->hash;
        $hubcurrentpackageHash = $hubpackageIntermediary->hash;
        if ($action === 'push') {
            if ($localcurrentpackageHash === $locallastpackageHash) {
                if ($hubcurrentpackageHash === $locallastpackageHash) {
                    return array();
                } else {
                    return $this->differences($hubpackageIntermediary, $localpackageIntermediary);
                }
            } else {
                if ($hubcurrentpackageHash === $locallastpackageHash) {
                    return array(
                        'force' => true,
                        'data' => $this->differences($hubpackageIntermediary, $localpackageIntermediary),
                    );
                } else {
                    if ($hubcurrentpackageHash === $localcurrentpackageHash) {
                        $backend->save_packageHash($packageSlug, $hubpackageIntermediary);
                        return array();
                    } else {
                        return array(
                            'flag' => true,
                            'data' => $this->differences($hubpackageIntermediary, $localpackageIntermediary),
                        );
                    }
                }
            }
        } elseif ($action === 'pull') {
            if ($localcurrentpackageHash === $locallastpackageHash) {
                if ($hubcurrentpackageHash === $locallastpackageHash) {
                    return array();
                } else {
                    return array(
                        'force' => true,
                        'data' => $this->differences($hubpackageIntermediary, $localpackageIntermediary),
                    );
                }
            } else {
                if ($hubcurrentpackageHash === $locallastpackageHash) {
                    return $this->differences($hubpackageIntermediary, $localpackageIntermediary);
                } else {
                    if ($hubcurrentpackageHash === $localcurrentpackageHash) {
                        $backend->save_packageHash($packageSlug, $hubpackageIntermediary);
                        return array();
                    } else {
                        return array(
                            'flag' => true,
                            'data' => $this->differences($hubpackageIntermediary, $localpackageIntermediary),
                        );
                    }
                }
            }
        }
    }

    /**
     * Update Canonical.txt file.
     *
     * @param string $packageSlug is packagefully qualaified name.
     * @param string $label       is the package version points to canonical.
     */
    function updateCanonical_txt($packageSlug, $label) {
        $backend = $this->backend;
        try {
            $backend->updateCanonical_txt($packageSlug, $label);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Compare package.
     *
     * @param  string $accountSlug1 is the path to source package.
     * @param  string $slug1        is package name.
     * @param  string $accountSlug2 is the path to source package.
     * @param  string $slug2        is package name.
     * @return array list of files which are non matching.
     */
    function compare_draft($accountSlug1, $slug1, $accountSlug2, $slug2) {
        $backend = $this->backend;
        try {
            $packagePath1 = $backend->package_exists($accountSlug1 . '/' . $slug1);

            if ($packagePath1 === false) {
                $packageIntermediary1 = $this->pull_package($accountSlug1 . '/' . $slug1, null, true, true);
            } else {
                $packageIntermediary1 = $backend->get_package($packagePath1);
            }
            $packagePath2 = $backend->package_exists($accountSlug2 . '/' . $slug2);
            if ($packagePath2 === false) {
                $network = $this->network;
                $packageJptf = $network->downloadDraft($accountSlug2, $slug2);
                $packageJptf = json_encode($packageJptf);
                $packageJptf = json_decode($packageJptf, true);
                $canonicalVersion = $packageJptf[0]['/']['canonical'];
                if (isset($canonicalVersion) && !is_null($canonicalVersion)) {
                    $this->updateCanonical_txt($accountSlug2 . '/' . $slug2, $canonicalVersion);
                }
                $packageIntermediary2 = $this->deserialize_jptf($packageJptf);
            } else {
                $packageIntermediary2 = $backend->get_package($packagePath2);
            }
            $differences = $this->differences($packageIntermediary1, $packageIntermediary2);
        } catch (\Exception $e) {
            throw $e;
        }

        return $differences;
    }

    /**
     * Compare canonical package version.
     *
     * @param  string $accountSlug1 is the path to source package.
     * @param  string $slug1        is package name.
     * @param  string $accountSlug2 is the path to source package.
     * @param  string $slug2        is package name.
     * @return array list of files which are non matching.
     */
    function compare_canonical($accountSlug1, $slug1, $accountSlug2, $slug2) {
        $backend = $this->backend;
        try {
            $packageDetail1 = $this->canonicalDetails($accountSlug1, $slug1, true);
            $version1 = $packageDetail1['label'];
            $packagePath1 = $backend->package_exists($accountSlug1 . '/' . $slug1, $version1);
            if ($packagePath1 === false) {
                $packageIntermediary1 = $this->pull_package($accountSlug1 . '/' . $slug1, $version1, false, true);
            } else {
                $packageIntermediary1 = $backend->get_package($packagePath1);
            }
            $packageDetail2 = $this->canonicalDetails($accountSlug2, $slug2, true);
            $version2 = $packageDetail2['label'];
            $packagePath2 = $backend->package_exists($accountSlug2 . '/' . $slug2, $version2);
            if ($packagePath2 === false) {
                $packageIntermediary2 = $this->pull_package($accountSlug2 . '/' . $slug2, $version2, false, true);
            } else {
                $packageIntermediary2 = $backend->get_package($packagePath2);
            }
            $differences = $this->differences($packageIntermediary1, $packageIntermediary2);
        } catch (\Exception $e) {
            throw $e;
        }

        return $differences;
    }

    /**
     * Compare version of package.
     *
     * @param  string $accountSlug1 is the path to source package.
     * @param  string $slug1        is package name.
     * @param  string $accountSlug2 is the path to source package.
     * @param  string $slug2        is package name.
     * @param  string $label        is the package version.
     * @return array list of files which are non matching.
     */
    function compare_version($accountSlug1, $slug1, $accountSlug2, $slug2, $label) {

        $backend = $this->backend;
        try {

            $packagePath1 = $backend->package_exists($accountSlug1 . '/' . $slug1);

            if ($packagePath1 === false) {
                $packageJptf1 = $this->pull_package($accountSlug1 . '/' . $slug1, null, true, true);
                $packageIntermediary1 = $this->deserialize_jptf($packageJptf1);
            } else {
                $packageIntermediary1 = $backend->get_package($packagePath1);
            }

            $packagePath2 = $backend->package_exists($accountSlug2 . '/' . $slug2, $label);

            if ($packagePath2 === false) {
                $packageJptf2 = $this->pull_package($accountSlug2 . '/' . $slug2, $label, false, true);
                $packageIntermediary2 = $this->deserialize_jptf($packageJptf2);
            } else {
                $packageIntermediary2 = $backend->get_package($packagePath2);
            }
            $differences = $this->differences($packageIntermediary1, $packageIntermediary2);
        } catch (\Exception $e) {
            throw $e;
        }

        return $differences;
    }

    /**
     * Retrieve file data in comparison of packages.
     *
     * @param  string $accountSlug1 is the path to source package.
     * @param  string $slug1        is package name.
     * @param  string $accountSlug2 is the path to source package.
     * @param  string $slug2        is package name.
     * @param  string $filePath     is path to the file.
     * @return arrat list of files which are non matching.
     */
    function compare_labeledversion_file($accountSlug1, $slug1, $accountSlug2, $slug2, $label, $filePath) {
        $backend = $this->backend;
        try {
            $packagePath1 = $backend->package_exists($accountSlug1 . '/' . $slug1, $label);
            if ($packagePath1 === false) {
                $packageJptf1 = $this->pull_package($accountSlug1 . '/' . $slug1, $label, false, true);
                $packageIntermediary1 = $this->deserialize_jptf($packageJptf1);
            } else {
                $packageIntermediary1 = $backend->get_package($packagePath1);
            }
            $package1fileJptf = $this->createPartialJptf(
                    array(
                $filePath,
                    ), $packageIntermediary1, $accountSlug1 . '/' . $slug1
            );
            $package1fileJptf = $package1fileJptf['0']['/']['content'];
            $package1fileJptfData = $this->_getfileData($package1fileJptf, $filePath);
            $packagePath2 = $backend->package_exists($accountSlug2 . '/' . $slug2, $label);
            if ($packagePath2 === false) {
                $packageJptf2 = $this->pull_package($accountSlug2 . '/' . $slug2, $label, false, true);
                $packageIntermediary2 = $this->deserialize_jptf($packageJptf2);
            } else {
                $packageIntermediary2 = $backend->get_package($packagePath2);
            }
            $package2fileJptf = $this->createPartialJptf(
                    array(
                $filePath,
                    ), $packageIntermediary2, $accountSlug2 . '/' . $slug2
            );
            $package2fileJptf = $package2fileJptf['0']['/']['content'];
            $package2fileJptfData = $this->_getfileData($package2fileJptf, $filePath);
            $content = array(
                'package1' => $package1fileJptfData["content"],
                'package2' => $package2fileJptfData["content"],
            );
        } catch (\Exception $e) {
            throw $e;
        }

        return $content;
    }

    /**
     * find difference b/w local package and hub package.
     *
     * @param  object $hubpackageIntermediary   is package intermediary representation.
     * @param  object $localpackageIntermediary is package intermediary representation.
     * @return array package differences.
     */
    function differences($hubpackageIntermediary, $localpackageIntermediary) {
        $hubDifferences = @$this->_blendedRecursiveDiff($hubpackageIntermediary->data, $localpackageIntermediary->data);
        $localDifferences = @$this->_blendedRecursiveDiff($localpackageIntermediary->data, $hubpackageIntermediary->data, null, true);
        $totalDifferences = array_merge($hubDifferences, $localDifferences);
        $totalDifferences = array_unique($totalDifferences);
        $totalDifferences = array_values($totalDifferences);

        return $totalDifferences;
    }

    /**
     * Create Package to Hub  for the first time. This will be called in Push Operation where package does not exists to Hub.
     *
     * @param  string $accountSlug as current active user account.
     * @param  string $slug        as theme slug.
     * @return array package properties.
     */
    function createatHub($accountSlug, $slug) {
        $backend = $this->backend;
        try {
            $package_exist = $backend->package_exists($accountSlug . '/' . $slug);
            if ($package_exist === false) {
                throw new \Exception('{ "status_code":"500", "message":"Package ' . $slug . ' does not exist to local filesystem." }');
            } else {
                $packageIntermediary = $backend->get_package($package_exist, true);
                $invalidList = $backend->invalidFileDirectories();
                if (!empty($invalidList)) {
                    $backend->invalidFileDirectories(array(), true);
                    throw new \Exception(json_encode(array_merge(array("code" => 4035, "message" => "Invalid File/Directory"), array("data" => $invalidList))));
                }
                $this->packageType($packageIntermediary->data, $slug);
                $this->download_dependencies($packageIntermediary->data, 'weak', $accountSlug . '/' . $slug);
                $this->local_dependency_check($packageIntermediary->data, $slug);
                $packageJptf = $this->get_package($accountSlug . '/' . $slug, 'jptf', true, null, false, true);
                $packageIntermediary = $this->deserialize_jptf($packageJptf);
                $packageJptf = $this->as_jptf($packageIntermediary, $accountSlug . '/' . $slug);
                $network = $this->network;
                $properties = $network->createDraft($accountSlug, $slug, '', $packageJptf);
            }
        } catch (\Exception $e) {
            throw $e;
        }

        return $properties;
    }

    /**
     * Check Package type.
     *
     * @param object $packageIntermediary intermediary representation of package.
     */
    function packageType($packageIntermediary, $slug) {
        $packageDetails = $packageIntermediary['_package.json']->data;
        $packageDetails = json_decode($packageDetails, true);
        if (!isset($packageDetails['type'])) {
            throw new \Exception('{ "status_code":"500", "message":"Field type not defined with package ' . $slug . '." }');
        }
        if (strtolower($packageDetails['type']) != 'theme' && strtolower($packageDetails['type']) != 'layout') {
            throw new \Exception('{ "status_code":"500", "message":"Incorrect field type passed with package ' . $slug . '. Type can be either THEME or LAYOUT." }');
        }
    }

    /**
     * Save package Hash.
     *
     * @param string  $packageSlug package whose packageHash need to be updated.
     * @param object  $package     package object Intermediary representation.
     * @param boolean $trim        used to distiguish lib or src directory of package.
     */
    function save_packageHash($packageSlug, $packageIntermediary) {
        $backend = $this->backend;
        $backend->save_packageHash($packageSlug, $packageIntermediary);
    }

    /**
     * Move anonymous package to user directory on Signup.
     *
     * @param  array $packageList is the list of package in user account.
     * @return array local + hub package list.
     */
    function sync_client_hub($packageList) {
        try {
            $backend = $this->backend;
            $allPackages = $backend->sync_client_hub($packageList);
        } catch (\Exception $e) {
            throw new \Exception('{"status_code":"500", "message":"Package Conflict occured."}');
        }

        return $allPackages;
    }

    /**
     * Merge Hub and local stored packages details of logged-in user account.
     *
     * @param  array $packageList are the package details from hub current user account.
     * @return local + hub package details.
     */
    function sync_client_account($packageList) {
        try {
            $backend = $this->backend;
            $allPackages = $backend->sync_client_account($packageList);
        } catch (\Exception $e) {
            throw new \Exception('{"status_code":"500", "message":"Blended Exception in getting user local Account Packages."}');
        }

        return $allPackages;
    }

    /**
     * Check Package dependency exist in the filesytem or produce appropriate error message.
     *
     * @param           object $packageIntermediary as backend filesystem package.
     * @param           string $slug                is package name.
     * @param[optional] array $dependencyStack as dependency tree.
     * @param[optional] array $aliasStack as dependency alias name.
     * @return          array $dependencyStack is the list of packages in hierarchy of dependencies.
     */
    function local_dependency_check($packageIntermediary, $slug, $dependencyStack = array(), $aliasStack = array()) {
        if (isset($packageIntermediary['_package.json'])) {
            $packageInfo = $packageIntermediary['_package.json']->data;
            $packageInfo = json_decode($packageInfo, true);
            $depStack = json_encode($dependencyStack);
            if (empty($packageInfo) && json_last_error() !== JSON_ERROR_NONE) {
                throw new \Exception('{ "status_code":"500", "message":"Package ' . $slug . ' contains Invalid package.json file.", "data" : ' . $depStack . '  }');
            }
            if (empty($packageInfo['version']) || !isset($packageInfo['version'])) {
                throw new \Exception('{ "status_code":"500", "message":"Package ' . $slug . ' version not defined!", "data" : ' . $depStack . '  }');
            }
            if (empty($packageInfo['name']) || !isset($packageInfo['name'])) {
                throw new \Exception('{ "status_code":"500", "message":"Package ' . $slug . ' with version ' . $packageInfo['version'] . ' contains empty package name.", "data" : ' . $depStack . '  }');
            }
            if (isset($packageInfo['dependencies']) && !empty($packageInfo['dependencies']) && isset($packageInfo['name'])) {

                foreach ($packageInfo['dependencies'] as $key => $value) {
                    $depStack = json_encode($dependencyStack);
                    if (!isset($value['name']) || empty($value['name'])) {
                        throw new \Exception('{ "status_code":"500", "message":"Package name not defined with dependencies required by package ' . $slug . ' with version ' . $packageInfo['version'] . '.", "data" : ' . $depStack . ' }');
                    }
                    if (!isset($value['version']) || empty($value['version'])) {
                        throw new \Exception('{ "status_code":"500", "message":"Version not defined with dependencies required by package ' . $slug . ' with version ' . $packageInfo['version'] . '.", "data" : ' . $depStack . ' }');
                    }
                    if (empty($value['alias']) || !isset($value['alias'])) {
                        throw new \Exception('{ "status_code":"500", "message":"Dependency alias not defined with dependencies required by package ' . $slug . ' with version ' . $packageInfo['version'] . '.", "data" : ' . $depStack . ' }');
                    }
                    $depSlug = $value['name'];
                    $depVersion = $value['version'];
                    $depAlias = $value['alias'];
                    $packageSlug = $depSlug;
                    $dependencyName = basename($packageSlug);
                    // Call controller get_package to look for dependencies and return package object.
                    if (in_array($packageInfo['name'] . ':' . $depSlug . ':' . $depVersion, $dependencyStack) || $packageInfo['name'] === $depSlug) {
                        throw new \Exception('{ "status_code":"500", "message":"Cycle detected with dependencies defined with package' . $slug . ' with version ' . $packageInfo['version'] . '.", "data" : ' . $depStack . ' }');
                        break;
                    } else {
                        array_push($dependencyStack, $packageInfo['name'] . ':' . $depSlug . ':' . $depVersion);
                        $depStack = json_encode($dependencyStack);
                        $backend = $this->backend;
                        $packageStatus = $backend->package_exists($packageSlug, $depVersion);
                        if ($packageStatus != false) {
                            $packagePath = $packageStatus;
                            $package = $backend->get_package($packagePath);
                        } else {
                            throw new \Exception('{ "status_code":"500", "message":"Package ' . $slug . ' with version ' . $packageInfo['version'] . ' dependency for ' . $dependencyName . ' with version ' . $depVersion . ' does not exist.", "data" : ' . $depStack . ' }');
                        }
                        if (in_array($value['alias'], $aliasStack)) {
                            throw new \Exception('{ "status_code":"500", "message":"Duplicate alias detected with dependencies defined with package' . $slug . '.", "data" : ' . $depStack . ' }');
                            break;
                        } else {
                            array_push($aliasStack, $value['alias']);
                        }
                        $dependencyStack = $this->local_dependency_check($package->data, $dependencyName, $dependencyStack, $aliasStack);
                    }
                }
            }
        }

        return $dependencyStack;
    }

    /**
     * Get current active theme from stored configuration.
     *
     * @return string active package name.
     */
    function getActiveTheme() {
        try {
            $backend = $this->backend;
            $activeTheme = $backend->getActiveTheme();
        } catch (\Exception $e) {
            throw new \Exception('{ "status_code":"500", "message":"Blended Exception while getting active theme name." }');
        }

        return $activeTheme;
    }

    /**
     * Activate theme on being request.
     * Save theme name into current configuration.
     *
     * @param  string $themeSlug is the package name.
     * @return array package name.
     */
    function make_active($themeSlug) {
        $backend = $this->backend;
        $accountSlug = $backend->getCurrentAccount();
        $slug = $themeSlug;
        if (strpos($themeSlug, '/')) {
            $identifier = explode('/', $themeSlug);
            $slug = $identifier[1];
            $accountSlug = $identifier[0];
        }
        $packageIntermediary = $this->get_package($themeSlug, null, true);
        try {
            $this->download_dependencies($packageIntermediary->data, null, $themeSlug);
        } catch (\Exception $e) {
            throw $e;
        }
        if (!is_null($packageIntermediary)) {
            $backend->make_active($themeSlug);
        }

        return $this->getActiveTheme();
    }

    /**
     * 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())) {
        try {
            $backend = $this->backend;
            $allPackages = $backend->get_local_Packages($hubPackages);
        } catch (\Exception $e) {
            throw new \Exception('{ "status_code":"500", "message":"Blended Exception in getting user Account." }');
        }

        return $allPackages;
    }

    /**
     * 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.
     * @param  string $session                is the user session.
     * @return array local package detail list.
     */
    function get_Offline_Package_Details($slug, $accountSlug, $session) {
        try {
            $backend = $this->backend;
            $packageSlug = $accountSlug . '/' . $slug;
            $allPackages = $backend->get_Offline_Package_Details($packageSlug, $session);
        } catch (\Exception $e) {
            throw new \Exception('{ "status_code":"500", "message":"Blended Exception in getting user Account." }');
        }

        return $allPackages;
    }

    /**
     * Clone draft on local file system .
     *
     * @param  string $accountSlug1 is the path to destination package.
     * @param  string $slug1        is package name.
     * @param  string $accountSlug2 is the path to source package.
     * @param  string $slug2        is package name.
     * @return array package jptf representation.
     */
    function draftClone($accountSlug1, $slug1, $accountSlug2, $slug2) {
        $network = $this->network;
        $backend = $this->backend;
        try {
            $package1 = $backend->package_exists($accountSlug1 . '/' . $slug1);
            if ($package1 != false) {
                throw new \Exception('{"status_code":"500", "message":"Package name ' . $slug1 . ' already in use."}');
            }
            $packageJptf = $network->downloadDraft($accountSlug2, $slug2);
            $packageJptf = json_encode($packageJptf);
            $packageJptf = json_decode($packageJptf, true);
            if (isset($packageJptf[0]['/']['canonical']) && !is_null($packageJptf[0]['/']['canonical'])) {
                $canonicalVersion = $packageJptf[0]['/']['canonical'];
                $this->updateCanonical_txt($accountSlug2 . '/' . $slug2, $canonicalVersion);
            }
            $packageIntermediary = $this->deserialize_jptf($packageJptf);
            $packageIntermediary = $this->update_packageInfo($accountSlug1, $slug1, $packageIntermediary);
            $label = 'draft';
            $this->save_local($accountSlug1 . '/' . $slug1, $packageIntermediary, $label, true);
        } catch (\Exception $e) {
            throw $e;
        }

        return $packageJptf;
    }

    /**
     * Clone version of a package on local file system .
     *
     * @param  string $accountSlug1 is the path to destination package.
     * @param  string $slug1        is package name.
     * @param  string $accountSlug2 is the path to source package.
     * @param  string $slug2        is package name.
     * @param  string $label        is package version.
     * @return array|boolean package jptf representation else boolean true if package already exist and needs to be local cloned.
     */
    function versionClone($accountSlug1, $slug1, $accountSlug2, $slug2, $label, $newpackageHash, $packageTitle) {
        $network = $this->network;
        $backend = $this->backend;
        try {
            $package1 = $backend->package_exists($accountSlug1 . '/' . $slug1);
            if ($package1 != false && empty($newpackageHash)) {
                throw new \Exception('{"status_code":"500", "message":"Package name ' . $slug1 . ' already in use."}');
            } elseif ($package1 != false && !empty($newpackageHash)) {
                $packageIntermediary1 = $backend->get_package($package1);
                $oldpackageHash = $packageIntermediary1->hash;
                if ($oldpackageHash1 === $newpackageHash) {
                    throw new \Exception('{"status_code":"500", "message":"Package name ' . $slug1 . ' is uptodate. No Sync required."}');
                } else {
                    $packageJptf = $network->download($accountSlug2, $slug2, $label);
                    $packageJptf = json_encode($packageJptf);
                    $packageJptf = json_decode($packageJptf, true);
                    if (isset($packageJptf[0]['/']['canonical']) && !is_null($packageJptf[0]['/']['canonical'])) {
                        $canonicalVersion = $packageJptf[0]['/']['canonical'];
                        $this->updateCanonical_txt($accountSlug2 . '/' . $slug2, $canonicalVersion);
                    }
                    $packageIntermediary = $this->deserialize_jptf($packageJptf);
                    $packageIntermediary = $this->update_packageInfo($accountSlug1, $slug1, $packageIntermediary);
                    $this->save_local($accountSlug1 . '/' . $slug1, $packageIntermediary, 'draft', true);
                }
            }
            $package2 = $backend->package_exists($accountSlug2 . '/' . $slug2, $label);
            if ($package2 != false) {
                $backend->localClone($package2, $accountSlug1 . '/' . $slug1, null, true, $packageTitle);
                $packageJptf = $network->download($accountSlug2, $slug2, $label);
                $packageJptf = json_encode($packageJptf);
                $packageJptf = json_decode($packageJptf, true);
            } else {
                $packageJptf = $network->download($accountSlug2, $slug2, $label);
                $packageJptf = json_encode($packageJptf);
                $packageJptf = json_decode($packageJptf, true);
                if (isset($packageJptf[0]['/']['canonical']) && !is_null($packageJptf[0]['/']['canonical'])) {
                    $canonicalVersion = $packageJptf[0]['/']['canonical'];
                    $this->updateCanonical_txt($accountSlug2 . '/' . $slug2, $canonicalVersion);
                }
                $packageIntermediary1 = $this->deserialize_jptf($packageJptf);
                $packageIntermediary2 = $this->update_packageInfo($accountSlug1, $slug1, $packageIntermediary1);
                $this->save_local($accountSlug2 . '/' . $slug2, $packageIntermediary1, $label, false);
                $package2 = $backend->package_exists($accountSlug2 . '/' . $slug2, $label);
                $backend->localClone($package2, $accountSlug1 . '/' . $slug1, null, true, $packageTitle);
            }
        } catch (\Exception $e) {
            throw $e;
        }

        return $packageJptf;
    }

    /**
     * Compare draft document.
     *
     * @param  string $accountSlug1 is the path to destination package.
     * @param  string $slug1        is package name.
     * @param  string $accountSlug2 is the path to source package.
     * @param  string $slug2        is package name.
     * @return array both file differences.
     */
    function compareFileWithDraft($filePath, $accountSlug1, $slug1, $accountSlug2, $slug2) {
        $network = $this->network;
        $backend = $this->backend;
        $packageSlug1 = $accountSlug1 . '/' . $slug1;
        $packageSlug2 = $accountSlug2 . '/' . $slug2;
        $draft = true;
        $label = null;
        try {
            $package1 = $backend->package_exists($packageSlug1);
            if ($package1 === false) {
                $packageJptf1 = $this->pull_package($packageSlug1, null, true, true);
                $packageIntermediary1 = $this->deserialize_jptf($packageJptf1);
            } else {
                $packageIntermediary1 = $this->get_package($packageSlug1, null, $draft, $label);
            }
            $localfileJptf1 = $this->createPartialJptf(
                    array(
                $filePath,
                    ), $packageIntermediary1, $accountSlug1 . '/' . $slug1
            );
            $localfileJptf1 = $localfileJptf1['0']['/']['content'];
            $localfileData1 = $this->_getfileData($localfileJptf1, $filePath);
            $package2 = $backend->package_exists($packageSlug2);
            if ($package2 === false) {
                $packageJptf2 = $this->pull_package($packageSlug2, null, true, true);
                $packageIntermediary2 = $this->deserialize_jptf($packageJptf2);
            } else {
                $packageIntermediary2 = $this->get_package($packageSlug2, null, $draft, $label);
            }
            $localfileJptf2 = $this->createPartialJptf(
                    array(
                $filePath,
                    ), $packageIntermediary2, $accountSlug2 . '/' . $slug2
            );
            $localfileJptf2 = $localfileJptf2['0']['/']['content'];
            $localfileData2 = $this->_getfileData($localfileJptf2, $filePath);
            $content = array(
                'package1' => $localfileData1["content"],
                'package2' => $localfileData2["content"],
            );
        } catch (\Exception $e) {
            throw $e;
        }

        return $content;
    }

    /**
     * Compare draft document.
     *
     * @param  string $accountSlug1 is the path to destination package.
     * @param  string $slug1        is package name.
     * @param  string $accountSlug2 is the path to source package.
     * @param  string $slug2        is package name.
     * @return array both file differences.
     */
    function compareFileWithLocal($filePath, $accountSlug1, $slug1, $accountSlug2, $slug2) {
        $network = $this->network;
        $backend = $this->backend;
        $packageSlug1 = $accountSlug1 . '/' . $slug1;
        $packageSlug2 = $accountSlug2 . '/' . $slug2;
        $draft = true;
        $label = null;
        try {
            $package1 = $backend->package_exists($packageSlug1);
            if ($package1 === false) {
                $packageJptf1 = $this->pull_package($packageSlug1, null, $draft, true);
                $packageIntermediary1 = $this->deserialize_jptf($packageJptf1);
            } else {
                $packageIntermediary1 = $this->get_package($packageSlug1, null, $draft, $label);
            }
            $localfileJptf1 = $this->createPartialJptf(
                    array(
                $filePath,
                    ), $packageIntermediary1, $accountSlug1 . '/' . $slug1
            );
            $localfileJptf1 = $localfileJptf1['0']['/']['content'];
            $localfileData1 = $this->_getfileData($localfileJptf1, $filePath);
            $package2 = $backend->package_exists($packageSlug2);
            if ($package2 === false) {
                $packageJptf2 = $this->pull_package($packageSlug2, null, $draft, true);
                $packageIntermediary2 = $this->deserialize_jptf($packageJptf2);
            } else {
                $packageIntermediary2 = $this->get_package($packageSlug2, null, $draft, $label);
            }
            $localfileJptf2 = $this->createPartialJptf(
                    array(
                $filePath,
                    ), $packageIntermediary2, $accountSlug2 . '/' . $slug2
            );
            $localfileJptf2 = $localfileJptf2['0']['/']['content'];
            $localfileData2 = $this->_getfileData($localfileJptf2, $filePath);
            $content = array(
                'package1' => $localfileData1["content"],
                'package2' => $localfileData2["content"],
            );
        } catch (\Exception $e) {
            throw $e;
        }

        return $content;
    }

    /**
     * Compare version document.
     *
     * @param  string $accountSlug1 is the path to destination package.
     * @param  string $slug1        is package name.
     * @param  string $accountSlug2 is the path to source package.
     * @param  string $slug2        is package name.
     * @param  string $label        is package version.
     * @return array both file differences.
     */
    function compareFileWithVersion($filePath, $accountSlug1, $slug1, $accountSlug2, $slug2, $label) {
        $network = $this->network;
        $backend = $this->backend;
        $packageSlug1 = $accountSlug1 . '/' . $slug1;
        $packageSlug2 = $accountSlug2 . '/' . $slug2;
        $draft = true;
        try {
            $package1 = $backend->package_exists($packageSlug1, $label);
            if ($package1 === false) {
                $packageJptf1 = $this->pull_package($packageSlug1, null, $draft, true);
                $packageIntermediary1 = $this->deserialize_jptf($packageJptf1);
            } else {
                $packageIntermediary1 = $this->get_package($packageSlug1, null, $draft, null);
            }
            $localfileJptf1 = $this->createPartialJptf(
                    array(
                $filePath,
                    ), $packageIntermediary1, $accountSlug1 . '/' . $slug1
            );
            $localfileJptf1 = $localfileJptf1['0']['/']['content'];
            $localfileData1 = $this->_getfileData($localfileJptf1, $filePath);
            $package2 = $backend->package_exists($packageSlug2, $label);
            if ($package2 === false) {
                $packageJptf2 = $this->pull_package($packageSlug2, $label, false, true);
                $packageIntermediary2 = $this->deserialize_jptf($packageJptf2);
            } else {
                $draft = false;
                $packageIntermediary2 = $this->get_package($packageSlug2, null, $draft, $label);
            }
            $localfileJptf2 = $this->createPartialJptf(
                    array(
                $filePath,
                    ), $packageIntermediary2, $accountSlug2 . '/' . $slug2, $label
            );
            $localfileJptf2 = $localfileJptf2['0']['/']['content'];
            $localfileData2 = $this->_getfileData($localfileJptf2, $filePath);
            $content = array(
                'package1' => $localfileData1["content"],
                'package2' => $localfileData2["content"],
            );
        } catch (\Exception $e) {
            throw $e;
        }

        return $content;
    }

    /**
     * Compare draft.
     *
     * @param  string $accountSlug1 is the path to destination package.
     * @param  string $slug1        is package name.
     * @param  string $accountSlug2 is the path to source package.
     * @param  string $slug2        is package name.
     * @return array package differences.
     */
    function compareWithLocal($accountSlug1, $slug1, $accountSlug2, $slug2) {
        $backend = $this->backend;
        try {
            $packagePath1 = $backend->package_exists($accountSlug1 . '/' . $slug1);
            if ($packagePath1 === false) {
                //throw new \Exception('{"status_code":"500", "message":"Package ' . $slug1 . ' does not exist."}');
                $packageIntermediary1 = $this->pull_package($accountSlug1 . '/' . $slug1, null, true, true);
            } else {
                $packageIntermediary1 = $backend->get_package($packagePath1);
            }
            $packagePath2 = $backend->package_exists($accountSlug2 . '/' . $slug2);
            if ($packagePath2 === false) {
                throw new \Exception('{"status_code":"500", "message":"Package ' . $slug2 . ' does not exist."}');
            } else {
                $packageIntermediary2 = $backend->get_package($packagePath2);
            }
            $differences = $this->differences($packageIntermediary1, $packageIntermediary2);
        } catch (\Exception $e) {
            throw $e;
        }

        return $differences;
    }

    /**
     * Update draft.
     *
     * @param  string $accountSlug1 is the path to destination package.
     * @param  string $slug1        is package name.
     * @param  string $accountSlug2 is the path to source package.
     * @param  string $slug2        is package name.
     * @return array draft jptf representation.
     */
    function updateFromDraft($accountSlug1, $slug1, $accountSlug2, $slug2) {
        $network = $this->network;
        try {
            $packageJptf = $network->downloadDraft($accountSlug2, $slug2);
            $packageJptf = json_encode($packageJptf);
            $packageJptf = json_decode($packageJptf, true);
            if (isset($packageJptf[0]['/']['canonical']) && !is_null($packageJptf[0]['/']['canonical'])) {
                $canonicalVersion = $packageJptf[0]['/']['canonical'];
                $this->updateCanonical_txt($accountSlug2 . '/' . $slug2, $canonicalVersion);
            }
            $packageIntermediary = $this->deserialize_jptf($packageJptf);
            $packageIntermediary = $this->update_packageInfo($accountSlug1, $slug1, $packageIntermediary);
            $label = 'draft';
            $this->save_local($accountSlug1 . '/' . $slug1, $packageIntermediary, $label, true);
        } catch (\Exception $e) {
            throw $e;
        }

        return $packageJptf;
    }

    /**
     * Update local draft.
     *
     * @param  string $accountSlug1 is the path to destination package.
     * @param  string $slug1        is package name.
     * @param  string $accountSlug2 is the path to source package.
     * @param  string $slug2        is package name.
     * @return boolean status.
     */
    function updateFromLocal($accountSlug1, $slug1, $accountSlug2, $slug2, $files) {
        $backend = $this->backend;
        $packageSlug1 = $accountSlug1 . '/' . $slug1;
        $packageSlug2 = $accountSlug2 . '/' . $slug2;
        try {
            $destinationPackage = $backend->package_exists($accountSlug1 . '/' . $slug1);
            if ($destinationPackage === false) {
                throw new \Exception('{"status_code":"500", "message":"Package ' . $slug2 . ' does not exist."}');
            } elseif (!$backend->is_validPackage($destinationPackage)) {
                throw new \Exception('{"status_code":"500", "message":"Invalid package. _package.json file not found. This file must be present on root directory."}');
            }
            $sourcePackage = $backend->package_exists($packageSlug2);
            if ($sourcePackage === false) {
                $packageJptf2 = $this->pull_package($packageSlug2, null, true, true);
            }
            $files = $files['files'];
            foreach ($files as $file) {
                $path = ltrim($file, '/');
                $status = $backend->check_fileExist($packageSlug2, $path);
                if (false == $status) {
                    $backend->delete_local_to_syncHub($packageSlug1, $path);
                } else {
                    $backend->move_packages('/' . $path, '/' . $path, $sourcePackage, $destinationPackage);
                }
            }
        } catch (\Exception $e) {
            throw $e;
        }

        return true;
    }

    /**
     * Update draft from version document.
     *
     * @param  string $accountSlug1 is the path to destination package.
     * @param  string $slug1        is package name.
     * @param  string $accountSlug2 is the path to source package.
     * @param  string $slug2        is package name.
     * @param  string $label        is package version.
     * @return boolean status.
     */
    function updateFromVersion($accountSlug1, $slug1, $accountSlug2, $slug2, $label, $files) {
        $backend = $this->backend;
        $packageSlug1 = $accountSlug1 . '/' . $slug1;
        $packageSlug2 = $accountSlug2 . '/' . $slug2;
        $draft = false;
        try {
            $destinationPackage = $backend->package_exists($packageSlug1);
            if ($destinationPackage === false) {
                throw new \Exception('{"status_code":"500", "message":"Package ' . $slug1 . ' does not exist."}');
            } elseif (!$backend->is_validPackage($destinationPackage)) {
                throw new \Exception('{"status_code":"500", "message":"Invalid package. _package.json file not found. This file must be present with the package."}');
            }
            $sourcePackage = $backend->package_exists($packageSlug2, $label);
            if ($sourcePackage === false) {
                $packageJptf2 = $this->pull_package($packageSlug2, $label, false, true);
            }
            $files = $files['files'];
            foreach ($files as $file) {
                $path = ltrim($file, '/');
                $status = $backend->check_fileExist($packageSlug2, $path, $label);
                if (false == $status) {
                    $backend->delete_local_to_syncHub($packageSlug1, $path);
                } else {
                    $backend->move_packages('/' . $path, '/' . $path, $sourcePackage, $destinationPackage);
                }
            }
        } catch (\Exception $e) {
            throw $e;
        }

        return true;
    }

    /**
     * Update draft from canonical document.
     *
     * @param  string $accountSlug1 is the path to destination package.
     * @param  string $slug1        is package name.
     * @param  string $accountSlug2 is the path to source package.
     * @param  string $slug2        is package name.
     * @return array package jptf representation.
     */
    function updateFromCanonical($accountSlug1, $slug1, $accountSlug2, $slug2) {
        $network = $this->network;
        $backend = $this->backend;
        $packageSlug1 = $accountSlug1 . '/' . $slug1;
        $packageSlug2 = $accountSlug2 . '/' . $slug2;
        try {
            $packageJptf = $this->canonicalDownload($accountSlug2, $slug2);
            $packageIntermediary2 = $this->deserialize_jptf($packageJptf);
            $sourcePackage = $backend->package_exists($packageSlug1);
            if ($sourcePackage === false) {
                throw new \Exception('{"status_code":"500", "message":"Package ' . $slug1 . ' does not exist."}');
            }
            $packageIntermediary2 = $this->update_packageInfo($accountSlug1, $slug1, $packageIntermediary2);
            $this->save_local($accountSlug1 . '/' . $slug1, $packageIntermediary2, 'draft', true);
        } catch (\Exception $e) {
            throw $e;
        }

        return $packageJptf;
    }

    /**
     * Clone version of a package on local file system .
     *
     * @param  string $accountSlug1 is the path to destination package.
     * @param  string $slug1        is package name.
     * @param  string $accountSlug2 is the path to source package.
     * @param  string $slug2        is package name.
     * @param  string $label        is package version.
     * @return array package jptf representation.
     */
    function canonicalClone($accountSlug1, $slug1, $accountSlug2, $slug2, $packageTitle) {
        $network = $this->network;
        $backend = $this->backend;
        $packageSlug1 = $accountSlug1 . '/' . $slug1;
        $packageSlug2 = $accountSlug2 . '/' . $slug2;
        try {
            $sourcePackage = $backend->package_exists($packageSlug1);
            if ($sourcePackage != false) {
                throw new \Exception('{"status_code":"500", "message":"Package name ' . $slug1 . ' already in use."}');
            }
            $packageJptf = $network->downloadCanonical($accountSlug2, $slug2);
            $packageIntermediary1 = $this->deserialize_jptf($packageJptf);
            $canonicalDetails = $this->canonicalDetails($accountSlug2, $slug2, true);
            $canonicalDetails = json_encode($canonicalDetails);
            $canonicalDetails = json_decode($canonicalDetails, true);
            $label = $canonicalDetails['label'];
            $destinationPackage = $backend->package_exists($packageSlug2, $label);
            if ($destinationPackage == false) {
                $this->save_local($accountSlug2 . '/' . $slug2, $packageIntermediary1, $label, false);
            }
            $package2 = $backend->package_exists($accountSlug2 . '/' . $slug2, $label);
            $backend->localClone($package2, $accountSlug1 . '/' . $slug1, null, true, $packageTitle);
        } catch (\Exception $e) {
            throw $e;
        }

        return $packageJptf;
    }

    /**
     * Update package Info.
     *
     * @param  string $accountSlug         is account slug.
     * @param  string $slug                is package name.
     * @param  object $packageIntermediary is package intermediary representation.
     * @return array updated package Intermediary representation.
     */
    function update_packageInfo($accountSlug, $slug, $packageIntermediary) {
        $packageData = $packageIntermediary->data['_package.json']->data;
        $packageData = json_decode($packageData, true);
        $packageData['slug'] = $slug;
        $packageData['user'] = $accountSlug;
        $packageData['name'] = $accountSlug . '/' . $slug;
        $packageData['version'] = "draft";
        $packageData = json_encode($packageData);
        $packageIntermediary->data['_package.json']->data = $packageData;

        return $packageIntermediary;
    }

    /**
     * Compare Canonical version of a document.
     *
     * @param  string $$filePath    is location to file.
     * @param  string $accountSlug1 is the path to destination package.
     * @param  string $slug1        is package name.
     * @param  string $accountSlug2 is the path to source package.
     * @param  string $slug2        is package name.
     * @return array both file differences.
     */
    function compareFileWithCanonical($filePath, $accountSlug1, $slug1, $accountSlug2, $slug2) {
        $network = $this->network;
        $backend = $this->backend;
        $packageSlug1 = $accountSlug1 . '/' . $slug1;
        $packageSlug2 = $accountSlug2 . '/' . $slug2;
        $draft = true;
        try {
            $packageJptf = $network->downloadCanonical($accountSlug2, $slug2);
            $packageIntermediary2 = $this->deserialize_jptf($packageJptf);
            $CanonicalJptf = $this->createPartialJptf(
                    array(
                $filePath,
                    ), $packageIntermediary2, $accountSlug2 . '/' . $slug2
            );
            $canonicalfileJptf = $CanonicalJptf['0']['/']['content'];
            $canonicalfileData = $this->_getfileData($canonicalfileJptf, $filePath);
            $package1 = $backend->package_exists($packageSlug1);
            if ($package1 === false) {
                $packageJptf = $this->pull_package($packageSlug1, null, true, true);
                $packageIntermediary1 = $this->deserialize_jptf($packageJptf);
            } else {
                $packageIntermediary1 = $this->get_package($packageSlug1, null, $draft, true);
            }
            $localfileJptf1 = $this->createPartialJptf(
                    array(
                $filePath,
                    ), $packageIntermediary1, $accountSlug1 . '/' . $slug1
            );
            $localfileJptf1 = $localfileJptf1['0']['/']['content'];
            $localfileData1 = $this->_getfileData($localfileJptf1, $filePath);
            $content = array(
                'draft' => $localfileData1["content"],
                'canonical' => $canonicalfileData["content"],
            );
        } catch (\Exception $e) {
            throw $e;
        }

        return $content;
    }

    /**
     * Get current package hash.
     *
     * @param  string $accountSlug is the path to destination package.
     * @param  string $slug        is package name.
     * @return current package hash.
     */
    function currenthash($accountSlug, $slug) {
        try {
            $packageIntermediary = $this->get_package($accountSlug . '/' . $slug, null, true);
            $packageHash = array('currentpackageHash' => $packageIntermediary->hash);
        } catch (\Exception $e) {
            throw $e;
        }

        return $packageHash;
    }

    function blended_initials() {
        $network = $this->network;
        $backend = $this->backend;
        $packages = $network->getPackageInitial();
        if (is_array($packages) && array_key_exists("items", $packages)) :
            try {
                foreach ((array) $packages['items'] as $key => $acquisition):
                    if (strpos($acquisition['name'], '/')) {
                        $accountIdentifier = explode('/', $acquisition['name']);
                        $accountSlug2 = $accountIdentifier[0];
                        $slug2 = $accountIdentifier[1];
                        $label = $acquisition['version'];
                    }
                    //Check if acquisition package already exist into local filesystem.
                    $acquisitionExist = $backend->package_exists($accountSlug2 . '/' . $slug2, $label);
                    if (false === $acquisitionExist) :
                        //Install the package version.
                        $install = $this->install_package($accountSlug2 . '/' . $slug2, $label);
                    endif;

                    $accountSlug1 = $backend->getCurrentAccount();
                    $slug1 = $slug2;
                    //Check if package need to be moved as a draft to user account directory.
                    $draftExist = $backend->package_exists($accountSlug1 . '/' . $slug1);
                    if (false === $draftExist) :
                        $slug1 = $slug2;
                        $version = $label;
                        $packageHash = '';
                        $this->versionClone($accountSlug1, $slug1, $accountSlug2, $slug2, $version, $packageHash, $acquisition['title']);
                    endif;

                    //Activate default theme
                    if (isset($acquisition['is_active']) && $acquisition['is_active'])
                        $this->make_active($accountSlug1 . "/" . $slug1);
                endforeach;
                $backend->firstLogintrack();
            } catch (\Exception $e) {
                throw new \Exception("An automatic procedure failed to download Acquisitions as the package either not published or it's a paid package. However You may continue and require to refresh your browser page.");
            }
        endif;
    }

    /*
     * ----------------------------------------------------------------------------------------------------------------------------------------
     * METHODS THAT COMMUNICATE TO NETWORK.
     * ----------------------------------------------------------------------------------------------------------------------------------------
     */

    /**
     * Get detail view of canonical version of a package.
     *
     * @param  string              $accountSlug is account name.
     * @param  string              $slug        is package name.
     * @param  boolean['optional'] $verbose     is the type of details in detailed or summary.                             
     * @return array canonical package Details.
     */
    function canonicalDetails($accountSlug, $slug, $verbose = false) {
        $network = $this->network;
        $backend = $this->backend;
        try {
            $canonicalDetails = $network->getCanonical($accountSlug, $slug, $verbose);
            $label = $canonicalDetails['label'];
            $this->updateCanonical_txt($accountSlug . '/' . $slug, $label);
        } catch (\Exception $e) {
            throw $e;
        }

        return $canonicalDetails;
    }

    /**
     * Update package version as canonical version.
     *
     * @param string $accountSlug is account name.
     * @param string $slug        is package name.
     * @param string $label       is package version.                  
     * @param array  $body        is to request data used to update package version.
     */
    function canonicalSet($accountSlug, $slug, $body) {
        $network = $this->network;
        $backend = $this->backend;
        try {
            $network->canonicalSet($accountSlug, $slug, $body);
            $label = $body['label'];
            $this->updateCanonical_txt($accountSlug . '/' . $slug, $label);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Get canonical version of a package and store it in local filesystem.
     *
     * @param  string $accountSlug is account name.
     * @param  string $slug        is package name.
     * @return array package Jptf representation.
     */
    function canonicalDownload($accountSlug, $slug) {
        $network = $this->network;
        $backend = $this->backend;
        try {
            $canonicalDetails = $this->canonicalDetails($accountSlug, $slug, true);
            $canonicalDetails = json_encode($canonicalDetails);
            $canonicalDetails = json_decode($canonicalDetails, true);
            $label = $canonicalDetails['label'];
            $packageSlug = $accountSlug . '/' . $slug;
            $packageExist = $backend->package_exists($packageSlug, $label);
            if ($packageExist === false) {
                $packageJptf = $network->downloadCanonical($accountSlug, $slug);
                $packageJptf = $this->readOnly($packageJptf);
                $packageIntermediary = $this->deserialize_jptf($packageJptf);
                $this->save_local($accountSlug . '/' . $slug, $packageIntermediary, $label, false);
            } else {
                $packageJptf = $this->get_package($packageSlug, 'jptf', false, $label);
                $packageJptf = $this->readOnly($packageJptf);
            }
        } catch (\Exception $e) {
            throw $e;
        }

        return $packageJptf;
    }

    /**
     * Add extra detail for readoly to packagejptf
     *
     * @param  array $packageJptf is package jptf format.
     * @return array package Jptf representation for readOly packages.
     */
    function readOnly($packageJptf) {
        $packageJptf[0]['/']['readonly'] = true;
        return $packageJptf;
    }

    /**
     * Get file from canonical version of a package.
     *
     * @param  string $accountSlug is the user account name.
     * @param  string $slug        is the package name.
     * @param  string $label       is the package version.
     * @param  string $filePath    is the location to the file in filesystem.
     * @return array file jptf representation.
     */
    function canonicalDownloadFile($accountSlug, $slug, $filePath) {
        $backend = $this->backend;
        $network = $this->network;
        try {
            $packageJptf = $network->downloadCanonical($accountSlug, $slug);
            $packageIntermediary = $this->deserialize_jptf($packageJptf);
            $label = $this->_getpackage_version($packageIntermediary->data);
            $packageSlug = $accountSlug . '/' . $slug;
            $package = $backend->package_exists($packageSlug, $label);
            if ($package === false) {
                $this->save_local($packageSlug, $packageIntermediary, $label, false);
            }
            $Intermediary_object = $this->get_package($packageSlug, null, false, $label);
            $object_dictionary = $this->_string_to_obj_hash($Intermediary_object->data, $Intermediary_object->hash);
            $iterate_path = explode('/', $filePath);
            $fileData = $this->_find_fileData($iterate_path, $object_dictionary);
            if (empty($fileData['content'])) {
                throw new \Exception('{ "status_code": "500", "message" : "File ' . $filePath . ' not found in ' . $slug . '." }');
            }
            $fileJptf = $this->createPartialJptf(array($filePath), $Intermediary_object, $accountSlug . '/' . $slug);

            return $fileJptf;
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Create a snapshot of a package.
     *
     * @param  string $accountSlug is the user account name.
     * @param  string $slug        is the package name.
     * @param  array  $body        is the package label detail.
     * @return array package details.
     */
    function versionCreateSnapshot($accountSlug, $slug, $body) {
        $network = $this->network;
        try {
            if (strtolower($body['label']) === 'draft' || strtolower($body['label']) === 'canonical') {
                throw new \Exception('{ "status_code": "500", "message" : "You are not allowed to use version label as ' . $body['label'] . '." }');
            }
            $type = "snapshot";
            $this->isSync($accountSlug, $slug, $type);
            $packageDetails = $network->snapshot($accountSlug, $slug, $body);
        } catch (\Exception $e) {
            throw $e;
        }

        return $packageDetails;
    }

    /**
     * Check local and hub package synchronous status.
     *
     * @param  string $accountSlug is the user account name.
     * @param  string $slug        is the package name.
     */
    function isSync($accountSlug, $slug, $type) {
        $network = $this->network;
        $backend = $this->backend;
        try {
            $packageStatus = $backend->package_exists($accountSlug . '/' . $slug);
            $packageJptf = $network->downloadDraft($accountSlug, $slug);
            $packageJptf = json_encode($packageJptf);
            $packageJptf = json_decode($packageJptf, true);
            if ($packageStatus != false) {
                $packageIntermediary = $backend->get_package($packageStatus);
                $currentpackageHash = $packageIntermediary->hash;
                $packageJptf = $network->downloadDraft($accountSlug, $slug, "1");
                $packageJptf = json_encode($packageJptf);
                $packageJptf = json_decode($packageJptf, true);
                $hubpackageHash = $packageJptf[0]['/']['hash'];
                if ($currentpackageHash != $hubpackageHash) {
                    if ($type == "transfer") {
                        throw new \Exception('{ "status_code": "500", "message" : "Your local copy of Package ' . $accountSlug . ' is out-of-sync with the Hub. Before transfer, you need to pull or push in order to sync up with the Hub." }');
                    } elseif ($type == "share") {
                        throw new \Exception('{ "status_code": "500", "message" : "Your local copy of Package ' . $accountSlug . ' is out-of-sync with the Hub. Before share, you need to pull or push in order to sync up with the Hub." }');
                    }
                    throw new \Exception('{ "status_code": "500", "message" : "Your local copy of Package ' . $accountSlug . ' is out-of-sync with the Hub. Before creating a snapshot, you need to pull or push in order to sync up with the Hub." }');
                }
            } else {
                $packageJptf = $network->downloadDraft($accountSlug, $slug);
                $packageJptf = json_encode($packageJptf);
                $packageJptf = json_decode($packageJptf, true);
                $packageIntermediary = $this->deserialize_jptf($packageJptf);
            }
            if ($type != "share") {
                $this->checkDraft($packageIntermediary);
            }
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Check local and hub package synchronous status.
     *
     * @param  string $accountSlug is the user account name.
     * @param  string $slug        is the package name.
     */
    function checkDraft($packageIntermediary) {
        try {
            $packageInfo = $packageIntermediary->data['_package.json']->data;
            $packageInfo = json_decode($packageInfo, true);
            if (isset($packageInfo['dependencies']) && !empty($packageInfo['dependencies'])) {
                $errorPackages = array();
                foreach ($packageInfo['dependencies'] as $key => $value) {
                    $depVersion = $value['version'];
                    if ($depVersion === "draft") {
                        $errorPackages[] = $value["name"];
                    }
                }
                if (!empty($errorPackages)) {
                    throw new \Exception(json_encode(array("status_code" => 500, "message" => "Operation not allowed with dependency version as draft", "draft_dependency" => $errorPackages)));
                }
            }
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Get version data of a package in details.
     *
     * @param  string $accountSlug is the user account name.
     * @param  string $slug        is the package name.
     * @param  string $label       is the package version.
     * @return array package details.
     */
    function getVersionDetails($accountSlug, $slug, $label) {
        $network = $this->network;
        try {
            $packageDetails = $network->getVersionDetails($accountSlug, $slug, $label);
        } catch (\Exception $e) {
            throw $e;
        }

        return $packageDetails;
    }

    /**
     * Get version documents of a package.
     *
     * @param  string $accountSlug is the user account name.
     * @param  string $slug        is the package name.
     * @param  string $label       is the package version.
     * @return array package jptf representation.
     */
    function versionDownload($accountSlug, $slug, $label, $asHash = false) {
        $network = $this->network;
        $backend = $this->backend;
        try {
            $packageSlug = $accountSlug . '/' . $slug;
            $packageExist = $backend->package_exists($packageSlug, $label);
            if ($asHash === true && false == $packageExist) {
                throw new \Exception('{"status_code":"500", "message":"Package ' . $slug . ' does not exist."}');
            }
            if ($packageExist === false) {
                $packageJptf = $network->download($accountSlug, $slug, $label);
                $packageJptf = json_encode($packageJptf);
                $packageJptf = json_decode($packageJptf, true);
                if (isset($packageJptf[0]['/']['canonical']) && !is_null($packageJptf[0]['/']['canonical'])) {
                    $canonicalVersion = $packageJptf[0]['/']['canonical'];
                    $this->updateCanonical_txt($accountSlug . '/' . $slug, $canonicalVersion);
                }
                $packageIntermediary = $this->deserialize_jptf($packageJptf);
                $this->save_local($accountSlug . '/' . $slug, $packageIntermediary, $label, false);
                $packageJptf = $this->get_package($packageSlug, "jptf", false, $label);
            } else {
                $packageJptf = $this->get_package($packageSlug, 'jptf', false, $label);
                if (isset($packageJptf[0]['/']['canonical']) && !is_null($packageJptf[0]['/']['canonical'])) {
                    $canonicalVersion = $packageJptf[0]['/']['canonical'];
                    $this->updateCanonical_txt($accountSlug . '/' . $slug, $canonicalVersion);
                }
            }
            if (true === $asHash) {
                $packageIntermediary = $this->get_package($accountSlug . '/' . $slug, null, false, $label);
                $packageJptf = $this->as_jptf_hashOnly($packageIntermediary);
            }
        } catch (\Exception $e) {
            throw $e;
        }

        return $packageJptf;
    }

    /**
     * Get file jptf package representation.
     *
     * @param  string $filePath    is the file location.
     * @param  string $accountSlug is the user account name.
     * @param  string $slug        is the package name.
     * @param  string $label       is the package version.
     * @return array file differences.
     */
    function getFile($filePath, $accountSlug, $slug, $label = 'draft') {
        $network = $this->network;
        $packageSlug = $accountSlug . '/' . $slug;
        $draft = false;
        $backend = $this->backend;
        $packageStatus = $backend->package_exists($packageSlug, $label);
        if ($packageStatus === false) {
            throw new \Exception('{"status_code":"500", "message":"Package ' . $slug . ' does not exist."}');
        }
        if ($label === 'draft') {
            $draft = true;
            $label = null;
        }
        if ($filePath === ".hash") {
            $packageLocation = $backend->package_exists($packageSlug);
            $packageHash = $backend->get_local_packageHash($packageLocation);

            return $packageHash;
        }

        $packageIntermediary = $this->get_package($packageSlug, null, $draft, $label);
        $localfileJptf = $this->createPartialJptf(array($filePath), $packageIntermediary, $accountSlug . '/' . $slug);
        $localfileJptf = $localfileJptf['0']['/']['content'];
        $localfileData = $this->_getfileData($localfileJptf, $filePath);
        $hubfileJptf = $this->getDetailDocument($accountSlug, $slug, $filePath);
        $hubfileJptf = $hubfileJptf['0']['/']['content'];
        $hubfileData = $this->_getfileData($hubfileJptf, $filePath);
        $content = array(
            'local' => $localfileData["content"],
            'remote' => $hubfileData["content"],
            'local_hash' => $localfileData["hash"],
            'remote_hash' => $hubfileData["hash"]
        );

        return $content;
    }

    /**
     * Retrieve package jptf from Hub.
     *
     * @param  string $slug  is package name.
     * @param  string $label is package version.
     * @return array package jptf representation.
     */
    function install_package($packageSlug, $label) {
        $network = $this->network;
        $backend = $this->backend;
        $accountSlug = $backend->getCurrentAccount();
        $format = 'jptf';
        if (strpos($packageSlug, '/')) {
            $account_identifier = explode('/', $packageSlug);
            $accountSlug = $account_identifier[0];
            $slug = $account_identifier[1];
            try {
                $packageJptf = $network->download($accountSlug, $slug, $label);
            } catch (\Exception $e) {
                throw $e;
            }
            $packageJptf = json_encode($packageJptf);
            $packageJptf = json_decode($packageJptf, true);
            $canonicalVersion = $packageJptf[0]['/']['canonical'];
            if (isset($canonicalVersion) && !is_null($canonicalVersion)) {
                $this->updateCanonical_txt($accountSlug . '/' . $slug, $canonicalVersion);
            }
            $packageIntermediary = $this->deserialize_jptf($packageJptf);
            $this->save_local($packageSlug, $packageIntermediary, $label, false);
        }

        return $packageJptf;
    }

    /**
     * Get package as Hash only
     *
     * @param  string            $accountSlug is the user account name.
     * @param  string            $slug        is package name.
     * @param  string[optional]  $label       is package version.
     * @param  boolean[optional] $draft       if package is draft.
     * @return array package jptf representation.
     */
    function package_asHash($accountSlug, $slug, $label = null, $draft = false) {
        $network = $this->network;
        if ($draft === true && !isset($label)) {
            $packageJptf = $network->downloadDraft($accountSlug, $slug, "1");
        } else {
            $packageJptf = $network->download($accountSlug, $slug, $label, "1");
        }

        return $packageJptf;
    }

    /**
     * Get package object from Hub and save it to locally by calling controller get_package method.
     *
     * @param  string            $slug  is package name.
     * @param  string[optional]  $label is package version.
     * @param  boolean[optional] $draft if package is draft.
     * @param  boolean[optional] $force for forcefully package download.
     * @return array as either package jptf representation or differences for package conflicts.
     */
    function pull_package($slug, $label = null, $draft = false, $force = false) {
        $network = $this->network;
        $backend = $this->backend;
        $account_slug = $backend->getCurrentAccount();
        if (strpos($slug, '/')) {
            $packageSlug = $slug;
            $accountIdentifier = explode('/', $slug);
            $account_slug = $accountIdentifier[0];
            $slug = $accountIdentifier[1];
        } else {
            $packageSlug = $account_slug . '/' . $slug;
        }
        if ($draft === true && !isset($label)) {

            $hubpackageJptf = $network->downloadDraft($account_slug, $slug);
            $label = 'draft';
            $trim = true;
        } else {
            $hubpackageJptf = $network->download($account_slug, $slug, $label);
            $trim = false;
        }

        $hubpackageJptf = json_encode($hubpackageJptf);
        $hubpackageJptf = json_decode($hubpackageJptf, true);
        if (isset($hubpackageJptf[0]['/']['canonical']) && !is_null($hubpackageJptf[0]['/']['canonical'])) {
            $canonicalVersion = $hubpackageJptf[0]['/']['canonical'];
            $this->updateCanonical_txt($account_slug . '/' . $slug, $canonicalVersion);
        }
        $packageJptf = $hubpackageJptf;
        $hubpackageIntermediary = $this->deserialize_jptf($hubpackageJptf);
        if ($force === true && !empty($hubpackageJptf)) {
            $this->save_local($packageSlug, $hubpackageIntermediary, $label, $trim);
            $localpackageIntermediary = $this->get_package($packageSlug, null, $draft, $label);
            try {
                $this->download_dependencies($localpackageIntermediary->data, 'weak', $packageSlug);
            } catch (\Exception $e) {
                throw $e;
            }
        } else {
            $localpackageIntermediary = $this->get_package($packageSlug, null, $draft, $label);
            try {
                $this->download_dependencies($localpackageIntermediary->data, null, $packageSlug);
            } catch (\Exception $e) {
                throw $e;
            }
            $differences = $this->compare_package($packageSlug, $hubpackageIntermediary, $localpackageIntermediary, 'pull');
            if (isset($differences['force']) && $differences['force']) {
                foreach ($differences['data'] as $key => $value) {
                    if (strpos($value, '::add') !== false) {
                        $removePath = explode('::add', $value);
                        $removePath = $removePath[0] . $removePath[1];
                        $filePath = $removePath;
                    } else {
                        $filePath = $value;
                    }
                    $filePath = ltrim($filePath, '/');
                    $backend->delete_local_to_syncHub($packageSlug, $filePath);
                }
                $this->save_local($packageSlug, $hubpackageIntermediary->data, $label, $trim);
                $localpackageIntermediary = $this->get_package($packageSlug, null, true);
                $backend->save_packageHash($packageSlug, $localpackageIntermediary);
                return $differences;
            } elseif (isset($differences['flag'])) {
                $flag = $differences['flag'];
                $differences = $differences['data'];
                $fileList = array(
                    'fileList' => $differences,
                    'draft' => array(),
                    'flag' => true,
                );
            } else {
                $fileList = array(
                    'fileList' => $differences,
                    'draft' => array(),
                );
            }
            return $fileList;
        }

        return $packageJptf;
    }

    /**
     * Merge partial jptf to package object.
     *
     * @param  string            $accountSlug is account name.
     * @param  string            $slug        is package name.
     * @param  array             $content     is list of file jptf need to merge.
     * @param  boolean[optional] $draft       if package is draft.
     * @param  string[optional]  $label       is package version.
     * @return object package Intermediary representation.
     */
    function mergePackage($accountSlug, $slug, $content, $draft = true, $label = null) {
        $packageSlug = $accountSlug . '/' . $slug;
        $backend = $this->backend;
        $mergedPackageObject = array();
        $network = $this->network;
        if ($draft === true && !isset($label)) {
            $packageJptf = $network->downloadDraft($accountSlug, $slug);
            $label = 'draft';
        } else {
            $packageJptf = $network->download($accountSlug, $slug, $label);
        }
        $packageJptf = json_encode($packageJptf);
        $packageJptf = json_decode($packageJptf, true);
        $canonicalVersion = $packageJptf[0]['/']['canonical'];
        if (isset($canonicalVersion) && !is_null($canonicalVersion)) {
            $this->updateCanonical_txt($accountSlug . '/' . $slug, $canonicalVersion);
        }
        $hubpackageIntermediary = $this->deserialize_jptf($packageJptf);

        foreach ($content as $contentKey => $contentValue) {
            $filePath = array_keys($content[$contentKey]);
            $filePath = $filePath[0];
            $fileData['data'] = $content[$contentKey][$filePath];
            $filePath = ltrim($filePath, '/');
            if (!empty($fileData['data'])) {
                $file_extension = basename($filePath);
                $file_extension = explode('.', $file_extension);
                $file_extension = $file_extension[count($file_extension) - 1];
                $fileData['type'] = $file_extension;
                $filePath = explode('/', $filePath);
                $mergedPackageObject = $this->_mergeData($filePath, $fileData);
                $this->save_local($packageSlug, $mergedPackageObject->data, $label, true);
            } else {
                $backend->delete_local_to_syncHub($packageSlug, $filePath);
                $mergedPackageObject = array();
            }
        }
        $backend->save_packageHash($packageSlug, $hubpackageIntermediary);

        return $mergedPackageObject;
    }

    /**
     * Push package object to Hub.
     *
     * @param  string           $accountslug          is user account name
     * @param  string           $slug                 is package name.
     * @param  boolean          $force                for forcefully package download.
     * @param  string[optional] $packageHash          is package Hash.
     * @param  array            $replaceFromLocalList is the file list which has conflicts.
     * @return array as file differences conflict else partial jptf representation.
     */
    function push_package($accountSlug, $slug, $force, $packageHash = null, $replaceFromLocalList = null) {
        $network = $this->network;
        $backend = $this->backend;
        $accountSlug = $backend->getCurrentAccount();
        if (strpos($slug, '/')) {
            $packageSlug = $slug;
            $accountIdentifier = explode('/', $slug);
            $accountSlug = $accountIdentifier[0];
            $slug = $accountIdentifier[1];
        } else {
            $packageSlug = $accountSlug . '/' . $slug;
        }

        try {
            $hubpackageJptf = $network->downloadDraft($accountSlug, $slug, '1');
            $hubpackageJptf = json_encode($hubpackageJptf);
            $hubpackageJptf = json_decode($hubpackageJptf, true);
            $canonicalVersion = $hubpackageJptf[0]['/']['canonical'];
            if (isset($canonicalVersion) && !is_null($canonicalVersion)) {
                $this->updateCanonical_txt($accountSlug . '/' . $slug, $canonicalVersion);
            }
            if ($force === true && !empty($hubpackageJptf)) {
                $packageJptf = $hubpackageJptf;
                $packageIntermediary = $this->deserialize_jptf($packageJptf);
                $this->save_local($packageSlug, $packageIntermediary, 'draft', true);
            } elseif (isset($replaceFromLocalList) && !empty($packageHash)) {
                $localpackageIntermediary = $this->get_package($packageSlug, null, true, null, false, true);
                $invalidList = $backend->invalidFileDirectories();
                if (!empty($invalidList)) {
                    $backend->invalidFileDirectories(array(), true);
                    throw new \Exception(json_encode(array_merge(array("code" => 4035, "message" => "Invalid File/Directory"), array("data" => $invalidList))));
                }
                $partialJptf = $this->createPartialJptf($replaceFromLocalList, $localpackageIntermediary, $accountSlug . '/' . $slug, null, 'push');
                if (!empty($partialJptf['list'])) {
                    $partialJptf['list'] = array_unique($partialJptf['list']);
                    $emptyDirectories = $this->createPartialJptf($partialJptf['list'], $localpackageIntermediary, $accountSlug . '/' . $slug);
                    $partialJptf['data'][0]['/']['content'] = array_merge_recursive($emptyDirectories[0]['/']['content'], $partialJptf['data'][0]['/']['content']);
                }
                $partialJptf = $partialJptf['data'];
                $packageJptf = $network->updateDraft($accountSlug, $slug, $packageHash, $partialJptf);
                $backend->save_packageHash($packageSlug, $packageHash);
                return array(
                    'package' => $packageJptf,
                    'jptf' => $partialJptf,
                );
            } else {
                $localpackageIntermediary = $this->get_package($packageSlug, null, true);
                $hubpackageIntermediary = $this->deserialize_jptf($hubpackageJptf, false);
                $differences = $this->compare_package($packageSlug, $hubpackageIntermediary, $localpackageIntermediary, 'push');
                if (isset($differences['flag'])) {
                    $flag = $differences['flag'];
                    $differences = $differences['data'];
                    $fileList = array(
                        'fileList' => $differences,
                        'packageHash' => $hubpackageIntermediary->hash,
                        'flag' => true,
                    );
                } elseif (isset($differences['force'])) {
                    $fileList = array(
                        'fileList' => $differences['data'],
                        'packageHash' => $hubpackageIntermediary->hash,
                        'force' => true,
                    );
                } else {
                    $fileList = array(
                        'fileList' => $differences,
                        'packageHash' => $hubpackageIntermediary->hash,
                    );
                }

                return $fileList;
            }
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * push image to hub.
     *
     * @param  string $accountslug is user account name
     * @param  string $slug        is package name.
     * @param  array  $tokenList   are the image tokens.
     * @return array error if encountered.
     */
    function uploadMedia($accountSlug, $slug, $tokenList) {
        $network = $this->network;
        $backend = $this->backend;
        $result = array();
        $error = array();

        foreach ($tokenList as $key => $value) {
            $filePath = $backend->get_mediaPath($accountSlug, $slug, $key);
            if (is_object($filePath)) {
                $media = $backend->get_mediaSplobject($filePath);
                $logInfo = basename($media['path']);
                $hash = sha1_file($media['path']);
                $splfileObject = new \SplFileObject($media['path']);
            } else {
                $logInfo = basename($filePath);
                $hash = sha1_file($filePath);
                $splfileObject = new \SplFileObject($filePath);
            }
            try {
                $result['packageHash'] = $network->uploadMedia($accountSlug, $slug, $hash, $key, $value, $splfileObject);
            } catch (\Exception $e) {
                // Dont throw exception So as to continue execution for rest of the images to upload.
                // However here we will be logging for the imagename for any issue with image upload to hub.
                error_log($logInfo);
                if (is_string($e->getMessage())) {
                    $error = json_decode($e->getMessage(), true);
                }
                if ($error['status_code'] === '4035') {
                    $result['data'][$logInfo] = $error['errors']['image'][0];
                } elseif (isset($error['message'])) {
                    $result['data'][$logInfo] = $error['message'];
                } else {
                    $result['data'][$logInfo] = '';
                }
            }
        }

        return $result;
    }

    /**
     * Get package draft.
     *
     * @param  string $accountSlug is the user account.
     * @param  string $slug        is package name.
     * @return array draft package jptf representation.
     */
    function draftGet($accountSlug, $slug) {
        $backend = $this->backend;
        $network = $this->network;
        $packageSlug = $accountSlug . '/' . $slug;
        $path = $backend->package_exists($packageSlug);
        if ($path != false) {
            $packageIntermediary = $backend->get_package($path);
            $packageJptf = $this->as_jptf($packageIntermediary, $accountSlug . '/' . $slug);
        } else {
            $packageJptf = $network->getDraft($accountSlug, $slug);
        }

        return $packageJptf;
    }

    /**
     * Get package draft details.
     *
     * @param  string  $accountSlug is the user account.
     * @param  string  $slug        is package name.
     * @param  boolean $verbose     is optional arg for more description.
     * @param  string  $session     is current session.
     * @return array package details.
     */
    function getPackageDetails($accountSlug, $slug, $verbose, $session) {
        $backend = $this->backend;
        $packageSlug = $accountSlug . '/' . $slug;
        $path = $backend->package_exists($packageSlug);
        if ($path != false) {
            $Details = $backend->get_Offline_Package_Details($packageSlug, $session, $path);
        } else {
            $network = $this->network;
            $Details = $network->getPackageDetails($accountSlug, $slug, $verbose);
        }

        return $Details;
    }

    /**
     * Retrieve all accounts from the system that the user has access to.
     *
     * @param  string  $slug    is package name.
     * @param  boolean $verbose is the parameter to provide detail view of hyperlink objects.
     * @return array all member accounts list.
     */
    function getAccountList($slug, $verbose) {
        $network = $this->network;
        try {
            $memberList = $network->getAccountList($slug, $verbose);
        } catch (\Exception $e) {
            throw $e;
        }

        return $memberList;
    }

    /**
     * Upload a media file to package's draft.
     *
     * @param string $accountSlug   is the user account.
     * @param string $slug          is package name.
     * @param string $hash          is image hash.
     * @param string $name          is image name.
     * @param string $token         is the token to upload to hub.
     * @param string $SplFileObject is Image Object.          
     */
    function packageUploadMedia($accountSlug, $slug, $hash, $name, $token, $SplFileObject) {
        $network = $this->network;
        try {
            $network->uploadMedia($accountSlug, $slug, $hash, $name, $token, $SplFileObject);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Retrieve list of package version.
     *
     * @param  string $accountSlug is the user account.
     * @param  string $slug        is package name.
     * @return array list of package version.
     */
    function getVersions($accountSlug, $slug) {
        $network = $this->network;
        try {
            $versionList = $network->getVersions($accountSlug, $slug);
        } catch (\Exception $e) {
            throw $e;
        }

        return $versionList;
    }

    /**
     * Logout user and discard sessionKey.
     */
    function logout() {
        try {
            $network = $this->network;
            $backend = $this->backend;
            $network->logout();
            $backend->setCurrentAccount('anonymous');
            $backend->setCurrentUser('anonymous');
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Get Details of package rating submitted by any user.
     *
     * @param  string  $accountslug is user account name.
     * @param  string  $slug        is the current user account slug.
     * @param  string  $ratingPk    is the current user account slug.
     * @param  boolean $verbose     parameter to provide detail view of hyperlink objects.
     * @return array rating details.
     */
    function getRatingDetails($accountSlug, $slug, $ratingPk, $verbose) {
        try {
            $network = $this->network;
            $ratingDetails = $network->getRatingDetails($accountSlug, $slug, $ratingPk, $verbose);
        } catch (\Exception $e) {
            throw $e;
        }

        return $ratingDetails;
    }

    /**
     * Update rating of package.
     *
     * @param string $accountslug is user account name.
     * @param string $slug        is the current user account slug.
     * @param string $ratingPk    is the current user account slug.
     * @param array  $body        request data used to update rating.
     */
    function updateRating($accountSlug, $slug, $ratingPk, $body) {
        try {
            $network = $this->network;
            $network->updateRating($accountSlug, $slug, $ratingPk, $body);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Get list of licenses associated with package.
     *
     * @param  string $accountslug is user account name.
     * @param  string $slug        is the current user account slug.
     * @param  string $label       is label for the version.
     * @return array license details.
     */
    function getLicenses($accountSlug, $slug, $label) {
        try {
            $network = $this->network;
            $licenceDetails = $network->getLicenses($accountSlug, $slug, $label);
        } catch (\Exception $e) {
            throw $e;
        }

        return $licenceDetails;
    }

    /**
     * delete a package license.
     *
     * @param string $accountslug is user account name.
     * @param string $slug        is the current user account slug.
     * @param string $label       is label for the version.
     * @param string $name        is name of the license.
     */
    function removeLicense($accountSlug, $slug, $label, $name) {
        try {
            $network = $this->network;
            $network->removeLicense($accountSlug, $slug, $label, $name);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Update price of package license.
     *
     * @param string $accountslug is user account name.
     * @param string $slug        is the current user account slug.
     * @param string $label       is label for the version.
     * @param string $name        is name of the license.
     * @param array $body        is request data used to update license price.
     */
    function updateLicense($accountSlug, $slug, $label, $name, $body) {
        try {
            $network = $this->network;
            $network->updateLicense($accountSlug, $slug, $label, $name, $body);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * API to acquire package between accounts.
     *
     * @param  string $accountslug is user account name.
     * @param  string $slug        is the current user account slug.
     * @param  string $body        is request data used to update license price.
     * @return array package object.
     */
    function acquirePackage($accountSlug, $slug, $body) {
        try {
            $network = $this->network;
            $transfer = isset($body['type']) && 'transfer' == $body['type'] ? true : false;
            if ($transfer)
                $this->isSync($accountSlug, $slug, $body['type']);
            $acquireDetail = $network->acquirePackage($accountSlug, $slug, $body);

            if ($transfer):
                global $DEPENDENCY_PATH;
                $backend = $this->backend;
                $packageStatus = $backend->package_exists($accountSlug . '/' . $slug);
                $libPath = $backend->blended_theme_dir() . "/" . $DEPENDENCY_PATH . "/" . $accountSlug . "/" . $slug;
                if ($packageStatus !== false)
                    $this->localDelete($accountSlug, $slug);
                if (file_exists($libPath))
                    $backend->localDelete($libPath, null, false);
            endif;
        } catch (\Exception $e) {
            throw $e;
        }

        return $acquireDetail;
    }

    /**
     * API to accept package.
     * To share,transfer, get, buy, extend and clone a package.
     * @param string $accountSlug
     * @param string $slug
     * @param array $body
     * @return type
     * @throws Exception
     */
    function acceptAcquisition($accountSlug, $slug, $body) {
        try {
            $network = $this->network;
            $package = $network->acceptAcquisition($accountSlug, $slug, $body);
        } catch (\Exception $e) {
            throw $e;
        }

        return $package;
    }

    /**
     * API to reject shared package.
     *
     * @param  string $accountslug is user account name.
     * @param  string $slug        is the current user account slug.
     * @param  string $body        is request data used to update license price.
     */
    function rejectAcquisitionInvite($accountSlug, $slug, $body) {
        try {
            $network = $this->network;
            $network->rejectAcquisitionInvite($accountSlug, $slug, $body);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Search accounts by starting keywords of account name.
     *
     * @param  string $accountslug is user account name.
     * @return array user object.
     */
    function searchAccounts($accountSlug) {
        /**
         * enable this code, after react changes only
         *
          try {
          $network = $this->network;

          $package = $network->searchAccounts($accountSlug);

          //Get local accounts if accontslug is less than equal to two
          if (strlen($accountSlug) <= 2) :
          $localAccounts = $this->searchLocalAccounts($accountSlug);
          $items = array();
          if ($localAccounts && $package["items"]):
          foreach ($localAccounts as $account):
          $key = array_search($account, array_column($package["items"], "user_name"));
          if (false !== $key)
          $items[] = $package["items"][$key];

          endforeach;
          endif;

          //Filtered Package
          $package["items"] = $items;

          endif;
          } catch (\Exception $e) {
          throw $e;
          }

          return $package;
         */
        try {
            $network = $this->network;
            //Get local accounts if accontslug is less than equal to two
            // if (strlen($accountSlug) <= 2) :
            //    $package = $this->searchLocalAccounts($accountSlug);
            // else:
            $package = $network->searchAccounts($accountSlug);
            // endif;
        } catch (\Exception $e) {
            throw $e;
        }
        return $package;
    }

    /**
     * 
     * @global string $THEME_DIRECTORY
     * @global string $ROOT_DIRECTORY
     * @param string $accountSlug
     * @return array|void
     */
    function searchLocalAccounts($accountSlug) {
        if (empty($accountSlug)):
            return;
        endif;

        global $THEME_DIRECTORY, $ROOT_DIRECTORY;
        $userLocation = $THEME_DIRECTORY . '/' . $ROOT_DIRECTORY;
        $users = array_filter(scandir($userLocation), function($item) use ($userLocation) {
            return (is_dir($userLocation . "/" . $item) && "anonymous" !== $item && stripos($item, ".") === false);
        });

        if (!$users)
            return;

        $searches = array();
        foreach ((array) $users as $user):
            if (false !== stripos($user, $accountSlug))
                $searches[] = $user;
        endforeach;
        return $searches;
    }

    /**
     * API to acquire package between accounts.
     *
     * @param  string $accountslug is user account name.
     * @param  string $slug        is the current user account slug.
     * @param  array $body        is request data used to update license price.
     * @return array package object.
     */
    function getAcquiredPackageAccountList($accountSlug, $slug, $share) {
        try {
            $network = $this->network;
            $package = $network->getAcquiredPackageAccountList($accountSlug, $slug, $share);
        } catch (\Exception $e) {
            throw $e;
        }

        return $package;
    }

    /**
     * Revoke shared pakage from user.
     *
     * @param  string $accountslug is user account name.
     * @param  string $slug        is the current user account slug.
     * @param  array $body        is request data used to update license price.
     * @return array package object.
     */
    function revokeAcquiredPackage($accountSlug, $slug, $body) {
        try {
            $network = $this->network;
            $package = $network->revokeAcquiredPackage($accountSlug, $slug, $body);
        } catch (\Exception $e) {
            throw $e;
        }

        return $package;
    }

    /**
     * Get list of user's account pending users.
     *
     * @param  string           $slug    is the current user account slug.
     * @param  string[optional] $verbose parameter to provide detail view of hyperlink objects.
     * @return array pending user account details.
     */
    function getPendingUsers($slug, $verbose) {
        try {
            $network = $this->network;
            $package = $network->getPendingUsers($slug, $verbose);
        } catch (\Exception $e) {
            throw $e;
        }

        return $package;
    }

    /**
     * Invite user to join account.
     *
     * @param string $slug is the current user account slug.
     * @param array $body the content of the request used to add admin into user account.
     */
    function inviteUser($slug, $body) {
        try {
            $network = $this->network;
            $network->inviteUser($slug, $body);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Delete a pending user from user account.
     *
     * @param string $slug is the current user account slug.
     * @param array $body the content of the request used to add admin into user account.
     */
    function removePendingUser($email, $slug) {
        try {
            $network = $this->network;
            $network->removePendingUser($email, $slug);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Update User Account.
     *
     * @param string $slug is the current user account slug.
     * @param array $body request data used to update user account.
     */
    function updateAccount($slug, $body) {
        try {
            $network = $this->network;
            $network->updateAccount($slug, $body);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Resend Email Verification link to verifiy account if already not verified.
     *
     * @param array $body request email
     */
    function resendAccountVerificationEmail($body) {
        try {
            $network = $this->network;
            $network->resendAccountVerificationEmail($body);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Return package dependencies.
     *
     * @param  string           $accountSlug is the user account slug.
     * @param  string           $slug        is the slug of package to return.
     * @param  string[optional] $verbose     parameter to provide detail view of hyperlink objects.
     * @return array  package dependencies.
     */
    function getDependencies($accountSlug, $slug, $verbose) {
        try {
            $network = $this->network;
            $packageDependencies = $network->getDependencies($accountSlug, $slug, $verbose);
        } catch (\Exception $e) {
            throw $e;
        }

        return $packageDependencies;
    }

    /**
     * Access draft documents and directory.
     *
     * @param  string $accountSlug is the user account slug.
     * @param  string $slug        is the slug of package to return.
     * @param  string $name        path of the directory. Ex. - meta/config.css
     * @return array draft documents.
     */
    function getDetailDocument($accountSlug, $slug, $name) {
        try {
            $network = $this->network;
            $draftDocument = $network->getDetailDocument($accountSlug, $slug, $name);
        } catch (\Exception $e) {
            throw $e;
        }

        return $draftDocument;
    }

    /**
     * Update Package.
     *
     * @param string $accountSlug is the user account slug.
     * @param string $slug        is the slug of package to return.
     * @param array  $body        is to request data used to update package version.
     */
    function updatePackage($accountSlug, $slug, $body) {
        try {
            $network = $this->network;
            $network->updatePackage($accountSlug, $slug, $body);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Create a new user account.
     *
     * @param  array            $body    is the the content of the request used to create user account.
     * @param  string[optional] $verbose parameter to provide detail view of hyperlink objects.
     * @return array user details.
     */
    function createAccount($body, $verbose) {
        try {
            $network = $this->network;
            $backend = $this->backend;
            $account = $network->createAccount($body, $verbose);
            $backend->make_active($body['user_name'] . '/business_theme');
        } catch (\Exception $e) {
            throw $e;
        }

        return $account;
    }

    /**
     * Detail View of user account.
     *
     * @param  string            $slug    is the slug of package to return.
     * @param  boolean[optional] $verbose parameter to provide detail view of hyperlink objects.
     * @return array account details.
     */
    function getAccountDetails($slug, $verbose) {
        try {
            $network = $this->network;
            $accountDetails = $network->getAccountDetails($slug, $verbose);
        } catch (\Exception $e) {
            throw $e;
        }

        return $accountDetails;
    }

    /**
     * Resend the inivite of shared package.
     *
     * @param  string $accountSlug is account slug.
     * @param  string $slug is package name.
     * @param  array  $body is request body.
     * @return array package details.
     */
    function resendInvite($accountSlug, $slug, $body) {

        try {
            $network = $this->network;
            $package = $network->resendInvite($accountSlug, $slug, $body);
        } catch (\Exception $e) {
            throw $e;
        }

        return $package;
    }

    /**
     * Reset password of user account.
     *
     * @param array $body is the the content of the request used to create user account.
     */
    function resetPassword($body) {
        try {
            $network = $this->network;
            $network->resetPassword($body);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Login user to account.
     *
     * @param  string           $slug    is the slug of package to return.
     * @param  string[optional] $verbose parameter to provide detail view of hyperlink objects.
     * @return array account details.
     */
    function login($body, $wpSession) {
        try {
            $network = $this->network;
            $details = $network->login($body['user_name'], $body['password']);
            $sessionKey = $this->blended_authenticate($details['session_key'], $wpSession);
            $this->blended_session_setter($wpSession);
            setcookie("blended_session_key", $details['session_key'], 0, "/", false, false, true);
            $verbose = true;
            $currentAccount = $network->getCurrentAccount($details['slug'], $verbose);
            $backend = $this->backend;
            $backend->setCurrentAccount($currentAccount['slug']);
            $backend->setCurrentUser($body['user_name']);
            $accountSlug = $this->getCurrentAccount();
            $packages = $network->getPackages($accountSlug);
            $checkConflicts = $this->sync_client_hub($packages);
            $checkConflicts = $this->checkConflicts($checkConflicts);
            $backend->movePackage($checkConflicts);
            $details['session_key'] = $sessionKey;
            $details['packageConflicts'] = $checkConflicts['conflictFound'];
        } catch (\Exception $e) {
            $this->logout();
            throw $e;
        }

        return $details;
    }

    function checkConflicts($checkConflicts) {
        $conflictList = array();
        $checkConflicts['conflictFound'] = array();
        $checkConflicts['dontMove'] = array();
        if (isset($checkConflicts['conflicts'])) {
            $backend = $this->backend;
            $network = $this->network;
            $accountSlug = $this->getCurrentAccount();
            foreach ($checkConflicts['conflicts'] as $slug) {
                $packageIntermediary = $backend->conflictCompare($slug);
                $packages = $network->getPackageInitial();
                foreach ((array) $packages['items'] as $key => $acquisition) {
                    if ($acquisition['name'] === 'blended/' . $slug) {
                        $label = $acquisition['version'];
                        $packageJptf = $network->download('blended', $slug, $label);
                        $packageJptf = json_encode($packageJptf);
                        $packageJptf = json_decode($packageJptf, true);
                    }
                }
                if ($packageJptf[0]['/']['hash'] != $packageIntermediary->hash) {
                    $oldPackagejson = json_decode($packageIntermediary->data['_package.json']->data, true);
                    $checkConflicts['conflictFound'][$accountSlug . '/' . $slug] = $oldPackagejson['title'];
                } else {
                    $checkConflicts['dontMove'][] = ''; //$slug;
                }
            }
        }
        return $checkConflicts;
    }

    /**
     * Create a new package.
     *
     * @param  array            $body    is request data used to update package draft.
     * @param  string[optional] $verbose parameter to provide detail view of hyperlink objects.
     * @return array  package draft.
     */
    function createPackage($body, $verbose) {
        try {
            $network = $this->network;
            $network->createPackage($body, $verbose);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Add a admin user into account.
     *
     * @param string $slug is the current user account slug.
     * @param array $body request data used to update user account.
     */
    function addAccountAdmin($slug, $body) {
        try {
            $network = $this->network;
            $network->addAccountAdmin($slug, $body);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Add a regular user into account.
     *
     * @param string $slug is the current user account slug.
     * @param array $body request data used to update user account.
     */
    function addAccountUser($slug, $body) {
        try {
            $network = $this->network;
            $network->addAccountUser($slug, $body);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /*
     * ----------------------------------------------------------------------------------------------------------------------------------------
     * METHODS RELATED TO SETTINGS APP.
     * ----------------------------------------------------------------------------------------------------------------------------------------
     */

    /**
     * Get theme setting required to render theme.
     *
     * @return theme settings form stored configuration.
     */
    function get_theme_settings() {
        try {
            $backend = $this->backend;
            $settings = $backend->get_theme_settings();
        } catch (\Exception $e) {
            throw new \Exception('{ "status_code":"500", "message":"Blended Exception in getting theme settings.." }');
        }

        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) {
        try {
            $backend = $this->backend;
            $settings = $backend->save_theme_settings($newSettings);
        } catch (\Exception $e) {
            throw new \Exception('{ "status_code":"500", "message":"Blended Exception while saving theme setting." }');
        }

        return $settings;
    }

    /**
     * Get WordPress specific setting required to render theme.
     *
     * @return array WordPress settings.
     */
    function get_wordpress_settings() {
        try {
            $backend = $this->backend;
            $settings = $backend->get_wordpress_settings();
        } catch (\Exception $e) {
            throw new \Exception('{ "status_code":"500", "message":"Blended Exception in getting WordPress settings." }');
        }

        return $settings;
    }

    /**
     * Save WordPress settings onto current configuration.
     *
     * @param  array $newSettings is the set of WordPress settings need to be updated.
     * @return array saved WordPress settings.
     */
    function save_wordpress_settings($newSettings) {
        try {
            $backend = $this->backend;
            $settings = $backend->save_wordpress_settings($newSettings);
        } catch (\Exception $e) {
            throw new \Exception('{ "status_code":"500", "message":"Blended Exception with saving WordPress settings." }');
        }

        return $settings;
    }

    /**
     * Get navigation specific setting required to render theme.
     *
     * @return array navigation settings.
     */
    function get_navigation_settings() {
        try {
            $backend = $this->backend;
            $settings = $backend->get_navigation_settings();
        } catch (\Exception $e) {
            throw new \Exception('{ "status_code":"500", "message":"Blended Exception in getting navigation settings." }');
        }

        return $settings;
    }

    /**
     * Get navigation items
     *
     * @param  string $menu get navigation.
     * @return array menu item.
     */
    function get_nav_menu($menu) {
        try {
            $menuItems = blended_nav_menu($menu);
        } catch (\Exception $e) {
            throw new \Exception('{ "status_code":"500", "message":"Blended Exception in getting navigation items." }');
        }

        return $menuItems;
    }

    /**
     * Get nav default settings for admin
     *
     * @return array default navigations for admin nav settings.
     */
    function get_nav_list() {
        try {
            $backend = $this->backend;
            $settings = $backend->get_nav_list();
        } catch (\Exception $e) {
            throw new \Exception('{ "status_code":"500", "message":"Blended Exception in getting navigation settings." }');
        }

        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) {
        try {
            $backend = $this->backend;
            $settings = $backend->save_navigation_settings($newSettings);
        } catch (\Exception $e) {
            throw new \Exception('{ "status_code":"500", "message":"Blended Exception with navigation settings." }');
        }

        return $settings;
    }

    /**
     * Get Hub Specific setting for getting and putting updates to user.
     *
     * @return array hub settings.
     */
    function get_hub_settings() {
        try {
            $backend = $this->backend;
            $settings = $backend->get_hub_settings();
        } catch (\Exception $e) {
            throw new \Exception('{ "status_code":"500", "message":"Blended Exception in getting hub settings." }');
        }

        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) {
        try {
            $backend = $this->backend;
            $settings = $backend->save_hub_settings($newSettings);
        } catch (\Exception $e) {
            throw new \Exception('{ "status_code":"500", "message":"Blended Exception while saving hub settings." }');
        }

        return $settings;
    }

    /**
     * Authenticating WordPress user session on every request and sets user session.
     *
     * @param  string $hubSession is the current Hub logged-in user ID.
     * @param  string $wpSession  is the current WordPress logged-in user ID.
     * @return string WordPress user sessionKey.
     */
    function blended_authenticate($hubSession, $wpSession) {
        try {
            $backend = $this->backend;
            $wpSession = $backend->blended_authenticate($hubSession, $wpSession);
        } catch (\Exception $e) {
            throw new \Exception('{ "status_code":"500", "message":"Blended Exception in Authenticating valid user session." }');
        }

        return $wpSession;
    }

    /**
     * Store Hub user session on host.
     *
     * @param  string            $sessionKey is the current Hub logged-in user session.
     * @param  boolean[optional] $noUser     false for anonymous user.
     * @return string hub user sessionKey else offline if user not loged-in to Hub.
     */
    function blended_session_setter($sessionKey, $noUser = false) {
        try {
            $backend = $this->backend;
            $network = $this->network;
            $session = $backend->blended_session_setter($sessionKey, $noUser);
            $network->sessionKey = $session;
        } catch (\Exception $e) {
            throw $e;
        }

        return $network->sessionKey;
    }

    /**
     * Set Hub current account from Hub settings on to current stored configuration.
     *
     * @param string $slug is the current user account slug.
     * @param array  $body is the current user account slug.
     */
    function setCurrentAccount($slug, $body) {
        try {
            $backend = $this->backend;
            $network = $this->network;
            $backend->setCurrentAccount($body['slug']);
            $network->setCurrentAccount($slug, $body);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Get current logged-in account settings on to current stored configuration.
     *
     * @return string saved user slug.
     */
    function getCurrentAccount() {
        try {
            $backend = $this->backend;
            $accountSlug = $backend->getCurrentAccount();
        } catch (\Exception $e) {
            throw $e;
        }

        return $accountSlug;
    }

    /**
     * Get current logged-in user settings on to current stored configuration.
     *
     * @return string saved user slug.
     */
    function getCurrentUser() {
        try {
            $backend = $this->backend;
            $accountSlug = $backend->getCurrentUser();
        } catch (\Exception $e) {
            throw $e;
        }

        return $accountSlug;
    }

    /**
     * Get acquire package details.
     *
     * @return array package details.
     */
    function sharedPackageListGet() {
        try {
            $network = $this->network;
            $packageDetail = $network->sharedPackageListGet();
        } catch (\Exception $e) {
            throw $e;
        }

        return $packageDetail;
    }

    /**
     * Get package publications
     * 
     * @param string $accountSlug
     * @param string $slug
     * @return array package publications.
     * @throws \Exception
     */
    function getPublication($accountSlug, $slug) {
        try {
            $network = $this->network;
            $packageDetail = $network->getPublication($accountSlug, $slug);
        } catch (\Exception $e) {
            throw $e;
        }

        return $packageDetail;
    }

    /**
     * Update package publications
     * 
     * @param string $accountSlug
     * @param string $slug
     * @param string $sessionKey
     * @param array $body
     * @return array package publications.
     * @throws \Exception
     */
    function updatePublication($accountSlug, $slug, $commit, $body) {
        try {
            $network = $this->network;
            $packageDetail = $network->updatePublication($accountSlug, $slug, $commit, $body);
        } catch (\Exception $e) {
            throw $e;
        }

        return $packageDetail;
    }

    /**
     * Validate package publications
     * 
     * @param string $accountSlug
     * @param string $slug
     * @param string $sessionKey
     * @param array $body
     * @return array package publications.
     * @throws \Exception
     * @todo check return type after React test
     */
    function validatePublication($accountSlug, $slug, $body) {
        try {
            $network = $this->network;
            $response = $network->validatePublication($accountSlug, $slug, $body);
        } catch (\Exception $e) {
            throw $e;
        }

        return $response;
    }

    /**
     * Get package version bundles
     * 
     * @param string $accountSlug
     * @param string $slug
     * @return array package version bundles
     * @throws \Exception
     */
    function getVersionBundles($accountSlug, $slug) {
        try {
            $network = $this->network;
            $packageDetail = $network->getVersionBundles($accountSlug, $slug);
        } catch (\Exception $e) {
            throw $e;
        }

        return $packageDetail;
    }

    /*
     * ----------------------------------------------------------------------------------------------------------------------------------------
     * HELPER METHODS
     * ----------------------------------------------------------------------------------------------------------------------------------------
     */

    /**
     * Merge partial jptf data to package object.
     *
     * @param  array  $filePath is path to the file splitted in array.
     * @param  string $fileData is content to the file.
     * @return array file jptf representation.
     */
    function _mergeData($filePath, $fileData) {
        $objectDictionary = array();

        while (count($filePath)) {
            $objectDictionary = array(
                array_pop($filePath) => $objectDictionary,
            );
        }
        $jptfData = $this->_setStructure($objectDictionary, $fileData);
        $fileJptf[0]['/']['content'] = array(
            $jptfData,
        );
        $fileJptf[0]['/']['type'] = 'directory';
        $fileJptf[0]['/']['hash'] = null;
        $fileIntermediary = $this->deserialize_jptf($fileJptf);

        return $fileIntermediary;
    }

    /**
     * ref_get extracts the ref path string and creates resolve_path that has the path from packageObject to the location where the ref points
     * path is extracted based on what kind of ref it is, like #/ (same file) ref or @ (dependency) ref
     * It forwards it to get_value_at_path function
     *
     * @param  string $refValue       is the string that contains the ref pointer
     * @param  object $packageObject  is the package object.
     * @param  object $jsonFile       is the current file whose refrences are to be resolved.
     * @param  object $dependencyDict is the package dependency stack object.
     * @param  object $visitedSet     is the list of refrences that has been resolved and help to track looping in resolve.
     * @param  string $refPath        is a list that provides the path till the ref (list of keys that lead to the ref)
     * @param  object $refLocation    is the array/dict that contains the ref as the value of some key
     * @param  string $reflocKey      is the key in the array/dict whose value is the ref
     * @param  string $currAlias      is the type of ref : '#', '@' or '/' which led to the curr ref or null if this ref is found independently
     * @return array|string as reference value.
     */
    function _ref_get($refValue, &$packageObject, &$jsonFile, &$dependencyDict, $visitedSet, $refPath, &$refLocation, $reflocKey, $currAlias) {
        if ((strpos($refValue, "#/") === 0) || (strpos($refValue, "#") === 0)) {
            $this->_get_last_json($refPath);
            $path = str_replace('#/', '', $refValue);
            if ($path === '') {
                return $jsonFile;
            }
            $resolvePath = explode('/', $path);
            $resolvePath = array_merge($refPath, $resolvePath);
            if (!is_null($currAlias) && $currAlias === '@') {
                $ref_alias = '@';
            } else {
                $ref_alias = '#';
            }
            $refValue = $this->_get_value_at_path(
                    $dependencyDict, $jsonFile, $packageObject, $resolvePath, $visitedSet, $refLocation, $reflocKey, $ref_alias
            );
            return $refValue;
        } elseif (strpos($refValue, "/") === 0) {
            $refValue = ltrim($refValue, "/");
            $path = str_replace('#', '', $refValue);
            if ($path === '') {
                return $packageObject;
            }
            $resolvePath = explode('/', $path);
            $refValue = $this->_get_value_at_path(
                    $dependencyDict, $jsonFile, $packageObject, $resolvePath, $visitedSet, $refLocation, $reflocKey, '/'
            );
        } elseif (strpos($refValue, "@") === 0) {
            $path = str_replace(
                    array(
                '@',
                '#'
                    ), '', $refValue
            );
            if ($path === '') {
                return $dependencyDict;
            }
            $resolvePath = explode('/', $path);
            $refValue = $this->_get_value_at_path(
                    $dependencyDict, $jsonFile, $packageObject, $resolvePath, $visitedSet, $refLocation, $reflocKey, '@'
            );
        } else {
            $path = str_replace('#', '', $refValue);
            if ($path === '') {
                return $packageObject;
            }
            $resolvePath = explode('/', $path);
            $refValue = $this->_get_value_at_path(
                    $dependencyDict, $jsonFile, $packageObject, $resolvePath, $visitedSet, $refLocation, $reflocKey, '/'
            );
        }

        return $refValue;
    }

    /**
     * Utilizes resolve_path to traverse to the location where the ref points to
     * tempValue will contain the value requested by the ref_get
     * If the final value is again a dict and contains a $ref, it is sent back to resolve_pointer to resolve it
     * If it is a dict but has nor ref directly as a key, it is sent to clear_dep to traverse through the dict DF and resolve all the refs in it
     *
     * @param  object $packageObject  is the package object.
     * @param  object $jsonFile       is the current file whose refrences are to be resolved.
     * @param  object $dependencyDict is the package dependency stack object.
     * @param  object $visitedSet     is the list of refrences that has been resolved and help to track looping in resolve.
     * @param  object $refLocation    is the array/dict that contains the ref as the value of some key.
     * @param  string $reflocKey      is the key in the array/dict whose value is the ref.
     * @param  string $refIdentifier  tells what kind of ref is being resolved : '#', '@' or '/'.
     * @return array|string as referenced value being referred.
     */
    function _get_value_at_path(&$dependencyDict, &$jsonFile, &$packageObject, $resolvePath, $visitedSet, &$refLocation, $reflocKey, $refIdentifier) {
        if ($refIdentifier === '@') {
            $tempValue = &$dependencyDict;
        } else {
            $tempValue = &$packageObject;
        }
        $prevPtr = null;
        $currPath = [];
        $currentjsonFile = $jsonFile;

        foreach ($resolvePath as $key => $value) {
            try {
                if (!is_null($tempValue) && array_key_exists('$ref', $tempValue)) {
                    if ((strpos($tempValue['$ref'], "#/") === 0) || (strpos($tempValue['$ref'], "#") === 0)) {
                        $newresolvePath = $resolvePath;
                        $this->_get_last_json($newresolvePath);
                        $setVal = array();
                        $tempValue = $this->resolve_pointer(
                                $tempValue, $packageObject, $dependencyDict, $setVal, $currentjsonFile, $newresolvePath, $prevPtr, $this->_get_last_element($currPath), $refIdentifier
                        );
                    } else {
                        $setVal = array();
                        $tempValue = $this->resolve_pointer(
                                $tempValue, $packageObject, $dependencyDict, $setVal, $currentjsonFile, $resolvePath, $prevPtr, $this->_get_last_element($currPath), null
                        );
                    }
                    $currKey = $this->_get_last_element($currPath);
                    $prevPtr[$currKey] = $tempValue;
                }
                if (substr($value, -5) === ".json") {
                    $currentjsonFile = $tempValue[$value];
                }
                $prevPtr = &$tempValue;
                array_push($currPath, $value);
                if (array_key_exists($value, $tempValue)) {
                    $tempValue = &$tempValue[$value];
                } else {
                    throw new \Exception($value . " not found");
                }
            } catch (\Exception $e) {
                $exceptedreturnValue = "Caught Exception: " . $e->getMessage();
                return $exceptedreturnValue;
            }
        }
        $refLocation[$reflocKey] = $tempValue;
        if (is_array($tempValue)) {
            if (array_key_exists('$ref', $tempValue)) {
                if ((strpos($tempValue['$ref'], "#/") === 0) || (strpos($tempValue['$ref'], "#") === 0)) {
                    $newresolvePath = $resolvePath;
                    $this->_get_last_json($newresolvePath);
                    $tempValue = $this->resolve_pointer(
                            $tempValue, $packageObject, $dependencyDict, $visitedSet, $currentjsonFile, $newresolvePath, $prevPtr, $this->_get_last_element($currPath), $refIdentifier
                    );
                } else {
                    $tempValue = $this->resolve_pointer(
                            $tempValue, $packageObject, $dependencyDict, $visitedSet, $currentjsonFile, $resolvePath, $prevPtr, $this->_get_last_element($currPath), null
                    );
                }
            } else {
                $tempVar = null;
                $tempValue = $this->_clear_dep($tempVar, $packageObject, $dependencyDict, $currentjsonFile, $resolvePath, null, $refIdentifier, true);
            }
        }

        return $tempValue;
    }

    /**
     * Recursive util that clears all the dependencies found in the dict/value requested by the $ref currently being processed
     *
     * @param  object $currentNode     is the current pointer.
     * @param  object $packageObject   is the package object.
     * @param  object $dependencyDict  is the package dependency stack object.
     * @param  object $currentjsonFile is the current file whose refrences are to be resolved.
     * @param  array  $resolvePath     is a list of values of the object that leads to the object being referred
     * @param  string $refPath         is a list that provides the path till the ref (list of keys that lead to the ref)
     * @param  string $currAlias       is the type of ref : '#', '@' or '/' which led to the curr ref or null if this ref is found independently
     * @param  bool   $pathFlag        tells the method if $resolvePath is to be traversed or not
     * @return array|string referenced value being passed.
     */
    function _clear_dep(&$currentNode, &$packageObject, &$dependencyDict, &$currentjsonFile, $resolvePath, $refPath, $currAlias, $pathFlag = false) {
        if (is_null($currentNode)) {
            if (!is_null($currAlias) && $currAlias === '@') {
                $currentNode = & $dependencyDict;
            } else {
                $currentNode = & $packageObject;
            }
        }
        $refpointObject = null;
        if (is_null($refPath)) {
            $refPath = [];
        }
        if ($pathFlag) {

            foreach ($resolvePath as $key => $value) {
                array_push($refPath, $value);
                $currentNode = &$currentNode[$value];
            }
            $refpointObject = &$currentNode;
        }

        foreach ($currentNode as $key => $value) {
            // Keep track of json files so that other objects can look here for reference values.
            array_push($refPath, $key);
            if (substr($key, -5) === ".json") {
                $currentjsonFile = &$currentNode[$key];
            }
            $currentValue = &$currentNode[$key];
            // Look for the key '$ref' to resolve its pointing value.
            if (is_array($currentValue)) {
                if (array_key_exists('$ref', $currentValue)) {
                    $setVal = array();
                    $refPathCopy = $refPath;
                    $refLocation = &$currentNode;
                    try {
                        $currentNode[$key] = $this->resolve_pointer(
                                $currentValue, $packageObject, $dependencyDict, $setVal, $currentjsonFile, $refPathCopy, $refLocation, $key, null
                        );
                    } catch (\Exception $e) {
                        $currentNode[$key] = 'Caught exception: ' . $e->getMessage() . "\n";
                    }
                } else {
                    if (is_null($currentValue)) {
                        continue;
                    }
                    $this->_clear_dep($currentValue, $packageObject, $dependencyDict, $currentjsonFile, null, $refPath, null);
                }
            }
            array_pop($refPath);
        }

        return $refpointObject;
    }

    /**
     * Get version of a package.
     *
     * @param  object $packageIntermediary is account name.
     * @return string package version.
     */
    function _getpackage_version($packageIntermediary) {
        if (isset($packageIntermediary['_package.json'])) {
            $packageJson = json_decode($packageIntermediary['_package.json']->data, true);
            if (isset($packageJson['version']) && !empty($packageJson['version'])) {
                $label = $packageJson['version'];
            } else {
                throw new \Exception('{"status_code":"500", "message":"Package version not defined."}');
            }
        }

        return $label;
    }

    /**
     * Converts Intermediary package object to dictionary and make json string to objects.
     *
     * @param  object          $packageIntermediary is package intermediary representation.
     * @param  array[optional] $packageDict         is the package jptf representation.
     * @return array package dictionary.
     */
    function _string_to_obj($packageIntermediary, $packageDict = array()) {
        foreach ($packageIntermediary as $key => $value) {
            if ($value instanceof DirectoryNode || $value instanceof \Blended\Orm\DirectoryNode) {
                $packageDict[$key] = $this->_string_to_obj($value->data);
            } elseif ($value instanceof HashFile) {
                $tempRefFilePath = explode("." . $value->ref_file . ".hash", $value->location);
                $refFilePath = $tempRefFilePath[0] . $value->ref_file;
                if (file_exists($refFilePath))
                    continue;
                $packageDict[$value->ref_file] = $this->backend->getMediaFomHash($value);
            } elseif ($value instanceof BinaryFile || $value instanceof \Blended\Orm\BinaryFile) {
                $packageDict[$key] = $this->backend->get_media($value);
            } elseif ($value instanceof JsonFile || $value instanceof \Blended\Orm\JsonFile) {
                $packageDict[$key] = json_decode($value->data, true);
            } elseif ($value instanceof TextFile || $value instanceof \Blended\Orm\TextFile) {
                $packageDict[$key] = $value->data;
            }
        }

        return $packageDict;
    }

    /**
     * Converts Intermediary package object to dictionary with hashes and make json string to objects.
     *
     * @param  object           $packageIntermediary is package intermediary representation.
     * @param  string[optional] $hash                is the package hash.
     * @return array package dictionary.
     */
    function _string_to_obj_hash($packageIntermediary, $hash = null) {
        $jptf = array();
        if (!is_null($hash)) {
            $packageObject['hash'] = $hash;
        }
        $packageDict = array();
        foreach ($packageIntermediary as $key => $value) {
            if ($value instanceof DirectoryNode || $value instanceof \Blended\Orm\DirectoryNode) {
                $packageDict[$key]['hash'] = $value->hash;
                $packageDict[$key]['data'] = $this->_string_to_obj_hash($value->data);
            } elseif ($value instanceof HashFile) {
                $tempRefFilePath = explode("." . $value->ref_file . ".hash", $value->location);
                $refFilePath = $tempRefFilePath[0] . $value->ref_file;
                if (file_exists($refFilePath))
                    continue;
                $packageDict[$value->ref_file]['hash'] = $value->hash;
                $packageDict[$value->ref_file]['href'] = $value->href;
            } elseif ($value instanceof BinaryFile || $value instanceof \Blended\Orm\BinaryFile) {
                $packageDict[$key]['href'] = $value->location;
            } elseif ($value instanceof JsonFile || $value instanceof \Blended\Orm\JsonFile) {
                $packageDict[$key] = $value->data;
            } elseif ($value instanceof TextFile || $value instanceof \Blended\Orm\TextFile) {
                $packageDict[$key] = $value->data;
            }
        }

        return $packageDict;
    }

    /**
     * Map's _index shortcuts refrences available to package object.
     *
     * @param  array $packageContext is the package Dictionary.
     * @return array package context representation.
     */
    function _index_shortcuts($packageContext) {
        foreach ($packageContext as $key => $value) {
            if (($key === '_index' || $key === '_index.json') && is_array($value)) {
                $packageContext = array_merge($packageContext, $value);
            } elseif (is_array($value)) {
                $this->_index_shortcuts($value);
            }
        }

        return $packageContext;
    }

    /**
     * Removes all values starting with "_" and shortens resource paths.
     *
     * @param  array $packageContext is the package object.
     * @return array package context after trimming.
     */
    function _trim_context($packageContext) {
        if (is_array($packageContext)) {

            foreach ($packageContext as $key => $value) {
                if ($key === '_index.json') {
                    $packageContext['_index.json'] = $this->_trim_context($packageContext['_index.json']);
                    $packageContext = array_merge($packageContext, $packageContext['_index.json']);
                    unset($packageContext['_index.json']);
                } elseif (substr($key, 0, 1) === '_') {
                    unset($packageContext[$key]);
                } elseif (strpos($key, '.') !== false) {
                    $newKey = explode('.', $key);
                    $newKey = $newKey[0];
                    $packageContext[$newKey] = $value;
                    unset($packageContext[$key]);
                } elseif (is_array($packageContext[$key])) {
                    $packageContext[$key] = $this->_trim_context($packageContext[$key]);
                }
            }
        } elseif (is_string($packageContext)) {
            $packageContext = json_decode($packageContext, true);
        }

        return $packageContext;
    }

    /**
     * Helper function that returns the last element of an array.
     *
     * @param  array $arr is the array given.
     * @return string the last element of the given array.
     */
    function _get_last_element($arr) {
        return array_values(array_slice($arr, -1))[0];
    }

    /**
     * Helper function that gives path till the latest .json file encountered.
     *
     * @param $arr is the array where the latest json file is searched for
     */
    function _get_last_json(&$arr) {
        while (!(substr($this->_get_last_element($arr), -5) === '.json')) {
            array_pop($arr);
        }
    }

    /**
     * Converts Intermediary package object to jptf format.
     *
     * @param  object $packageIntermediary is the current path to be resolved.
     * @return array package jptf representation.
     */
    function _as_jptf_builder($packageIntermediary, $packageSlug, $label) {
        $packageJptf = array();
        try {
            foreach ((array) $packageIntermediary as $key => $value) {
                if (is_int($key) && !is_object($value)) {
                    if (is_array($value) && count($value) == 1) {
                        $instance = array_values($value)[0];
                        if ($instance instanceof PartFile)
                            continue;
                        if ($instance instanceof BinaryFile) {
                            $ref_file = array_keys($value)[0];
                            $hashFile = "." . $ref_file . ".hash";
                            //Construct $hashFilePath
                            $tempHashFilePath = explode($ref_file, $instance->location);
                            $hashFilePath = $tempHashFilePath[0] . $hashFile;
                            if (file_exists($hashFilePath))
                                continue;
                        }
                    }
                    $packageJptf[] = $this->_as_jptf_builder($value, $packageSlug, $label);
                } elseif ($value instanceof DirectoryNode || $value instanceof \Blended\Orm\DirectoryNode) {
                    $packageJptf[$key]['type'] = 'directory';
                    $packageJptf[$key]['hash'] = $value->hash;
                    $packageJptf[$key]['content'] = $this->_as_jptf_builder($this->_JptfList($value->data), $packageSlug, $label);
                } elseif ($value instanceof TextFile || $value instanceof \Blended\Orm\TextFile) {
                    $packageJptf[$key]['hash'] = md5($value->data);
                    $packageJptf[$key]['type'] = 'file';
                    $packageJptf[$key]['content'] = base64_encode(gzcompress($value->data, 6));
                } elseif ($value instanceof JsonFile || $value instanceof \Blended\Orm\JsonFile) {
                    $packageJptf[$key]['type'] = 'file';
                    $packageJptf[$key]['hash'] = md5($value->data);
                    $packageJptf[$key]['content'] = base64_encode(gzcompress($value->data, 6));
                } elseif ($value instanceof HashFile) {
                    $packageJptf[$value->ref_file]['type'] = 'media';
                    $packageJptf[$value->ref_file]['hash'] = $value->hash;
                    //Build href
                    $temp_separator = explode("/", $packageSlug);
                    $separator = $packageSlug . "/" . $label;
                    if ("draft" == $label)
                        $separator = $temp_separator[0] . "/src/" . $temp_separator[1];
                    $temp_file = explode($separator, $value->location);
                    $filepath = ltrim($temp_file[1], "/");
                    $tempEndPoint = explode("/", $filepath);
                    if (count($tempEndPoint) > 1) {
                        array_pop($tempEndPoint);
                        $absFilePath = implode("/", $tempEndPoint);
                        $endpoint = "/" . $absFilePath . '/' . $value->ref_file;
                    } else {
                        $endpoint = "/" . $value->ref_file;
                    }

                    $packageJptf[$value->ref_file]['href'] = str_replace("\\", "", $endpoint);
                } elseif ($value instanceof BinaryFile || $value instanceof \Blended\Orm\BinaryFile) {
                    if (isset($value->location) && file_exists($value->location)) {
                        // $packageJptf[ $key ]['href'] = 'data:image/png;base64,' . base64_encode($this->backend->get_image_content($value->location, $packageSlug, $label));
                        //$packageJptf[$key]['uri'] = $this->backend->get_url($value->location);
                    } elseif ($this->backend->get_imageByHash($value->hash)) {
                        $packageJptf[$key]['href'] = $this->backend->get_imageByHash($value->hash, true);
                        $packageJptf[$key]['uri'] = $value->location;
                    }
                    $packageJptf[$key]['hash'] = $value->hash;
                    $packageJptf[$key]['type'] = 'media';
                }
            }
        } catch (\Exception $e) {
            throw new \Exception('BlendedError while converting to jptf');
        }

        return $packageJptf;
    }

    /**
     * Converts Intermediary package object to hash onlyjptf format.
     *
     * @param  object $packageIntermediary is the current path to be resolved.
     * @return array package jptf representation.
     */
    function _as_jptf_builder_hashOnly($packageIntermediary) {
        $packageJptf = array();
        try {
            foreach ((array) $packageIntermediary as $key => $value) {

                if (is_int($key) && !is_object($value)) {
                    if (is_array($value) && count($value) == 1) {
                        $instance = array_values($value)[0];
                        if ($instance instanceof PartFile)
                            continue;
                        if ($instance instanceof BinaryFile) {
                            $ref_file = array_keys($value)[0];
                            $hashFile = "." . $ref_file . ".hash";
                            //Construct $hashFilePath
                            $tempHashFilePath = explode($ref_file, $instance->location);
                            $hashFilePath = $tempHashFilePath[0] . $hashFile;
                            if (file_exists($hashFilePath))
                                continue;
                        }
                    }
                    $packageJptf[] = $this->_as_jptf_builder_hashOnly($value);
                } elseif ($value instanceof DirectoryNode || $value instanceof \Blended\Orm\DirectoryNode) {
                    $packageJptf[$key]['type'] = 'directory';
                    $packageJptf[$key]['hash'] = $value->hash;
                    $packageJptf[$key]['content'] = $this->_as_jptf_builder_hashOnly($this->_JptfList($value->data));
                } elseif ($value instanceof TextFile || $value instanceof \Blended\Orm\TextFile) {
                    $packageJptf[$key]['hash'] = md5($value->data);
                    $packageJptf[$key]['type'] = 'file';
                } elseif ($value instanceof JsonFile || $value instanceof \Blended\Orm\JsonFile) {
                    $packageJptf[$key]['type'] = 'file';
                    $packageJptf[$key]['hash'] = md5($value->data);
                } elseif ($value instanceof HashFile) {
                    $packageJptf[$value->ref_file]['type'] = 'media';
                    $packageJptf[$value->ref_file]['hash'] = $value->hash;
                } elseif ($value instanceof BinaryFile || $value instanceof \Blended\Orm\BinaryFile) {
                    $packageJptf[$key]['hash'] = $value->hash;
                    $packageJptf[$key]['type'] = 'media';
                }
            }
        } catch (\Exception $e) {
            throw new \Exception('BlendedError while converting to jptf');
        }

        return $packageJptf;
    }

    /**
     * Create file jptf and insert file data.
     *
     * @param  array $partialDict is package dictionary representation.
     * @return array partial jptf representation.
     */
    function _builtJptf($partialDict, $packageSlug, $label) {
        /**
         * Allowed image extensions.
         * Defined within backend.
         *
         * @access global
         * @var    array
         */
        global $ALLOWED_IMAGE_TYPES;

        $partialJptf = array();
        $count = 0;
        foreach ($partialDict as $key => $value) {
            $fileExtension = explode('.', $key);
            $fileExtension = $fileExtension[count($fileExtension) - 1];
            if (is_int($key)) {
                if (!empty($value)) {
                    $partialJptf[$key] = $this->_builtJptf($value, $packageSlug, $label);
                }
            } elseif (substr($key, -5) === '.part' || (substr($key, -5) === '.hash' && strlen($key) > 5)) {
                continue;
            } elseif (substr($key, -5) === '.json') {
                $value = json_encode($value);
                $partialJptf[$key]['type'] = 'file';
                if (!is_null($partialDict[$key])) {
                    $partialJptf[$key]['content'] = base64_encode(gzcompress($partialDict[$key], 6));
                    $partialJptf[$key]['hash'] = md5($partialDict[$key]);
                }
            } elseif (strpos($key, '.') !== true && is_array($value) && !in_array(strtolower($fileExtension), $ALLOWED_IMAGE_TYPES)) {
                $partialJptf[$key]['type'] = 'directory';
                if (!isset($value['hash'])) {
                    
                } elseif (!is_array($value['hash'])) {
                    $partialJptf[$key]['hash'] = $value['hash'];
                    unset($value['hash']);
                } else {
                    $hash = array_unique($value['hash']);
                    $hashString = $hash[0];
                    $partialJptf[$key]['hash'] = $hashString;
                    unset($value['hash']);
                }
                $partialJptf[$key]['content'] = $this->_builtJptf($this->_JptfList($value), $packageSlug, $label);
                $count++;
            } elseif (in_array(strtolower($fileExtension), $ALLOWED_IMAGE_TYPES)) {
                $partialJptf[$key]['type'] = 'media';
                if (!is_null($value) && !empty($value)) {
                    if (filter_var($value['href'], FILTER_VALIDATE_URL)) {
                        $href = $value['href'];
                        $hash = $value['hash'];
                    } else {
                        $type = pathinfo($value['href'], PATHINFO_EXTENSION);
                        $data = $this->backend->get_image_content($value['href'], $packageSlug, $label);
                        $href = 'data:image/' . $type . ';base64,' . base64_encode($data);
                        $hash = sha1($data);
                    }

                    $partialJptf[$key]['hash'] = $hash;
                    $partialJptf[$key]['href'] = $href;
                }
            } elseif (strpos($key, '.') !== false) {
                $partialJptf[$key]['type'] = 'file';
                if (!is_null($partialDict[$key])) {
                    $partialJptf[$key]['content'] = base64_encode(gzcompress($partialDict[$key], 6));
                    $partialJptf[$key]['hash'] = md5($partialDict[$key]);
                }
            } elseif (empty($value)) {
                $partialJptf[$key]['type'] = 'directory';
            }
        }

        return $partialJptf;
    }

    /**
     * Create file jptf and insert file data.
     *
     * @param  array $dict is file jptf.
     * @return array file list.
     */
    function _JptfList($dict) {
        $list = array();
        if (!empty($dict)) {
            foreach ($dict as $key => $value) {
                $list[][$key] = $value;
            }
        }

        return $list;
    }

    /**
     * unset visited path from jptf path iterator.
     *
     * @param  string $key is the value to be deleted from stack.
     * @return array remained visited path stack.
     */
    function _unset_jptfIterator($key, $array) {
        unset($array[$key]);
        return $array;
    }

    /**
     * Initiate JsonFile backend
     *
     * @param  string $args as file data.
     * @return jsonFile Object.
     */
    function getJsonObject($args) {
        global $blendedConfig;
        $switch = $blendedConfig->get("setup_configuration", "BACKEND");
        switch ($switch) {
            case "DB":
                $data = new \Blended\Orm\JsonFile($args);
                return $data;
            case "FS":
                $data = new \Blended\hostlib\JsonFile($args);
                return $data;
            default:
                $data = new \Blended\hostlib\JsonFile($args);
                return $data;
        }
    }

    /**
     * Initiate TextFile backend
     *
     * @param  string $args as file data.
     * @return TextFile Object.
     */
    function getTextObject($args) {
        global $blendedConfig;
        $switch = $blendedConfig->get("setup_configuration", "BACKEND");
        switch ($switch) {
            case "DB":
                $data = new \Blended\Orm\TextFile($args);
                return $data;
            case "FS":
                $data = new \Blended\hostlib\TextFile($args);
                return $data;
            default:
                $data = new \Blended\hostlib\TextFile($args);
                return $data;
        }
    }

    /**
     * Initiate BinaryFile backend
     *
     * @param  string $args as file data.
     * @return BinaryFile Object.
     */
    function getBinaryObject($args) {
        global $blendedConfig;
        $switch = $blendedConfig->get("setup_configuration", "BACKEND");
        switch ($switch) {
            case "DB":
                $data = new \Blended\Orm\BinaryFile($args);
                return $data;
            case "FS":
                $data = new \Blended\hostlib\BinaryFile($args);
                return $data;
            default:
                $data = new \Blended\hostlib\BinaryFile($args);
                return $data;
        }
    }

    /**
     * Initiate DirectoryFile backend
     *
     * @param  string $args1 as path data.
     * @param  object $args2 as package data.
     * @param  string|optional $args3 as file data.
     * @return DirectoryFile Object.
     */
    function getDirectoryObject($args1, $args2, $args3 = null) {
        global $blendedConfig;
        $switch = $blendedConfig->get("setup_configuration", "BACKEND");
        switch ($switch) {
            case "DB":
                $data = new \Blended\Orm\DirectoryNode($args1, $args2, $args3);
                return $data;
            case "FS":
                $data = new \Blended\hostlib\DirectoryNode($args1, $args2, $args3);
                return $data;
            default:
                $data = new \Blended\hostlib\DirectoryNode($args1, $args2, $args3);
                return $data;
        }
    }

    /**
     * Deserialize jptf package representation to package Intermediary representation.
     *
     * @param  array            $packageJptf         is the package jptf format.
     * @param  object[optional] $packageIntermediary is the Intermediary package object.
     * @return object Intermediary package object representation.
     */
    function _blended_deserialize_jptf($packageJptf, $packageIntermediary = array()) {
        /**
         * Allowed image extensions.
         * Defined within backend.
         *
         * @access global
         * @var    array
         */
        global $ALLOWED_IMAGE_TYPES;

        try {

            foreach ($packageJptf as $key => $value) {
                $fileExtension = explode('.', $key);
                $fileExtension = $fileExtension[count($fileExtension) - 1];
                if (is_int($key)) {
                    $packageIntermediary = array_merge($packageIntermediary, $this->_blended_deserialize_jptf($value));
                } elseif ((strpos($key, '.json') !== false)) {
                    $packageIntermediary[$key] = $this->getJsonObject(gzuncompress(base64_decode($value['content'])));
                    $packageIntermediary[$key]->location = $key;
                    $packageIntermediary[$key]->hash = $value['hash'];
                } elseif (in_array(strtolower($fileExtension), $ALLOWED_IMAGE_TYPES)) {
                    $packageIntermediary[$key] = $this->getBinaryObject($key);
                    $packageIntermediary[$key]->location = $key;
                    $packageIntermediary[$key]->hash = $value['hash'];
                    if (isset($value['href'])) {
                        $packageIntermediary[$key]->data = $value['href'];
                    }
                } elseif ((strpos($key, '.') !== false) && $key != '.blended') {
                    $packageIntermediary[$key] = $this->getTextObject(gzuncompress(base64_decode($value['content'])));
                    $packageIntermediary[$key]->location = $key;
                    $packageIntermediary[$key]->hash = $value['hash'];
                } elseif ((strpos($key, '.') !== true) && $key != 'blendedrc' && $key != '.blended') {
                    $data = $this->getDirectoryObject($key, $value['content'], "deserialize");
                    $data->hash = $value['hash'];
                    $data->data = $this->_blended_deserialize_jptf($data->data);
                    $packageIntermediary[$key] = $data;
                }
            }
        } catch (\Exception $e) {
            throw new \Exception('{"status_code":"500", "message":"' . $e->getMessage() . '"}');
        }

        return $packageIntermediary;
    }

    function merge_new_media_in_jptf($packageJptf, $newMediaFiles, $localMediaFiles) {
        global $ALLOWED_IMAGE_TYPES;

        try {
            $content = array();
            foreach ($packageJptf as $key => $value) {
                $fileExtension = $this->backend->getLastString($key);
                if (is_int($key)) {
                    $content[$key] = $this->merge_new_media_in_jptf($value, $newMediaFiles, $localMediaFiles);
                } elseif ($value["type"] == "directory") {
                    $content[$key]['type'] = 'directory';
                    $content[$key]['hash'] = $value["hash"];
                    $content[$key]['content'] = $this->merge_new_media_in_jptf($value["content"], $newMediaFiles, $localMediaFiles);
                } elseif ($value["type"] == "file") {
                    $content[$key]['type'] = 'file';
                    $content[$key]['hash'] = $value["hash"];
                    $content[$key]['content'] = $value["content"];
                } elseif ($value["type"] == "media" && in_array(strtolower($fileExtension), $ALLOWED_IMAGE_TYPES)) {
                    $content[$key]['type'] = 'media';
                    $content[$key]['hash'] = $value["hash"];
                    $content[$key]['href'] = isset($value["href"]) ? $value["href"] : "";
                    if (array_key_exists($value["hash"], $newMediaFiles)) {
                        $content[$key]['href'] = $newMediaFiles[$value["hash"]];
                    } elseif (array_key_exists($value["hash"], $localMediaFiles)) {
                        $content[$key]['href'] = $localMediaFiles[$value["hash"]];
                    }
                }
            }
        } catch (\Exception $e) {
            throw new \Exception('{"status_code":"500", "message":"' . $e->getMessage() . '"}');
        }
        return $content;
    }

    /**
     * Create package media list
     * @param object $packageIntermediary
     * @param array $media Storage Variable
     * @throws \Exception
     */
    function create_media_list_from_intermediary($packageIntermediary, &$media) {
        try {
            foreach ((array) $packageIntermediary as $key => $value) {
                if (is_int($key) && !is_object($value)) {
                    $this->create_media_list_from_intermediary($value, $media);
                } elseif ($value instanceof DirectoryNode) {
                    $this->create_media_list_from_intermediary($this->_JptfList($value->data), $media);
                } elseif ($value instanceof HashFile) {
                    $tempRefFilePath = explode($key, $value->location);
                    $refFilePath = $tempRefFilePath[0] . $value->ref_file;
                    $media[$value->hash] = file_exists($refFilePath) ? 'data:image/png;base64,' . base64_encode($this->backend->get_image_content($refFilePath)) : $value->href;
                }
            }
        } catch (\Exception $e) {
            throw new \Exception('{"status_code":"500", "message":"' . $e->getMessage() . '"}');
        }
    }

    /**
     * Deserialize jptf package representation to package Intermediary representation.
     *
     * @param  array            $packageJptf         is the package jptf format.
     * @param  object[optional] $packageIntermediary is the Intermediary package object.
     * @return object Intermediary package object representation.
     */
    function _blended_deserialize_jptf_nocontent($packageJptf, $packageIntermediary = array()) {
        /**
         * Allowed image extensions.
         * Defined within backend.
         *
         * @access global
         * @var    array
         */
        global $ALLOWED_IMAGE_TYPES;

        try {

            foreach ($packageJptf as $key => $value) {
                $fileExtension = explode('.', $key);
                $fileExtension = $fileExtension[count($fileExtension) - 1];
                if (is_int($key)) {
                    $packageIntermediary = array_merge($packageIntermediary, $this->_blended_deserialize_jptf_nocontent($value));
                } elseif ((strpos($key, '.json') !== false)) {
                    $packageIntermediary[$key] = $this->getJsonObject('');
                    $packageIntermediary[$key]->location = $key;
                    $packageIntermediary[$key]->hash = $value['hash'];
                    unset($packageIntermediary[$key]->data);
                } elseif (in_array(strtolower($fileExtension), $ALLOWED_IMAGE_TYPES)) {
                    $packageIntermediary[$key] = $this->getBinaryObject($key);
                    $packageIntermediary[$key]->location = $key;
                    $packageIntermediary[$key]->hash = $value['hash'];
                    unset($packageIntermediary[$key]->data);
                } elseif ((strpos($key, '.') !== false) && $key != '.blended') {
                    $packageIntermediary[$key] = $this->getTextObject('');
                    $packageIntermediary[$key]->location = $key;
                    $packageIntermediary[$key]->hash = $value['hash'];
                    unset($packageIntermediary[$key]->data);
                } elseif ((strpos($key, '.') !== true) && $key != 'blendedrc' && $key != '.blended') {
                    $data = $this->getDirectoryObject($key, $value['content']);
                    $data->hash = $value['hash'];
                    $data->data = $this->_blended_deserialize_jptf_nocontent($data->data);
                    $packageIntermediary[$key] = $data;
                }
            }
        } catch (\Exception $e) {
            throw new \Exception('{"status_code":"500", "message":"' . $e->getMessage() . '"}');
        }

        return $packageIntermediary;
    }

    /**
     * Get file data used in construction of file jptf package object.
     *
     * @param  array  $packageJptf is the file jptf.
     * @param  string $filePath    is the file location.
     * @return array file jptf representation.
     */
    function _getfileData($packageJptf, $filePath) {
        $fileExtension = explode('.', $filePath);
        $fileExtension = $fileExtension[count($fileExtension) - 1];
        $filePath = explode('/', $filePath);
        if (empty($packageJptf)) {
            $content = array("content" => "", "hash" => "");
        } else {

            foreach ($filePath as $value) {
                if (isset($packageJptf[0]) && isset($packageJptf[0][$value]['content'])) {
                    $hash = (isset($packageJptf[0][$value]['hash']) ? $packageJptf[0][$value]['hash'] : "");
                    $packageJptf = $packageJptf[0][$value]['content'];
                    $content = array("content" => $packageJptf, "hash" => $hash);
                } elseif (!isset($packageJptf[0][$value]['href'])) {
                    $content = array("content" => "", "hash" => "");
                } elseif (isset($packageJptf[0][$value]['href'])) {
                    $hash = (isset($packageJptf[0][$value]['hash']) ? $packageJptf[0][$value]['hash'] : "");
                    $packageJptf = $packageJptf[0][$value]['href'];
                    $content = array("content" => $packageJptf, "hash" => $hash);
                }
            }
        }

        return $content;
    }

    /**
     * Create a partial jptf package to push.
     *
     * @param  string $iteratePath   is location to file.
     * @param  string $fileName      is file name.
     * @param  string $fileData      is file data.
     * @param  string $directoryHash is directory Hash.
     * @return array Partial jptf representation.
     */
    function _createPartialDict($iteratePath, $fileName, $level, $fileData, $directoryHash) {
        $data = array();
        foreach ($iteratePath as $key => $value) {
            if ($level != 1) {
                $level--;
                $data[$value] = array_merge($this->_createPartialDict($this->_unset_jptfIterator($key, $iteratePath), $fileName, $level, $fileData, $directoryHash), $data);
                break;
            } else {
                $data[$value] = $fileData;
            }
            if (isset($directoryHash[$value])) {
                $data[$value]['hash'] = $directoryHash[$value];
            }
        }

        return $data;
    }

    /**
     * Get file data from $packageDict.
     *
     * @param  string $iteratePath is location to file.
     * @param  object $packageDict package object dictionary format.
     * @return string file data.
     */
    function _find_fileData($iteratePath, $packageDict) {
        $temp = $packageDict;
        $directoryhashList = array();
        $uniqueDirectory = '#';
        foreach ($iteratePath as $key => $value) {
            if (strpos($value, '.') !== true && isset($temp[$value]['data'])) {
                if (array_key_exists($value, $directoryhashList)) {
                    $directoryhashList[$uniqueDirectory . $value] = & $temp[$value]['hash'];
                    $uniqueDirectory .= '#';
                } else {
                    $directoryhashList[$value] = & $temp[$value]['hash'];
                }
                $temp = &$temp[$value]['data'];
            } elseif ($value === "_package.json" && !isset($temp[$value])) {
                throw new \Exception('{ "status_code":"500", "message":"Invalid package. File _package.json not found!"}');
            } else {
                $temp = & $temp[$value];
            }
        }
        $hashList['hash_list'] = $directoryhashList;
        $temp = array_merge($hashList, array('content' => $temp));

        return $temp;
    }

    /**
     * Retrieve Jptf representation of package
     *
     * @param  string $filePath is file location.
     * @param  object $fileData is file data.
     * @return array package jptf representation.
     */
    function _setStructure($filePath, $fileData) {
        /**
         * Allowed image extensions.
         * Defined within backend.
         *
         * @access global
         * @var    array
         */
        global $ALLOWED_IMAGE_TYPES;

        foreach ($filePath as $key => $value) {
            if (strpos($key, '.') != true) {
                $packageJptf[$key]['hash'] = null;
                $packageJptf[$key]['type'] = 'directory';
                $packageJptf[$key]['content'] = array(
                    $this->_setStructure($value, $fileData),
                );
            } elseif (in_array(strtolower($fileData['type']), $ALLOWED_IMAGE_TYPES)) {
                $packageJptf[$key]['hash'] = null;
                $packageJptf[$key]['type'] = 'media';
                $packageJptf[$key]['href'] = $fileData['data'];
                $packageJptf[$key]['content'] = $fileData['data'];
            } elseif ($fileData['type'] === 'json') {
                $packageJptf[$key]['hash'] = null;
                $packageJptf[$key]['type'] = 'json';
                $packageJptf[$key]['content'] = $fileData['data'];
            } else {
                $packageJptf[$key]['hash'] = null;
                $packageJptf[$key]['type'] = 'file';
                $packageJptf[$key]['content'] = $fileData['data'];
            }
        }

        return $packageJptf;
    }

    /**
     * find difference b/w local package and hub package.
     *
     * @param  array $hubpackageJptf   is jptf package representation.
     * @param  array $localpackageJptf is jptf package representation.
     * @param  array $track            is visited set stack.
     * @return array package differences.
     */
    function _blendedRecursiveDiff($hubpackageJptf, $localpackageJptf, $track = null, $flag = false, $addLocalFile = false) {
        $list = array();
        foreach ((array) $hubpackageJptf as $key => $value) {
            if ($value instanceof DirectoryNode || $value instanceof \Blended\Orm\DirectoryNode) {
                if (!isset($localpackageJptf[$key])) {
                    $track = $track . '/' . $key;

                    if (empty($hubpackageJptf[$key]->data)) {
                        $list = array_merge($list, array($track));
                    }
                    $list = array_merge($this->_blendedRecursiveDiff($hubpackageJptf[$key]->data, null, $track, $flag, $addLocalFile), $list);
                    $track = explode('/', $track);
                    unset($track[count($track) - 1]);
                    $track = implode('/', $track);
                } elseif ($hubpackageJptf[$key]->hash != $localpackageJptf[$key]->hash) {
                    $track = $track . '/' . $key;
                    if (empty($localpackageJptf[$key]->data) && $flag === true) {
                        $addLocalFile = true;
                        $track = $track . '::add';
                    }
                    $list = array_merge($this->_blendedRecursiveDiff($hubpackageJptf[$key]->data, $localpackageJptf[$key]->data, $track, $flag, $addLocalFile), $list);
                    $track = explode('/', $track);
                    unset($track[count($track) - 1]);
                    $track = implode('/', $track);
                }
            } elseif ($value instanceof HashFile) {
                if ($value instanceof HashFile) {
                    $tempRefFilePath = explode("." . $value->ref_file . ".hash", $value->location);
                    $refFilePath = $tempRefFilePath[0] . $value->ref_file;
                    if (file_exists($refFilePath))
                        continue;
                }
                if (!empty($localpackageJptf[$value->ref_file]) && !empty($hubpackageJptf[$key])) {
                    if ($hubpackageJptf[$key]->hash != $localpackageJptf[$value->ref_file]->hash) {
                        $temp = $track;
                        $track = $track . '/' . $key;
                        array_push($list, $track);
                        $track = $temp;
                    }
                } elseif (!isset($hubpackageJptf[$key])) {
                    $temp = $track;
                    $track = $track . '/' . $key;
                    array_push($list, $track);
                    $track = $temp;
                } elseif (!isset($localpackageJptf[$value->ref_file])) {
                    $temp = $track;
                    $track = $track . '/' . $value->ref_file;
                    array_push($list, $track);
                    $track = $temp;
                }
            } elseif ($value instanceof BinaryFile || $value instanceof TextFile || $value instanceof JsonFile || $value instanceof \Blended\Orm\BinaryFile || $value instanceof \Blended\Orm\JsonFile || $value instanceof \Blended\Orm\TextFile) {
                if ($value instanceof BinaryFile) {
                    $ref_file = $value->location;
                    $hashFile = "." . $ref_file . ".hash";
                    $partFile = "." . $ref_file . ".part";
                    if (array_key_exists($hashFile, $localpackageJptf) && array_key_exists($partFile, $localpackageJptf))
                        continue;
                }
                if (!empty($localpackageJptf[$key]) && !empty($hubpackageJptf[$key])) {
                    if ($hubpackageJptf[$key]->hash != $localpackageJptf[$key]->hash) {
                        $temp = $track;
                        $track = $track . '/' . $key;
                        array_push($list, $track);
                        $track = $temp;
                    }
                } elseif (!isset($hubpackageJptf[$key])) {
                    $temp = $track;
                    $track = $track . '/' . $key;
                    array_push($list, $track);
                    $track = $temp;
                } elseif (!isset($localpackageJptf[$key])) {
                    $temp = $track;
                    $track = $track . '/' . $key;
                    array_push($list, $track);
                    $track = $temp;
                }
            }
        }

        return $list;
    }

    /**
     * Get current packageHash.
     *
     * @param  string $packageLocation
     * @return current packageHash.
     */
    function _get_current_draft_packageHash($packageLocation) {
        $backend = $this->backend;
        $packageIntermediary = $backend->get_package($packageLocation);
        $currentpackageHash = $packageIntermediary->hash;

        return $currentpackageHash;
    }

    /**
     * Retrieve changes need to be updated with the package.
     *
     * @param  string $packageJptf is the un-resolved jptf from source code.
     * @return array package update Details.
     */
    function _resolveJPTF($packageJptf, $directoryTrack = array('track' => '', 'count' => '', 'stack' => array()), $flag = true) {
        foreach ($packageJptf as $key => $value) {
            if (isset($value['content']) && empty($value['content']) && $value['type'] === 'directory') {
                $directoryTrack['emptyDirectory'][] = $directoryTrack['track'] . '/' . $key;
                if (isset($value['hash']) && !empty($value['hash']))
                    $directoryTrack["on_hub"][] = ltrim($directoryTrack['track'], "/") . '/' . $key;
                $directoryTrack['track'] = '';
            }
            if (isset($value['content']) && is_array($value['content']) && count($value['content']) > 0) {
                if (is_int($key) && $flag === true) {
                    $directoryTrack['track'] = $directoryTrack['track'];
                } else {

                    $delete = $directoryTrack['track'];
                    $directoryTrack['track'] = $directoryTrack['track'] . '/' . $key;
                    if (isset($value['hash']) && !empty($value['hash'])) {
                        if (!empty($delete))
                            $directoryTrack["on_hub"][] = ltrim($directoryTrack['track'], "/");
                        else
                            $directoryTrack["on_hub"][] = $key;
                    }
                }
                $items = count(array_keys($value['content']));
                if (empty($value['content'])) {
                    if (isset($directoryTrack['temp'])) {
                        $directoryTrack['delete'] = $delete;
                        $directoryTrack['track'] = $directoryTrack['temp'];
                    } else {
                        $directoryTrack['track'] = '';
                    }
                } elseif ($flag === false && $items > 1) {
                    $directoryTrack['temp'] = $directoryTrack['track'];
                    $directoryTrack['count'] = $items;
                    array_push($directoryTrack['stack'], $items);
                }
                $flag = false;
                $directoryTrack = $this->_resolveJPTF($value['content'], $directoryTrack, $flag);
            } elseif ($key === '$ref') {
                $directoryTrack['toRename'][] = $directoryTrack['track'] . '/' . $key . '@' . $value;
                $directoryTrack['track'] = '';
            } elseif (is_int($key) && isset($value['content']) && is_string($value['content']) && $value['type'] === 'file') {
                if (strpos($key, '.') === false) {
                    $key = $key . '.txt';
                }
                $directoryTrack['file'][] = array(
                    'name' => $key,
                    'path' => $directoryTrack['track'],
                    'content' => array(
                        $key => $value,
                    ),
                );
                if (isset($directoryTrack['stack'])) {
                    $stack = $this->_trackStack($directoryTrack['stack'], $directoryTrack['temp']);
                    $directoryTrack['temp'] = $stack['track'];
                    $directoryTrack['stack'] = $stack['stack'];
                }
                $directoryTrack['track'] = '';
            } elseif (is_int($key)) {
                if (isset($value['type']) && !isset($value['content'])) {
                    $directoryTrack['toDelete'][] = $directoryTrack['track'] . '/' . $key;
                    $directoryTrack['track'] = '';
                } else {
                    $directoryTrack = $this->_resolveJPTF($value, $directoryTrack, $flag);
                    if (isset($directoryTrack['stack']) && isset($directoryTrack['temp'])) {
                        $stack = $this->_trackStack($directoryTrack['stack'], $directoryTrack['temp']);
                        $directoryTrack['track'] = $stack['track'];
                        $stackCount = count($stack['stack']);
                    }
                }
            } elseif (!isset($value['href']) && $value['type'] === 'media') {
                if (isset($directoryTrack['delete'])) {
                    $directoryTrack['toDelete'][] = $directoryTrack['delete'] . '/' . $key;
                } else {
                    $directoryTrack['toDelete'][] = $directoryTrack['track'] . '/' . $key;
                }
                $directoryTrack['track'] = '';
            } elseif (!isset($value['content']) && $value['type'] === 'directory') {
                if (isset($directoryTrack['delete'])) {
                    $directoryTrack['toDelete'][] = $directoryTrack['delete'] . '/' . $key;
                } else {
                    $directoryTrack['toDelete'][] = $directoryTrack['track'] . '/' . $key;
                }
                $directoryTrack['track'] = '';
            } elseif (!isset($value['content']) && $value['type'] === 'file') {
                $directoryTrack['toDelete'][] = $directoryTrack['track'] . '/' . $key;
                $directoryTrack['track'] = '';
            } elseif (isset($value['content']) && is_string($value['content']) && $value['type'] === 'file') {
                if (strpos($key, '.') === false) {
                    $key = $key . '.txt';
                }
                if (isset($directoryTrack['temp']) && empty($directoryTrack['track'])) {
                    $directoryTrack['track'] = $directoryTrack['temp'];
                }
                $directoryTrack['file'][] = array(
                    'name' => $key,
                    'path' => $directoryTrack['track'],
                    'content' => array(
                        $key => $value,
                    ),
                );
                if (isset($directoryTrack['stack']) && isset($directoryTrack['temp'])) {
                    $stack = $this->_trackStack($directoryTrack['stack'], $directoryTrack['temp']);
                    $directoryTrack['temp'] = $stack['track'];
                    $directoryTrack['stack'] = $stack['stack'];
                }
                $directoryTrack['track'] = '';
            } elseif (isset($value['href']) && is_string($value['href']) && $value['type'] === 'media') {
                if (strpos($key, '.') === false) {
                    throw new \Exception('{"status_code":"500", "message":"Image does not specify its extension or type in the following path : ' . $directoryTrack['track'] . $key . ' ."}');
                }
                $directoryTrack['file'][] = array(
                    'name' => $key,
                    'path' => $directoryTrack['track'],
                    'content' => array(
                        $key => $value,
                    ),
                );
                if (isset($directoryTrack['stack']) && isset($directoryTrack['temp'])) {
                    $stack = $this->_trackStack($directoryTrack['stack'], $directoryTrack['temp']);
                    $directoryTrack['temp'] = $stack['track'];
                    $directoryTrack['stack'] = $stack['stack'];
                }
                $directoryTrack['track'] = '';
            }
        }

        return $directoryTrack;
    }

    /**
     * Keep track of path between iterating from directories to files
     *
     * @param  array  $stack is the directories or files count.
     * @param  string $track is the current path.
     * @return array stack.
     */
    function _trackStack($stack, $track, $flag = true) {
        if (empty($stack)) {
            return array(
                'stack' => $stack,
                'track' => $track,
            );
        }
        $item = $stack[count($stack) - 1];
        $size = count($stack);
        if ($item === 0) {
            $track = explode('/', $track);
            unset($track[count($track) - 1]);
            $track = implode('/', $track);
            array_pop($stack);
            if ($size > 1 && $flag === true) {
                for ($i = $size; $i > $size - 2; $i--) {
                    $flag = false;
                    $newStack = $this->_trackStack($stack, $track, $flag);
                    $stack = $newStack['stack'];
                    $track = $newStack['track'];
                }
            }
        } else {
            $item--;
            array_pop($stack);
            array_push($stack, $item);
        }
        $size = count($stack);
        if ($size === 1) {
            $item = $stack[count($stack) - 1];
            if ($item === 0) {
                $track = '';
            }
        }
        if (empty($stack)) {
            $track = '';
        }
        return array('stack' => $stack, 'track' => $track);
    }

}
