<?php

/**
 * Questo file è parte di Pongho.
 *
 * @author    Daniele Termini
 * @copyright Web Agency Meta Line S.r.l.
 * @package   Application\Admin
 */

namespace Application\Admin\Controller;

use Pongho\Form\Form;
use Pongho\Http\RedirectResponse;
use Pongho\Http\Response;

abstract class CrudFormController extends Crud2Controller
{
    /**
     * @return string
     */
    protected function renderForm(Form $form)
    {
        return $form->render();
    }

    protected function renderAddEdit(Form $form)
    {
        $this->getHelper()
            ->addJavaScript(pongho_url(
                '/Application/Admin/Resources/vendor/jqueryui-nestedsortable/jquery.ui.nestedSortable.js?v='
                . filemtime(PONGHO_PATH . '/Application/Admin/Resources/vendor/jqueryui-nestedsortable/jquery.ui.nestedSortable.js')
            ))
            ->addJavaScript(pongho_url(
                '/Application/Admin/Resources/views/js/plugins.js?v='
                . filemtime(PONGHO_PATH . '/Application/Admin/Resources/views/js/plugins.js')
            ))
            ->addJavaScript(pongho_url(
                '/Application/Admin/Resources/views/js/scripts.js?v='
                . filemtime(PONGHO_PATH . '/Application/Admin/Resources/views/js/scripts.js')
            ));

        $this->getHelper()->getBodyView()
            ->setTemplatePath(__DIR__ . '/../Resources/views/edit.php')
            ->assignVars([
                'title'   => $this->getAddEditTitle($form->getSubject()->getModel()),
                'content' => $form,
            ]);

        return null;
    }

    /**
     * @return \Application\Admin\Form\FormConfig
     */
    abstract protected function getFormsOptions();

    /**
     * @return null|RedirectResponse|Response
     */
    protected function addEdit()
    {
        $path = $this->getParameter('path');
        $form = $this->getForm();
        $model = $form->getSubject()->getModel();

        $this->updateLastViewedRow($model);

        $this->getHelper()->notify($this, $this->getEventNamespace('notify_before_render_view'), ['model' => $model]);

        $this->invokeCallBack('before_set_template', $model);

        $this->invokeCallBack('after_initialize', $form);

        if ($this->getRequest()->getMethod() === 'POST') {
            $this->getHelper()->notify($this, $this->getEventNamespace('before_save'), ['form' => $form, 'model' => $model]);
            $this->invokeCallBack('before_save', $form);

            $form->handleRequest($this->getRequest());

            if (!$form->hasErrors()) {
                $this->invokeCallBack('after_save', $form, []);
                $this->getHelper()->notify($this, $this->getEventNamespace('after_save'), ['form' => $form, 'model' => $model]);
            }

            if ($this->response instanceof Response) {
                return $this->response;
            }

            if ($this->action === 'add') {
                if (!$form->hasErrors()) {
                    return $this->getHelper()->redirectResponse($this->url('/' . $path . '/edit/' . $model->id . '/', '&'));
                }
            } else {
                return new Response($this->renderForm($form));
            }
        }

        $this->renderAddEdit($form);

        $this->getHelper()->notify($this, $this->getEventNamespace('notify_after_render_view'), ['model' => $model]);

        return null;
    }

    /**
     * @return \Application\Admin\Form\FormBuilder
     */
    protected function getFormBuilderClass()
    {
        return \Application\Admin\Form\FormBuilder::class;
    }

    /**
     * @return \Application\Admin\Form\Generator\AdminGenerator
     */
    protected function getFormGeneratorClass()
    {
        return \Application\Admin\Form\Generator\AdminGenerator::class;
    }

    /**
     * @return string
     */
    protected function getFormAction()
    {
        $path = $this->getParameter('path');

        // Gestione dell'azione
        if ($this->action === 'add') {
            return $this->url('/' . $path . '/add/');
        } else {
            return $this->url('/' . $path . '/edit/' . $this->getModel()->id . '/');
        }
    }

    /**
     * @return \Pongho\Form\Form
     */
    protected function getForm()
    {
        /** @var $form \Pongho\Form\Form */
        static $form;

        if ($form === null) {
            $builder = $this->getFormBuilderClass();
            $generator = $this->getFormGeneratorClass();

            $form = $builder::buildConfig($this->getFormsOptions());

            // TODO: Queste due righe di codice dovrebbero essere spostate nel metodo buildConfig() del builder.
            $form->setGenerator(new $generator($this->getHelper()->getLocalization()));
            $form->setAction($this->getFormAction());
        }

        return $form;
    }

    /**
     * Restituisce la chiave della tab che deve essere attivata
     *
     * @return string
     */
    protected function getActiveTab()
    {
        return $this->getRequest()->query->get(
            'tabs[tab]',
            $this->getRequest()->post->get('tabs[tab]', 'main', true),
            true
        );
    }


    /**
     * callback after_initialize
     *
     * Serve a modificare il formato dei dati nella Request, i menu più grandi potrebbero arrivare al limite di campi HTTP
     * serializzando i campi ed inviando il dato serializzato si risolve il problema
     *
     * I campi della Form mi arrivano serializzati, devo deserializzarli prima di farli gestire alla Form
     */
    public function afterInitializeCallback(Form $form)
    {
        $form->setAttribute('data-request-format', 'serialized');

        /** @var \Pongho\Http\Request $request */
        $request = $this->getRequest();

        $post = $request->post->get('form');

        // Se è arrivata la form serializzata riscrivo la Request in modo da passare alla Form quanto si aspetta.
        if ($post) {
            $post = $this->deepConvertStdClassToArray(json_decode((string) $post));

            foreach ($post as $name => $value) {
                $request->post->set($name, $value);
            }
        }
    }

    /**
     * @param array|\stdClass $value
     * @return array
     */
    private function deepConvertStdClassToArray($value)
    {
        if ($value instanceof \stdClass) {
            $value = (array) $value;
        }

        if (is_array($value)) {
            $new_value = [];

            foreach ($value as $key => &$field_value) {
                if (is_numeric($key)) {
                    $key = (int) $key;
                }

                $new_value[$key] = $this->deepConvertStdClassToArray($field_value);
            }

            $value = $new_value;
        }

        return $value;
    }
}
