<?php

namespace App\Service;

use App\Tool\Utils;
use Pimcore\Db;
use Pimcore\File;
use Pimcore\Logger;
use Pimcore\Model\DataObject\ClassDefinition;
use Pimcore\Model\Document;
use Symfony\Component\Yaml\Yaml;

class Generator
{
	const CONTROLLER_SECTION_DOCUMENT_ACTIONS = 'DOCUMENT ROUTED ACTIONS';
	const CONTROLLER_SECTION_FORM_ACTIONS = 'FORM HANDLERS';

	/**
	 * @param string $name
	 * @param string $url
	 * @param bool   $localized
	 * @param string $icon
	 * @param int    $id
	 * @param string $definitionPath
	 *
	 * @return bool
	 *
	 * @throws \Exception
	 */
	public function createClassDefinition($name, $url = false, $localized = false,
		$icon = null, $id = null, $definitionPath = null)
	{
		if ($id && $definitionPath) {
			if (!file_exists($definitionPath)) {
				Logger::error(
					'CREATING CLASS - definition file '.$definitionPath.' not found',
					['component' => 'generator']
				);

				return false;
			}
			//we have to insert a row to `classes` table manually before saving the definition
			$db = Db::get();
			try {
				$saved = $db->insert('classes', [
					'name' => $name,
					'id' => $id,
				]);
			} catch (\Exception $e) {
				$saved = false;
			}
			if (!$saved) {
				Logger::error(
					'CREATING CLASS - Could not insert row into `classes` table',
					['component' => 'generator']
				);

				return false;
			}

			$classDefinition = include $definitionPath;
			$classDefinition->setId($id);
		} else {
			if (!$id) {
				$id = strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $name));
			}
			$classDefinition = new ClassDefinition();
			$classDefinition->setName($name);
			$classDefinition->setId($id);
			$classDefinition->setCreationDate(time());
			$classDefinition->setModificationDate(time());
			$classDefinition->setUserOwner(2);
			$classDefinition->setUserModification(2);
			$classDefinition->setAllowInherit(false);
			$classDefinition->setAllowVariants(false);
			if ($icon) {
				$classDefinition->setIcon($this->normalizePimcoreIconPath($icon));
			}
			$classDefinition->setShowVariants(false);
			$classDefinition->setPropertyVisibility([
				'grid' => [
					'id' => true,
					'path' => false,
					'published' => true,
					'modificationDate' => false,
					'creationDate' => true,
				],
				'search' => [
					'id' => true,
					'path' => true,
					'published' => true,
					'modificationDate' => true,
					'creationDate' => true,
				],
			]);
			$fieldDefinitions = [];
			$nameInput = new ClassDefinition\Data\Input();
			$nameInput->setWidth(($localized) ? 550 : 600);
			$nameInput->setName('name');
			$nameInput->setTitle('Name');
			$nameInput->setMandatory(false);
			$nameInput->setVisibleGridView(true);
			$nameInput->setVisibleSearch(true);
			$urlInput = null;
			if ($url) {
				$urlInput = new ClassDefinition\Data\CalculatedValue();
				$urlInput->setCalculatorClass('\App\Service\ValueCalculator');
				$urlInput->setWidth(($localized) ? 550 : 600);
				$urlInput->setName('url');
				$urlInput->setTitle('Url');
				$urlInput->setMandatory(false);
				$urlInput->setNoteditable(true);
				$urlInput->setVisibleGridView(true);
				$urlInput->setVisibleSearch(false);
			}
			if ($localized) {
				$lf = new ClassDefinition\Data\Localizedfields();
				$lf->setName('localizedfields');
				$lf->setWidth(700);
				$lf->addChild($nameInput);
				if ($urlInput) {
					$lf->addChild($urlInput);
				}
				$fieldDefinitions['localizedfields'] = $lf;
			} else {
				$fieldDefinitions['name'] = $nameInput;
				if ($urlInput) {
					$fieldDefinitions['url'] = $urlInput;
				}
			}
			$classDefinition->setFieldDefinitions($fieldDefinitions);

			$panel = new ClassDefinition\Layout\Panel();
			$panel->setLabelWidth(100);
			$panel->setName($name);
			$panel->setChildren(array_values($fieldDefinitions));
			$layout = new ClassDefinition\Layout\Panel();
			$layout->setLabelWidth(100);
			$layout->setName('pimcore_root');
			$layout->addChild($panel);

			$classDefinition->setLayoutDefinitions($layout);
		}

		try {
			$classDefinition->save();

			// check if the ID checks out
			if ($id && $classDefinition->getId() != $id) {
				throw new \Exception(sprintf('ID mismatch [%d]', $classDefinition->getId()));
			}

			return $classDefinition->getId();
		} catch (\Exception $e) {
			\Pimcore\Logger::error(
				'CREATING CLASS - '.$e->getMessage()."\n".$e->getTraceAsString(),
				['component' => 'generator']
			);

			return false;
		}
	}

	/**
	 * @param string $name
	 *
	 * @return bool
	 */
	public function deleteClassDefinition($name)
	{
		try {
			$classDefinition = ClassDefinition::getByName($name);
		} catch (\Exception $e) {
			$classDefinition = null;
		}
		if (!$classDefinition) {
			Logger::error(
				'DELETING CLASS - '.$name.'not found',
				['component' => 'generator']
			);

			return false;
		}

		try {
			$classDefinition->delete();
		} catch (\Exception $e) {
			Logger::error(
				'DELETING CLASS - '.$e->getMessage()."\n".$e->getTraceAsString(),
				['component' => 'generator']
			);

			return false;
		}

		return true;
	}

	/**
	 * @param string|int $classNameOrId
	 * @param bool       $mapList
	 *
	 * @return bool
	 */
	public function createClassMapping($classNameOrId, $mapList = true)
	{
		$className = $this->getClassName($classNameOrId);
		if (!$className) {
			return false;
		}
		$configPath = PIMCORE_PROJECT_ROOT.'/config/config.yaml';
		try {
			$classMap = Yaml::parseFile($configPath);
		} catch (\Exception $e) {
			Logger::error(
				'CREATING CLASS MAPPING - '.$e->getMessage(),
				['component' => 'generator']
			);

			return false;
		}

		$parent = sprintf('Pimcore\Model\DataObject\%s', $className);
		$parentList = sprintf('Pimcore\Model\DataObject\%s\Listing', $className);
		$child = sprintf('App\Model\DataObject\%s', $className);
		$childList = sprintf('App\Model\DataObject\%s\Listing', $className);
		if (!isset($classMap['pimcore'])) {
			$classMap['pimcore'] = [];
		}
		if (!isset($classMap['pimcore']['models'])) {
			$classMap['pimcore']['models'] = [];
		}
		if (!isset($classMap['pimcore']['models']['class_overrides'])) {
			$classMap['pimcore']['models']['class_overrides'] = [];
		}
		if (isset($classMap['pimcore']['models']['class_overrides'][$parent]) || ($mapList && isset($classMap['pimcore']['models']['class_overrides'][$parentList]))) {
			Logger::error(
				sprintf('CREATING CLASS MAPPING - mapping for class %s already exists', $className),
				['component' => 'generator']
			);

			return false;
		}

		$classMap['pimcore']['models']['class_overrides'][$parent] = $child;
		if (!$this->createMappedClass($className)) {
			return false;
		}
		if ($mapList) {
			$classMap['pimcore']['models']['class_overrides'][$parentList] = $childList;
			if (!$this->createMappedClassList($className)) {
				return false;
			}
		}

		return false !== file_put_contents($configPath, Yaml::dump($classMap, 99));
	}

	/**
	 * @param string|int $classNameOrId
	 *
	 * @return bool
	 */
	public function deleteClassMapping($classNameOrId)
	{
		$className = $this->getClassName($classNameOrId);
		if (!$className) {
			return false;
		}
		$configPath = PIMCORE_PROJECT_ROOT.'/config/config.yaml';
		try {
			$classMap = Yaml::parseFile($configPath);
		} catch (\Exception $e) {
			Logger::error(
				'CREATING CLASS MAPPING - '.$e->getMessage(),
				['component' => 'generator']
			);

			return false;
		}

		$parent = sprintf('Pimcore\Model\DataObject\%s', $className);
		$parentList = sprintf('Pimcore\Model\DataObject\%s\Listing', $className);
		if (isset($classMap['pimcore']['models']['class_overrides'][$parent])) {
			unset($classMap['pimcore']['models']['class_overrides'][$parent]);
			if (!$this->deleteMappedClass($className)) {
				return false;
			}
		}
		if (isset($classMap['pimcore']['models']['class_overrides'][$parentList])) {
			unset($classMap['pimcore']['models']['class_overrides'][$parentList]);
			if (!$this->deleteMappedClassList($className)) {
				return false;
			}
		}

		return false !== file_put_contents($configPath, Yaml::dump($classMap, 99));
	}

	/**
	 * @param string $path
	 * @param array  $row
	 * @param int    $offset
	 *
	 * @return bool
	 */
	private function phpFileAddRow($path, $row, $offset = 0)
	{
		$rows = file($path);
		if (!$rows) {
			return false;
		}

		array_splice($rows, count($rows) + $offset, 0, $row);

		return file_put_contents($path, $rows);
	}

	/**
	 * @param string $path
	 * @param string $expression
	 *
	 * @return bool
	 */
	private function phpFileRemoveRowByExpression($path, $expression)
	{
		$rows = file($path);
		if (!$rows) {
			return false;
		}

		foreach ($rows as $key => $row) {
			if (false !== stristr($row, $expression)) {
				unset($rows[$key]);
			}
		}

		return file_put_contents($path, $rows);
	}

	/**
	 * @param string $className
	 *
	 * @return bool
	 */
	public function createMappedClass($className)
	{
		$template = '<?php

namespace App\Model\DataObject;

use Pimcore\Model\DataObject;

class %s extends DataObject\%s
{

	/*								Helpers										*/

	/*								Mapped										*/

	/* 								For elastic search							*/

}
';

		$modelPath = sprintf('%s/src/Model/DataObject/%s.php', PIMCORE_PROJECT_ROOT, $className);
		try {
			if (!file_exists($modelPath)) {
				$result = $this->writePhpFile($modelPath, vsprintf($template, [
					$className,
					$className,
				]));
				if (!$result) {
					return false;
				}
			}
		} catch (\Exception $e) {
			Logger::error(
				'CREATING MAPPED CLASS - '.$e->getMessage()."\n".$e->getTraceAsString(),
				['component' => 'generator']
			);

			return false;
		}

		return true;
	}

	/**
	 * @param string $className
	 *
	 * @return bool
	 */
	public function createMappedClassList($className)
	{
		$template = '<?php

namespace App\Model\DataObject\%s;

use Pimcore\Model\DataObject;

class Listing extends DataObject\%s\Listing
{
}
';

		$modelPath = sprintf('%s/src/Model/DataObject/%s/Listing.php', PIMCORE_PROJECT_ROOT, $className);
		try {
			if (!file_exists($modelPath)) {
				$result = $this->writePhpFile($modelPath, vsprintf($template, [
					$className,
					$className,
				]));
				if (!$result) {
					return false;
				}
			}
		} catch (\Exception $e) {
			Logger::error(
				'CREATING MAPPED CLASS LIST - '.$e->getMessage()."\n".$e->getTraceAsString(),
				['component' => 'generator']
			);

			return false;
		}

		return true;
	}

	/**
	 * @param string $className
	 *
	 * @return bool
	 */
	public function deleteMappedClass($className)
	{
		$modelPath = PIMCORE_PROJECT_ROOT.'/src/Model/DataObject/'.$className.'.php';
		dump($modelPath);exit;
		try {
			if (file_exists($modelPath)) {
				unlink($modelPath);
			}
		} catch (\Exception $e) {
			Logger::error(
				'DELETING MAPPED CLASS - '.$e->getMessage()."\n".$e->getTraceAsString(),
				['component' => 'generator']
			);

			return false;
		}

		return true;
	}

	/**
	 * @param string $className
	 *
	 * @return bool
	 */
	public function deleteMappedClassList($className)
	{
		$modelsDir = PIMCORE_PROJECT_ROOT.'/src/Model/DataObject';
		$modelPath = sprintf('%s/%s/Listing.php', $modelsDir, $className);
		try {
			if (file_exists($modelPath)) {
				unlink($modelPath);
			}
			if (file_exists($modelsDir.'/'.$className)) {
				rmdir($modelsDir.'/'.$className);
			}
		} catch (\Exception $e) {
			Logger::error(
				'DELETING MAPPED CLASS LIST - '.$e->getMessage()."\n".$e->getTraceAsString(),
				['component' => 'generator']
			);

			return false;
		}

		return true;
	}

	/**
	 * @param string      $name
	 * @param string      $controller
	 * @param string      $form
	 * @param bool|string $view
	 * @param string      $documentPath
	 * @param bool        $forceExistenceError
	 *
	 * @return bool
	 */
	public function createAction($name, $controller, $form = null, $view = true, $documentPath = null, $forceExistenceError = true)
	{
		$tokens = $this->loadControllerTokens($controller);
		if (!$tokens) {
			return false;
		}
		if ($view && true !== $view && false === file_exists(PIMCORE_PROJECT_ROOT.$view)) {
			Logger::error(
				sprintf('CREATING CONTROLLER ACTION - template %s does not exist', PIMCORE_PROJECT_ROOT.$view),
				['component' => 'generator']
			);

			return false;
		}
		$actionExists = $this->checkIfTokenExists($tokens, lcfirst($name).'Action', $forceExistenceError);
		if ($forceExistenceError && $actionExists) {
			return false;
		}

		if (!$actionExists) {
			$sectionIndex = $this->findControllerSectionTokenIndex($tokens, self::CONTROLLER_SECTION_DOCUMENT_ACTIONS);
			if (!$sectionIndex) {
				return false;
			}
			$actionContent = $this->createControllerAction($name, $form, $documentPath);
			array_splice($tokens, $sectionIndex, 0, $actionContent);
		}

		$scriptPath = vsprintf('%s/templates/%s/%s.html.php', [
			PIMCORE_PROJECT_ROOT,
			ucfirst($controller),
			lcfirst($name),
		]);
		if ($view && !file_exists($scriptPath)) {
			$scriptContents = (true === $view)
				? "<?\n/**\n * @var \\Pimcore\\Templating\\PhpEngine \$this\n * @var \\Pimcore\\Templating\\PhpEngine \$view\n * @var \\Pimcore\\Templating\\GlobalVariables \$App\n */\n\n\$this->extend('layout.html.php');\n?>\n"
				: file_get_contents(PIMCORE_PROJECT_ROOT.$view);
			try {
				$result = $this->writePhpFile($scriptPath, $scriptContents);
				if (!$result) {
					return false;
				}
			} catch (\Exception $e) {
				Logger::error(
					'CREATING CONTROLLER ACTION - '.$e->getMessage()."\n".$e->getTraceAsString(),
					['component' => 'generator']
				);

				return false;
			}
		}

		if (!$actionExists) {
			return $this->saveControllerTokens($controller, $tokens);
		} else {
			return true;
		}
	}

	/**
	 * @param string $name
	 * @param string $controller
	 *
	 * @return bool|string
	 */
	public function createFormHandler($name, $controller)
	{
		$tokens = $this->loadControllerTokens($controller);
		if (!$tokens) {
			return false;
		}
		$handlerName = 'handle'.$name.'Form';
		$tokenExists = $this->checkIfTokenExists($tokens, $handlerName);
		if ($tokenExists) {
			return false;
		}

		$sectionIndex = $this->findControllerSectionTokenIndex($tokens, self::CONTROLLER_SECTION_FORM_ACTIONS);
		if (!$sectionIndex) {
			return false;
		}
		$actionContent = $this->createControllerFormHandler($name);

		array_splice($tokens, $sectionIndex, 0, $actionContent);

		if (!$this->saveControllerTokens($controller, $tokens)) {
			return false;
		}

		return $handlerName;
	}

	/**
	 * @param string $name
	 * @param string $form
	 * @param string $documentPath
	 *
	 * @return string
	 */
	public function createControllerAction($name, $form = null, $documentPath = null)
	{
		$template = '/**%s
	 * @return Response
	 */
	public function %sAction(\Symfony\Component\HttpFoundation\Request $request)
	{
		%s
	}

	';
		$formHandlerTemplate = '$form = $this->createForm(\\App\\Form\\%sFormType::class);
		$form->handleRequest($request);

		if ($form->isSubmitted()) {
			if ($this->handle%sForm($form)) {
				return $this->gotoUrl($this->docUrl(\'/\'));
			}
		}

		$this->view->%sForm = $form;';

		return vsprintf($template, [
			($documentPath) ? sprintf("\n	 * %s\n	 *", $documentPath) : '',
			lcfirst($name),
			($form) ? vsprintf($formHandlerTemplate, [$form, $form, lcfirst($form)]) : '',
		]);
	}

	/**
	 * @param string $name
	 *
	 * @return string
	 */
	public function createControllerFormHandler($name)
	{
		$template = '/**
	 * @param \\Symfony\\Component\\Form\\FormInterface $form
	 * @return boolean
	 */
	private function handle%sForm(\\Symfony\\Component\\Form\\FormInterface $form)
	{
		if ($form->isValid()) {
			$data = $form->getData();

			return true;
		} else {
			$this->addErrorMsg($this->translate(\'msg_form_invalid\'));
		}
		return false;
	}

	';

		return vsprintf($template, [$name]);
	}

	/**
	 * @param string      $title
	 * @param string      $controller
	 * @param string      $action
	 * @param string|int  $parent
	 * @param bool|string $view
	 * @param string      $formHandler
	 * @param string|int  $master
	 *
	 * @return bool|\Pimcore\Model\Document\Page
	 */
	public function createDocumentPage($title = null, $controller = null, $action = null, $parent = null, $view = null, $formHandler = null, $master = null)
	{
		// check parent or load the first under "home"
		$parentId = null;
		if (!$parent) {
			$parent = current(Document::getById(1)->getChildren());
			if ($parent) {
				$parentId = $parent->getId();
			}
		} else {
			$tmp = null;
			if (intval($parent)) {
				$tmp = Document::getById(intval($parent));
			} else {
				$parent = $this->normalizePathParam($parent, false);
				$tmp = Document::getByPath($parent);
			}
			if ($tmp) {
				$parentId = $tmp->getId();
			}
		}
		if (!$parentId) {
			Logger::error(
				sprintf(
					'CREATING DOCUMENT PAGE - could not load parent document [%s]',
					$parent
				),
				['component' => 'generator']
			);

			return false;
		}

		// check content document master
		$masterId = null;
		if ($master) {
			$tmp = null;
			if (intval($master)) {
				$tmp = Document\Page::getById(intval($master));
			} else {
				$master = $this->normalizePathParam($master, false);
				$tmp = Document\Page::getByPath($master);
			}
			if ($tmp) {
				$masterId = $tmp->getId();
			}
			if (!$tmp) {
				Logger::error(
					sprintf(
						'CREATING DOCUMENT PAGE - could not load master document [%s]',
						$master
					),
					['component' => 'generator']
				);

				return false;
			}
		}

		// check controller
		$controllerPath = PIMCORE_PROJECT_ROOT.'/src/Controller/'.$controller.'Controller.php';
		if (!file_exists($controllerPath)) {
			Logger::error(
				sprintf(
					'CREATING DOCUMENT PAGE - controller [%s] does not exist',
					$controllerPath
				),
				['component' => 'generator']
			);

			return false;
		}

		// check or try to create action
		if (!$action) {
			$action = 'TemplateSimple';
		}
		if (!$view) {
			$view = true;
		}

		// create document
		$document = new Document\Page();
		$document->setIndex(0);
		$document->setPublished(true);
		$document->setController($this->normalizeScriptFileName($controller));
		$document->setAction($this->normalizeScriptFileName($action));
		$document->setParentId($parentId);
		$document->setTitle($title);
		$document->setKey(Utils::webalize($this->normalizeScriptFileName($title)));
		$document->setProperty('navigation_name', 'text', $title, false, false);
		$document->setProperty('navigation_title', 'text', $title, false, false);
		if ($masterId) {
			$document->setContentMasterDocumentId($masterId);
		}
		try {
			$document->save();
		} catch (\Exception $e) {
			Logger::error(
				'CREATING DOCUMENT PAGE - '.$e->getMessage()."\n".$e->getTraceAsString(),
				['component' => 'generator']
			);

			return false;
		}

		$this->createAction($action, $controller, $formHandler, $view, $document->getRealFullPath(), false);

		return $document;
	}

	/**
	 * @param string     $subject
	 * @param string|int $parent
	 *
	 * @return bool|\Pimcore\Model\Document\Email
	 */
	public function createDocumentEmail($subject, $parent = null)
	{
		// check parent or load the first under "/notifikace"
		$parentId = null;
		if (!$parent) {
			$notificationsFolderPath = '/notifikace';
			$notificationsFolder = Document\Folder::getByPath($notificationsFolderPath);
			if ($notificationsFolder) {
				$parentId = $notificationsFolder->getId();
			}
		} else {
			$tmp = null;
			if (intval($parent)) {
				$tmp = Document::getById(intval($parent));
			} else {
				$parent = $this->normalizePathParam($parent, false);
				$tmp = Document::getByPath($parent);
			}
			if ($tmp) {
				$parentId = $tmp->getId();
			}
		}
		if (!$parentId) {
			Logger::error(
				sprintf(
					'CREATING DOCUMENT EMAIL - could not load parent document [%s]',
					$parent
				),
				['component' => 'generator']
			);

			return false;
		}

		$webTitle = Document\Page::getById(1)->getProperty('titlePostfix');
		$email = new Document\Email();
		$email->setParentId($parentId);
		$email->setKey(Utils::webalize($this->normalizeScriptFileName($subject)));
		$email->setPublished(true);
		$email->setController('App\\Controller\\CmsController::simple-mail');
		$email->setFrom('noreply@vm3.portadesign.cz');
		$email->setSubject(sprintf('%s - %s', $webTitle, $subject));
		$email->setIndex(0);
		try {
			$email->save();
		} catch (\Exception $e) {
			Logger::error(
				'CREATING DOCUMENT EMAIL - '.$e->getMessage()."\n".$e->getTraceAsString(),
				['component' => 'generator']
			);

			return false;
		}

		$email->save();

		return $email;
	}

	/**
	 * @param string $name
	 * @param string $icon
	 *
	 * @return bool
	 */
	public function createArea($name, $icon)
	{
		$className = preg_replace('@[-_ ]+@', '', ucwords($name, '-_ '));
		$areaFolderPath = sprintf('%s/src/Document/Areabrick', PIMCORE_PROJECT_ROOT);
		$areaViewFolderPath = sprintf('%s/templates/areas/%s', PIMCORE_PROJECT_ROOT, $name);

		// create folder
		if (file_exists($areaFolderPath.'/'.$className.'.php') || file_exists($areaViewFolderPath)) {
			Logger::error(
				sprintf(
					'CREATING AREA BLOCK - area [%s] already exists (either view or class)',
					$name
				),
				['component' => 'generator']
			);

			return false;
		}

		// enable area in extensions.php
		/*$extensionsPath = \Pimcore\Config::locateConfigFile('extensions.php');
		$exConfig = include $extensionsPath;
		if (!isset($exConfig['brick'][$name])) {
			$exConfig['brick'][$name] = true;
		}
		$result1 = $this->writePhpFile($extensionsPath, $exConfig, true);
		if (!$result1) {
			return false;
		}*/

		// create action.php and view.php files
		$actionTemplate = '<?php

namespace App\Document\Areabrick;

use Pimcore\Extension\Document\Areabrick\AbstractTemplateAreabrick;
use Pimcore\Model\Document\Editable\Area\Info;

class %s extends AbstractTemplateAreabrick
{
	/**
	 * @inheritdoc
	 */
	public function getTemplateLocation(): string
	{
		return static::TEMPLATE_LOCATION_GLOBAL;
	}

	/**
	 * @inheritdoc
	 */
	public function getTemplateSuffix(): string
	{
		return static::TEMPLATE_SUFFIX_TWIG;
	}

	/**
	 * @inheritdoc
	 */
	public function getName(): string
	{
		return \'area_%s\';
	}

	/**
	 * @inheritdoc
	 */
	public function getDescription(): string
	{
		return \'area_%s_description\';
	}

	/**
	 * @inheritdoc
	 */
	public function getVersion(): string
	{
		return \'1.0\';
	}

	/**
	 * @inheritdoc
	 */
	public function getIcon(): string
	{
		return \'%s\';
	}

	/**
	 * @inheritdoc
	 */
	public function getHtmlTagOpen(Info $info): string
	{
		return \'\';
	}

	/**
	 * @inheritdoc
	 */
	public function getHtmlTagClose(Info $info): string
	{
		return \'\';
	}
}
';
		try {
			$result2 = $this->writePhpFile(sprintf($areaFolderPath.'/%s.php', $className), vsprintf($actionTemplate, [
				$className,
				$name,
				$name,
				$icon ? $this->normalizePimcoreIconPath($icon) : '/bundles/pimcoreadmin/img/flat-color-icons/html.svg',
			]));
			if (!$result2) {
				return false;
			}
			$result3 = $this->writePhpFile($areaViewFolderPath.'/view.html.twig', '');
			if (!$result3) {
				return false;
			}
		} catch (\Exception $e) {
			Logger::error(
				'CREATING AREA BLOCK - '.$e->getMessage()."\n".$e->getTraceAsString(),
				['component' => 'generator']
			);

			return false;
		}

		return true;
	}

	/**
	 * @param string $name
	 *
	 * @return bool
	 */
	public function deleteArea($name)
	{
		$areaFolderPath = sprintf('%s/src/Document/Areabrick', PIMCORE_PROJECT_ROOT);
		$areaViewFolderPath = sprintf('%s/templates/areas/%s', PIMCORE_PROJECT_ROOT, $name);

		// remove files
		try {
			if (file_exists($areaViewFolderPath)) {
				foreach (scandir($areaViewFolderPath) as $fileOrFolder) {
					if (!in_array($fileOrFolder, ['.', '..'])) {
						unlink($areaViewFolderPath.'/'.$fileOrFolder);
					}
				}
				rmdir($areaViewFolderPath);
			}
			if (file_exists($areaFolderPath.'/'.ucfirst($name).'.php')) {
				unlink($areaFolderPath.'/'.ucfirst($name).'.php');
			}
		} catch (\Exception $e) {
			Logger::error(
				'DELETING AREA BLOCK - '.$e->getMessage()."\n".$e->getTraceAsString(),
				['component' => 'generator']
			);

			return false;
		}

		// disable area in extensions.php
		$extensionsPath = \Pimcore\Config::locateConfigFile('extensions.php');
		$exConfig = include $extensionsPath;
		if (isset($exConfig['brick'][$name])) {
			unset($exConfig['brick'][$name]);
		}

		return $this->writePhpFile($extensionsPath, $exConfig, true);
	}

	/**
	 * @param string $action
	 *
	 * @return string
	 */
	public function normalizeScriptFileName($action)
	{
		return strtolower(preg_replace('/([A-Z]+)/', '-$1', lcfirst($action)));
	}

	/**
	 * @param string $path
	 * @param bool   $startWithSlash
	 *
	 * @return string
	 */
	public function normalizePathParam($path, $startWithSlash = true)
	{
		if (stristr($path, 'Program Files') && stristr($path, '/Git/')) {
			$pos = stripos($path, '/Git/');
			$path = substr($path, $pos + 4);
		}
		if ($startWithSlash && '/' != $path[0]) {
			$path = '/'.$path;
		}

		return $path;
	}

	public function normalizePimcoreIconPath($path, $startWithSlash = true)
	{
		if ($startWithSlash && '/' != $path[0]) {
			$path = '/'.$path;
		}
		if ('/web/pimcore/' === substr($path, 0, 13)) {
			$path = substr($path, 4);
		}

		return $path;
	}

	/**
	 * @param string|int $classNameOrId
	 *
	 * @return false|string
	 */
	private function getClassName($classNameOrId)
	{
		$className = null;
		try {
			$class = ClassDefinition::getById($classNameOrId);
			if (!$class) {
				$class = ClassDefinition::getByName($classNameOrId);
			}
		} catch (\Exception $e) {
		}

		if ($class) {
			$className = $class->getName();
		}

		if (!$className) {
			Logger::error(
				sprintf(
					'GETTING CLASS NAME - class [%s] does not exist',
					$classNameOrId
				),
				['component' => 'generator']
			);

			return false;
		}

		return $className;
	}

	/**
	 * @param string $path
	 * @param string $data
	 * @param bool   $toPhpDataFileFormat
	 *
	 * @return bool
	 */
	private function writePhpFile($path, $data, $toPhpDataFileFormat = false)
	{
		if ($toPhpDataFileFormat) {
			$data = to_php_data_file_format($data);
		}
		// replace spaces with tabs
		$dataWithTabs = $this->replaceSpacesWithTabs($data);
		try {
			File::putPhpFile($path, $dataWithTabs);
		} catch (\Exception $e) {
			Logger::error(
				'WRITING PHP FILE - '.$e->getMessage()."\n".$e->getTraceAsString(),
				['component' => 'generator']
			);

			return false;
		}

		return true;
	}

	/**
	 * @param string $content
	 *
	 * @return string
	 */
	private function replaceSpacesWithTabs($content)
	{
		$rows = explode("\n", $content);

		foreach ($rows as $i => $row) {
			$tmp = preg_replace("/\G\t/", '    ', $row);
			$rows[$i] = preg_replace("/\G {4}/", "\t", $tmp);
		}

		return implode("\n", $rows);
	}

	/**
	 * @param string $controllerName
	 *
	 * @return false|array
	 */
	private function loadControllerTokens($controllerName)
	{
		$path = sprintf('%s/src/Controller/%sController.php', PIMCORE_PROJECT_ROOT, $controllerName);

		$fileContents = file_get_contents($path);

		if (!$fileContents) {
			Logger::error(
				sprintf(
					'LOADING CONTROLLER TOKENS - controller [%s] can not be loaded',
					$path
				),
				['component' => 'generator']
			);

			return false;
		}

		return token_get_all($fileContents);
	}

	/**
	 * @param string $controllerName
	 * @param array $tokens
	 *
	 * @return bool
	 */
	private function saveControllerTokens($controllerName, $tokens)
	{
		$path = sprintf('%s/src/Controller/%sController.php', PIMCORE_PROJECT_ROOT, $controllerName);

		$fileContents = '';
		foreach ($tokens as $token) {
			if (is_string($token)) {
				$fileContents .= $token;
			} else {
				list(, $text) = $token;
				$fileContents .= $text;
			}
		}

		try {
			$result = $this->writePhpFile($path, $fileContents);
			if (!$result) {
				return false;
			}
		} catch (\Exception $e) {
			Logger::error(
				'SAVING CONTROLLER TOKENS - '.$e->getMessage()."\n".$e->getTraceAsString(),
				['component' => 'generator']
			);

			return false;
		}

		return true;
	}

	/**
	 * @param array  $tokens
	 * @param string $tokenPart
	 * @param bool   $forceExit
	 *
	 * @return bool
	 */
	private function checkIfTokenExists($tokens, $tokenPart, $forceExit = true)
	{
		foreach ($tokens as $token) {
			$tokenString = null;
			if (is_string($token)) {
				$tokenString .= $token;
			} else {
				list(, $tokenString) = $token;
			}
			if (stristr($tokenString, $tokenPart)) {
				if ($forceExit) {
					Logger::error(
						sprintf(
							'CHECKING TOKEN EXISTENCE - token [%s] already exists',
							$tokenPart
						),
						['component' => 'generator']
					);

					return true;
				}

				return true;
			}
		}

		return false;
	}

	/**
	 * @param array  $tokens
	 * @param string $section
	 *
	 * @return bool|string
	 */
	private function findControllerSectionTokenIndex($tokens, $section)
	{
		$sectionFound = false;
		foreach ($tokens as $key => $token) {
			if (is_string($token)) {
				$text = $token;
			} else {
				list(, $text) = $token;
			}
			$text = trim($text);

			if (false !== strstr($text, $section)) {
				$sectionFound = true;
			} elseif ($sectionFound && !empty($text)) {
				return $key;
			}
		}

		Logger::error(
			sprintf(
				'SEARCHING CONTROLLER SECTION - section [%s] not found',
				$section
			),
			['component' => 'generator']
		);

		return false;
	}
}
