<?php

/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2021 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and later
 */

namespace Gantry\Component\Layout\Version;

/**
 * Read layout from simplified yaml file.
 */
class Format1
{
    /** @var array */
    protected $scopes = [0 => 'grid', 1 => 'block'];
    /** @var array */
    protected $data;
    /** @var array */
    protected $keys = [];

    /**
     * Format1 constructor.
     * @param array $data
     */
    public function __construct(array $data)
    {
        $this->data = $data;
    }

    /**
     * @return array
     */
    public function load()
    {
        $data = &$this->data;

        // Check if we have preset.
        $preset = [];
        if (isset($data['preset']) && is_array($data['preset']) && isset($data['layout']) && is_array($data['layout'])) {
            $preset = $data['preset'];
            $data = $data['layout'];
        }

        // We have user entered file; let's build the layout.

        // Two last items are always offcanvas and atoms.
        $offcanvas = isset($data['offcanvas']) ? $data['offcanvas'] : [];
        $atoms = isset($data['atoms']) ? $data['atoms'] : [];

        unset($data['offcanvas'], $data['atoms']);

        $data['offcanvas'] = $offcanvas;
        if ($atoms) {
            $data['atoms'] = $atoms;
        }

        $result = [];
        foreach ($data as $field => $params) {
            $child = $this->parse($field, (array) $params, 0);
            unset($child->size);

            $result[] = $child;
        }

        return ['preset' => $preset] + $result;
    }

    /**
     * @param array $preset
     * @param array $structure
     * @return array
     */
    public function store(array $preset, array $structure)
    {
        return ['preset' => $preset, 'children' => $structure];
    }

    /**
     * @param \stdClass $item
     * @param bool $container
     */
    protected function normalize(&$item, $container = false)
    {
        if ($item->type === 'pagecontent') {
            // Update pagecontent to match the new standards.
            $item->type = 'system';
            if (!$item->subtype || $item->subtype === 'pagecontent') {
                $item->subtype = 'content';
                $item->title = 'Page Content';
            } else {
                $item->subtype ='messages';
                $item->title = 'System Messages';
            }
        }

        if ($item->type === 'section') {
            // Update section to match the new standards.
            $section = strtolower($item->title);
            $item->id = $section;
            $item->subtype = (in_array($section, ['aside', 'nav', 'article', 'header', 'footer', 'main']) ? $section : 'section');
        } elseif ($item->type === 'offcanvas') {
            $item->id = $item->subtype = $item->type;
            unset ($item->attributes->name, $item->attributes->boxed);

            return;
        } else {
            // Update all ids to match the new standards.
            $item->id = $this->id($item->type, $item->subtype);
        }

        if (!empty($item->attributes->extra)) {
            /** @var \stdClass $attributes */
            $attributes = $item->attributes;
            foreach ($attributes->extra as $i => $extra) {
                $v = reset($extra);
                $k = key($extra);
                if ($k === 'id') {
                    $item->id = preg_replace('/^g-/', '', $v);
                    $attributes->id = $v;
                    unset ($attributes->extra[$i]);
                }
            }
            if (empty($item->attributes->extra)) {
                unset ($item->attributes->extra);
            }
        }

        $item->subtype = $item->subtype ?: $item->type;
        $item->layout = in_array($item->type, ['container', 'section', 'grid', 'block', 'offcanvas']);

        if (isset($item->attributes->boxed)) {
            // Boxed already set, just change boxed=0 to boxed='' to use default settings.
            $item->attributes->boxed = $item->attributes->boxed ?: '';

            return;
        }

        if (!$container) {
            return;
        }

        // Update boxed model to match the new standards.
        if (isset($item->children) && count($item->children) === 1) {
            $child = reset($item->children);
            if ($item->type === 'container') {
                // Remove parent container only if the only child is a section.
                if ($child->type === 'section') {
                    $child->attributes->boxed = 1;
                    $item = $child;
                }
                $item->attributes->boxed = '';
            } elseif ($child->type === 'container') {
                // Remove child container.
                $item->attributes->boxed = '';
                $item->children = $child->children;
            }
        }
    }

    /**
     * @param int|string $field
     * @param array $content
     * @param int $scope
     * @param bool|null $container
     * @return object
     */
    protected function parse($field, array $content, $scope, $container = true)
    {
        if (is_numeric($field))  {
            // Row or block
            $type = $this->scopes[$scope];
            $result = (object) ['id' => null, 'type' => $type, 'subtype' => $type, 'layout' => true, 'attributes' => (object) []];
            $scope = ($scope + 1) % 2;
        } elseif (strpos($field, 'container') === 0) {
            // Container
            $type = 'container';
            $result = (object) ['id' => null, 'type' => $type, 'subtype' => $type, 'layout' => true, 'attributes' => (object) []];
            $id = substr($field, 10) ?: null;
            if ($id !== null) {
                $result->attributes->id = $id;
            }
        } else {
            // Section
            $list = explode(' ', $field, 2);
            $field = array_shift($list);
            $size = ((float) array_shift($list)) ?: null;
            $type = in_array($field, ['atoms', 'offcanvas']) ? $field : 'section';
            $subtype = in_array($field, ['aside', 'nav', 'article', 'header', 'footer', 'main']) ? $field : 'section';

            $result = (object) [
                'id' => null,
                'type' => $type,
                'subtype' => $subtype,
                'layout' => true,
                'title' => ucfirst($field),
                'attributes' => (object) ['id' => 'g-' . $field]
            ];

            if ($size) {
                $result->size = $size;
            }
        }

        if (!empty($content)) {
            $result->children = [];
            foreach ($content as $child => $params) {
                if (is_array($params)) {
                    $child = $this->parse($child, $params, $scope, false);
                } else {
                    $child = $this->resolve($params, $scope);
                }
                if (!empty($child->size)) {
                    $result->attributes->size = $child->size;
                }
                unset($child->size);
                $result->children[] = $child;
            }
        }

        $this->normalize($result, $container);

        return $result;
    }

    /**
     * @param string $field
     * @param int $scope
     * @return object
     */
    protected function resolve($field, $scope)
    {
        $list = explode(' ', $field, 2);
        $list2 = explode('-', array_shift($list), 2);
        $size = ((float) array_shift($list)) ?: null;
        $type = array_shift($list2);
        $subtype = array_shift($list2) ?: false;
        $title = ucfirst($subtype ?: $type);

        $attributes = new \stdClass;

        $attributes->enabled = 1;

        if ($subtype && $type === 'position') {
            $attributes->key = $subtype;
            $subtype = false;
        }

        $result = (object) ['id' => $this->id($type, $subtype), 'title' => $title, 'type' => $type, 'subtype' => $subtype, 'attributes' => $attributes];
        $this->normalize($result);

        if ($scope > 1) {
            if ($size) {
                $result->attributes->size = $size;
            }
        } elseif ($scope === 1) {
            $result = (object) ['id' => $this->id('block'), 'type' => 'block', 'subtype' => 'block', 'layout' => true, 'children' => [$result], 'attributes' => new \stdClass];
            if ($size) {
                $result->attributes->size = $size;
            }
        } elseif ($scope === 0) {
            $result = (object) ['id' => $this->id('grid'), 'type' => 'grid', 'subtype' => 'grid', 'layout' => true, 'children' => [$result], 'attributes' => new \stdClass];
        }

        return $result;
    }


    /**
     * @param string $type
     * @param string|null $subtype
     * @return string
     */
    protected function id($type, $subtype = null)
    {
        if ($type === 'atoms') {
            return $type;
        }

        $result = [];
        if ($type !== 'particle' && $type !== 'atom') {
            $result[] = $type;
        }
        if ($subtype && $subtype !== $type) {
            $result[] = $subtype;
        }
        $key = implode('-', $result);

        do {
            $id = mt_rand(1000, 9999);
            if (!isset($this->keys[$key][$id])) {
                break;
            }
        } while (true);

        $this->keys[$key][$id] = true;

        return $key . '-'. $id;
    }
}
