<?php
namespace HwapX\Admin\Library\Builder;

use F3;
use HwapX\Core\Library\Template;
use HwapX\Core\Library\Validator;
use HwapX\Core\Library\Web;
use HwapX\Core\Library\DB\SQL\Mapper;
use Prefab;

class FormBuilder
{
    protected $default_rules = array(
        'primarykey' => 'id',
        'param'      => 'id',
        'name'       => 'Item',
        'log'        => array(
            'create' => 'O {{ @name }} de id {{ @id }} foi criado por {{ @email }}[{{ @user_id }}]',
            'edit'   => 'O {{ @name }} de id {{ @id }} foi alterado por {{ @email }}[{{ @user_id }}]',
            'delete' => 'O {{ @name }} de id {{ @id }} foi excluido por {{ @email }}[{{ @user_id }}]'
        ),
        'msg'        => array(
            'create' => array(
                'success' => 'O {{ @name }} foi criado com sucesso',
                'error'   => 'Ocorreu um erro ao criar o {{ @name }}'
            ),
            'edit'   => array(
                'success' => 'O {{ @name }} foi salvo com sucesso',
                'error'   => 'Ocorreu um erro ao salvar o {{ @name }}'
            ),
            'delete' => array(
                'success' => 'O {{ @name }} foi excluido com sucesso',
                'error'   => 'Ocorreu um erro ao excluir o {{ @name }}',
                'depends' => 'Ocorreu um erro ao excluir o {{ @name }}, existem {{ @depends }} vinculados a ele.',
            )
        )
    );

    protected $f3 = null;

    protected $hive = array();

    protected $logger = null;

    protected $mapper = null;
    protected $mappers = null;

    protected $rules = array();

    protected $afterSaveHook = array();
    protected $beforeSaveHook = array();
    protected $afterLoadHook = array();
    protected $onValidateHook = array();
    protected $onErrorHook = array();
    protected $onSuccessHook = array();

    public function __construct($mapper = null, $rules = array(), $logger = null)
    {
        $this->f3 = F3::instance();

        if (is_array($mapper)) {
            $this->mappers = $mapper;
        } else {
            $this->mappers = array($mapper);
        }

        $this->mapper = reset($this->mappers);

        foreach ($this->mappers as $key => $value) {
            $this->beforeSaveHook[$key] = array();
            $this->afterSaveHook[$key] = array();
            $this->afterLoadHook[$key] = array();
            $this->onValidateHook[$key] = array();
            $this->onErrorHook[$key] = array();
            $this->onSuccessHook[$key] = array();
        }

        $this->logger = $logger;

        if ($rules) {
            $this->rules = array_merge($this->default_rules, $rules);
        } elseif (isset($mapper->rules) && is_array($mapper->rules)) {
            $this->rules = array_merge_recursive($this->default_rules, $mapper->rules);
        }

        $this->hive = array(
            'email'    => $this->f3->get('SESSION.admin.email'),
            'user_id'  => $this->f3->get('SESSION.admin.id'),
            'name'     => $this->rules['name']
        );
    }

    public function delete($id = null)
    {
        $primarykey = $this->rules['primarykey'];
        $param      = $this->rules['param'];
        $where = isset($this->rules['filter']) ? $this->rules['filter'] : array();

        $token    = isset($where[1]) ? '?' : ':__id';
        $where[0] = isset($where[0]) ? "({$where[0]}) AND $primarykey = $token" : "$primarykey = $token";

        foreach ($where as &$value) if(is_string($value)) {
            $value = Template::instance()->resolve($value);
            if($value === '') $value = null;
        }

        $id = $id !== null ? $id : $this->f3->get('PARAMS.'.$param);

        if($token !== '?')
            $where[$token] = $id;
        else
            $where[] = $id;

        $this->mapper->load($where);

        /*if (!null === $id) {
            $this->mapper->load(array($primarykey.' = ?', $id));
        } else {
            $this->mapper->load(array($primarykey.' = ?', $this->f3->get('PARAMS.'.$param)));
        }*/

        if ($this->mapper->dry()) {
            $this->f3->error(404);
        }

        $this->callHooks($this->afterLoadHook[0], array($this->mapper));

        $groups = array();

        if(isset($this->rules['depends']))
            foreach ($this->rules['depends'] as $table => $data) {
                if(isset($data['table']))
                    $table = $data['table'];

                $mapper = new Mapper($this->f3->get('db'), $table);//$data['mapper'];
                $mapper->load(array($data['foreignkey'].' = ?', $this->mapper->{$primarykey}));

                if(!$mapper->dry()) {
                    $this->hive['depends'] = $data['name'];
                    flash('error', Template::instance()->resolve($this->rules['msg']['delete']['depends'], $this->hive));
                    $this->f3->reroute($this->f3->alias($this->rules['table'], $this->mapper->cast()));
                    return false;
                }
            }

        if (isset($this->rules['fields'])) {
            $groups[] = array('fields' => $this->rules['fields']);
        } else {
            $groups = $this->rules['groups'];
        }

        foreach ($groups as $groupname => $group) {
            if (isset($group['fields'])) {
                foreach ($group['fields'] as $fieldname => $data) {
                    if (in_array($data['type'], array('file', 'image'))) {
                        if ($this->mapper->{$fieldname}
                            && file_exists($this->f3->get('UPLOADS').$this->mapper->{$fieldname}
                            )) {
                            unlink($this->f3->get('UPLOADS').$this->mapper->{$fieldname}
                            );
                        }
                    }
                }
            } elseif (isset($group['table']) && (!isset($group['allow_delete']) || $group['allow_delete'])) {
                $foreignkey = $group['foreignkey'];

                $submapper = $this->mappers[$groupname];

                foreach ($submapper->find(array($foreignkey.' = ?', $this->mapper->{$primarykey}
                )) as $idx => $row) {
                    foreach ($group['table'] as $fieldname => $data) {
                        if (in_array($data['type'], array('file', 'image'))) {
                            if ($row->{$fieldname}
                                && file_exists($this->f3->get('UPLOADS').$row->{$fieldname}
                                )) {
                                unlink($this->f3->get('UPLOADS').$row->{$fieldname}
                                );
                            }
                        }
                    }

                    $old_data = $row->cast();

                    if($row->erase()) {
                        $this->log('delete', $row->table(), $old_data);
                    }
                }
            }
        }

        $this->hive['id'] = $id;

        $old_data = $this->mapper->cast();

        if ($this->mapper->erase()) {
            $this->log('delete', $this->mapper->table(), $old_data);
			if($this->logger)
				$this->logger->write(Template::instance()->resolve($this->rules['log']['delete'], $this->hive));
            flash('success', Template::instance()->resolve($this->rules['msg']['delete']['success'], $this->hive));
        } else {
            flash('error', Template::instance()->resolve($this->rules['msg']['delete']['error'], $this->hive));
        }

        $this->f3->reroute($this->f3->alias($this->rules['table'], $this->mapper->cast()));
    }

    /*
    public function render($id = null) {
    $primarykey = $this->rules['primarykey'];
    $param      = $this->rules['param'];

    if($id === true)
    $this->mapper->reset();
    elseif(!is_null($id))
    $this->mapper->load(array($primarykey.' = ?', $id));
    elseif(!is_null($id))
    $this->mapper->load(array($primarykey.' = ?', $f3->get('PARAMS.'.$param)));

    if($this->mapper->dry())
    $this->f3->error(404);

    $f3->set('form', $this->rules);

    if($f3->get('VERB') == 'POST') {
    $f3->set('form.values', $f3->get('POST'));

    if($this->validator->validate()) {
    $files = Web::instance()->receive(NULL, FALSE, FALSE);
    //$this->logger->write('O usuário "'.$us.'" foi alterado por "'.$f3->get('SESSION.admin.email').'"');

    $slideshow->copyFrom('POST');
    if(isset($files['background']))
    $slideshow->background = $files['background'];
    if(isset($files['image']))
    $slideshow->image      = $files['image'];
    $slideshow->save();

    $f3->set('msgs.success', 'Slideshow salvo com sucesso.');
    }

    $f3->set('form.errors', $this->validator->errors());
    } else {
    $slideshow->copyTo('form.values');
    $f3->set('form.values.roles', explode(';', $f3->get('form.values.roles')));
    }

    echo Template::instance()->render('hwapx/admin/admin/form.html');
    }
     */
    public function handle($id = null)
    {
        $primarykey = $this->rules['primarykey'];
        $param      = $this->rules['param'];
        $creating   = false;

        if (!$this->f3->exists('page.title')) {
            $this->f3->set('page.title', $this->rules['name']);
        }

        $this->f3->set('form', $this->rules);

        if ($id === true) {
            $this->mapper->reset();
            $creating = true;
        } else {
            $where = isset($this->rules['filter']) ? $this->rules['filter'] : array();

            $token    = isset($where[1]) ? '?' : ':__id';
            $where[0] = isset($where[0]) ? "({$where[0]}) AND $primarykey = $token" : "$primarykey = $token";

            foreach ($where as &$value) if(is_string($value)) {
                $value = Template::instance()->resolve($value);
                if($value === '') $value = null;
            }

            if (null === $id && $this->f3->exists('PARAMS.'.$param)) {
                if($token !== '?')
                    $where[$token] = $this->f3->get('PARAMS.'.$param);
                else
                    $where[] = $this->f3->get('PARAMS.'.$param);
                $this->mapper->load($where);

                if ($this->mapper->dry()) {
                    $this->f3->error(404);
                }
            } elseif (!null === $id) {
                if($token !== '?')
                    $where[$token] = $this->f3->get($id);
                else
                    $where[]       = $this->f3->get($id);
                $this->mapper->load($where);

                if ($this->mapper->dry()) {
                    $this->f3->error(404);
                }
            } else {
                $this->mapper->reset();
                $creating = true;
            }
        }

        $this->callHooks($this->afterLoadHook[0], array($this->mapper));

        if (!isset($this->rules['title'])) {
            if ($creating) {
                $this->f3->set('form.title', 'Criar '.$this->rules['name']);

                if (!$this->f3->exists('page.description')) {
                    $this->f3->set('page.description', 'Criar');
                }
            } else {
                $this->f3->set('form.title', 'Editar '.$this->rules['name']);

                if (!$this->f3->exists('page.description')) {
                    $this->f3->set('page.description', 'Editar');
                }
            }
        }

        if ($this->f3->get('VERB') == 'POST') {
            $groups = array();

            if (isset($this->rules['fields'])) {
                $groups[] = array('fields' => &$this->rules['fields']);
            } else {
                $groups = &$this->rules['groups'];
            }

            foreach ($groups as &$group) if (isset($group['fields'])) {
                foreach ($group['fields'] as $fieldname => $data) if($data['type'] === 'static') {
                    unset($group['fields'][$fieldname]);
                }
            }
            unset($group);

            $validator = Validator::createFrom(isset($this->rules['fields']) ? $this->rules['fields'] : $this->rules['groups']);

            $data = array_merge($this->mapper->cast(), $this->f3->get('POST'));
            $this->f3->set('form.values', $data);

            if ($validator->validate()) {
                $this->callHooks($this->onValidateHook, array($data));
                $files = Web::instance()->receive(null, false, false);

                $formfields = array();

                foreach ($groups as $group) {
                    if (isset($group['fields'])) {
                        foreach ($group['fields'] as $fieldname => $data) {
                            if ($data['type'] == 'password') {
                                if ($this->f3->get('POST.'.$fieldname) == '********') {
                                    $this->f3->clear('POST.'.$fieldname);
                                } else if($this->f3->get('POST.'.$fieldname)) {
                                    $this->f3->set('POST.'.$fieldname, \Bcrypt::instance()->hash($this->f3->get('POST.'.$fieldname)));
                                }
                            }
                            if ($data['type'] === 'dropdown' && $this->f3->get('POST.'.$fieldname) === '' ) {
                                $this->f3->set('POST.'.$fieldname, null);
                            }


                            $formfields[$fieldname] = $fieldname;
                        }
                    }
                }

                $post = $this->f3->get('POST');

                $this->mapper->copyFrom(array_intersect_key($post, $formfields));

                foreach ($groups as $group) {
                    if (isset($group['fields'])) {
                        foreach ($group['fields'] as $fieldname => $data) {
                            if($this->mapper->{$fieldname} === '') {
                                $this->mapper->{$fieldname} = null;
                            } else if ($data['type'] == 'checkbox') {
                                $this->mapper->{$fieldname} = implode(isset($data['delimiter']) ? $data['delimiter'] : ';', (array) $this->f3->get('POST.'.$fieldname));
                            } else if (in_array($data['type'], array('datetime', 'date', 'time'))) {
                                $this->mapper->{$fieldname} = $this->formatDatetime($this->mapper->{$fieldname}, $this->f3->get("app.{$data['type']}.format"), $this->f3->get("app.{$data['type']}.format4"));
                            } else if ($data['type'] == 'number') {
                                //echo $this->mapper->{$fieldname};
                                $this->mapper->{$fieldname} = str_replace(array($this->f3->get('app.number.thousands'), $this->f3->get('app.number.radixpoint')), array('', '.'), $this->f3->get('POST.'.$fieldname));
                                //echo $this->mapper->{$fieldname};
                                //echo $this->f3->get('POST.'.$fieldname);
                                //die();
                            }
                        }
                    }
                }

                foreach ($files as $field => $filename) {
                    $old_filepath = $this->f3->get('UPLOADS').$this->mapper->{$field};

                    if ($this->mapper->{$field} && file_exists($old_filepath)) {
                        unlink($old_filepath);
                    }

                    $this->mapper->{$field} = $filename;
                }

                if ($creating && $this->mapper->exists('created_on')) {
                    $this->mapper->created_on = date('Y-m-d H:i:s');
                }

                if ($creating && $this->mapper->exists('created_by')) {
                    $this->mapper->created_by = $this->f3->get('SESSION.admin.id');
                }

                if ($creating && $this->mapper->exists('owner_id')) {
                    $this->mapper->owner_id = $this->f3->get('SESSION.admin.id');
                }

                if($this->mapper->exists('updated_on')) {
                    $this->mapper->updated_on = date('Y-m-d H:i:s');
                }

                if($this->mapper->exists('updated_by')) {
                    $this->mapper->updated_by = $this->f3->get('SESSION.admin.id');
                }

                $this->callHooks($this->beforeSaveHook[0], array($this->mapper));

                $this->mapper->save();

                $this->callHooks($this->afterSaveHook[0], array($this->mapper));

                //process tables
                $submappers = array();
                foreach ($groups as $groupname => $group) {
                    if (isset($group['table'])) {
                        $groupmapper = $this->mappers[$groupname];
                        $foreignkey = $group['foreignkey'];
                        $subpk      = isset($group['primarykey']) ? $group['primarykey'] : 'id';
                        $subids     = array();
                        $whitelist  = array();
                        $subfilter  = isset($group['filter']) ? $group['filter'] : array();

                        foreach ($group['table'] as $fieldname => $data) if(!isset($data['type']) || $data['type'] !== 'static') {
                            $whitelist[$fieldname]  = $fieldname;
                        }
                        if(isset($group['order'])) {
                            $whitelist[$group['order']] = null;
                        }

                        $submappers[$groupname] = array();
                        if($this->f3->exists('POST.'.$groupname))
                        foreach ($this->f3->get('POST.'.$groupname) as $idx => $post) {
                            $subcreating = false;
                            $submapper = clone $groupmapper;
                            if (isset($post[$subpk]) && $post[$subpk]) {
                                $submapper->load(array($subpk.' = ?', $post[$subpk]));

                                if ($submapper->dry()) {
                                    $this->f3->error(500, 'child record not found');
                                }

                                if(isset($group['allow_edit']) && !$group['allow_edit']) {
                                    $subids[] = $submapper->{$subpk};
                                    continue;
                                }
                            } else if(!isset($group['allow_insert']) || $group['allow_insert']) {
                                $submapper->reset();
                                $subcreating = true;
                            } else {
                                continue;
                            }
                            $submappers[$groupname][] = $submapper;

                            if ($subcreating && $submapper->exists('created_on')) {
                                $submapper->created_on = date('Y-m-d H:i:s');
                            }

                            if ($subcreating && $submapper->exists('created_by')) {
                                $submapper->created_by = $this->f3->get('SESSION.admin.id');
                            }

                            if ($subcreating && $submapper->exists('owner_id')) {
                                $submapper->owner_id = $this->f3->get('SESSION.admin.id');
                            }

                            if($submapper->exists('updated_on')) {
                                $submapper->updated_on = date('Y-m-d H:i:s');
                            }

                            if($submapper->exists('updated_by')) {
                                $submapper->updated_by = $this->f3->get('SESSION.admin.id');
                            }

                            $submapper->copyFrom(array_intersect_key($this->f3->get("POST.$groupname.$idx"),  $whitelist));
                            $submapper->{$foreignkey} = $this->mapper->{$primarykey};
                            foreach ($group['table'] as $fieldname => $data) {
                                if($submapper->{$fieldname} == '') {
                                    $submapper->{$fieldname} = null;
                                } else if ($data['type'] == 'checkbox') {
                                    $submapper->{$fieldname} = implode(isset($data['delimiter']) ? $data['delimiter'] : ';', (array)$submapper->{$fieldname});
                                } else if (in_array($data['type'], array('datetime', 'date', 'time'))) {
                                    $submapper->{$fieldname} = $this->formatDatetime($submapper->{$fieldname}, $this->f3->get("app.{$data['type']}.format"), $this->f3->get("app.{$data['type']}.format4"));
                                } else if ($data['type'] == 'number') {
                                    $submapper->{$fieldname} = str_replace(array($this->f3->get('app.number.thousands'), $this->f3->get('app.number.radixpoint')), array('', '.'), $submapper->{$fieldname});
                                }
                            }

                            if($submapper->changed()) {
                                $this->callHooks($this->beforeSaveHook[$groupname], array($submapper, $this->mapper));

                                $submapper->save();

                                $this->callHooks($this->afterSaveHook[$groupname], array($submapper, $this->mapper));
                            }

                            $subids[] = $submapper->{$subpk};
                        }

                        if(!isset($group['allow_delete']) || $group['allow_delete']) {
                            if(count($subfilter))
                                $subfilter[0] .= " AND $foreignkey = ?";
                            else
                                $subfilter[0] = "$foreignkey = ?";

                            $subfilter[] = $this->mapper->{$primarykey};

                            foreach ($groupmapper->find($subfilter) as $row) {
                                if (!in_array($row->{$subpk}, $subids)) {
                                    $row->erase();
                                }
                            }
                        }
                    }
                }

                if ($this->logger) {
                    $this->hive['id'] = $this->mapper->{$primarykey};

                    if ($creating) {
                        $this->logger->write(Template::instance()->resolve($this->rules['log']['create'], $this->hive));
                    } else {
                        $this->logger->write(Template::instance()->resolve($this->rules['log']['edit'], $this->hive));
                    }
                }

                if ($creating) {
                    flash('success', Template::instance()->resolve($this->rules['msg']['create']['success'], $this->hive));
                } else {
                    flash('success', Template::instance()->resolve($this->rules['msg']['edit']['success'], $this->hive));
                }

                if (isset($this->rules['table'])) {
                    $this->f3->reroute($this->f3->alias($this->rules['table'], $this->mapper->cast()));
                } else {
                    $this->f3->reroute('@admin_dashboard');
                }

                $this->callHooks($this->onSuccessHook, array($this->mapper, $submappers, $data));

                return;
            } else {
                $this->callHooks($this->onErrorHook, array($data));

                if ($creating) {
                    flash('error', Template::instance()->resolve($this->rules['msg']['create']['error'], $this->hive));
                } else {
                    flash('error', Template::instance()->resolve($this->rules['msg']['edit']['error'], $this->hive));
                }
            }

            $this->f3->set('form.errors', $validator->errors());
        } else {
            $this->mapper->copyTo('form.values');

            $groups = array();

            if (isset($this->rules['fields'])) {
                $groups[] = array('fields' => $this->rules['fields']);
            } else {
                $groups = $this->rules['groups'];
            }

            foreach ($groups as $groupname => $group) {
                if (isset($group['fields'])) {
                    foreach ($group['fields'] as $fieldname => $data) {
                        if ($data['type'] == 'checkbox') {
                            $this->f3->set('form.values.'.$fieldname, explode(isset($data['delimiter']) ? $data['delimiter'] : ';', $this->mapper->{$fieldname}
                            ));
                        }

                        if ($data['type'] == 'number') {
                            $this->f3->set('form.values.'.$fieldname, is_numeric($this->mapper->{$fieldname}) ? number_format($this->mapper->{$fieldname}, isset($data['digits']) ? $data['digits'] : 0, $this->f3->get('app.number.radixpoint'), $this->f3->get('app.number.thousands')) : null);
                        }

                        if ($data['type'] == 'static' && isset($data['format']) && in_array($data['format'], array('datetime', 'date', 'time')) && $this->mapper->{$fieldname} === null) {
                            $this->f3->set('form.values.'.$fieldname, $this->formatDatetime($this->mapper->{$fieldname}, $this->f3->get("app.{$data['format']}.format4"), $this->f3->get("app.{$data['format']}.format")));
                        }

                        if (in_array($data['type'], array('datetime', 'date', 'time')) && $this->mapper->{$fieldname} !== null) {
                            $this->f3->set('form.values.'.$fieldname, $this->formatDatetime($this->mapper->{$fieldname}, $this->f3->get("app.{$data['type']}.format4"), $this->f3->get("app.{$data['type']}.format")));
                        }
                    }
                } elseif (isset($group['table'])) {
                    $foreignkey = $group['foreignkey'];
                    $subpk      = isset($group['primarykey']) ? $group['primarykey'] : 'id';
                    $orderfield = isset($group['order']) ? $group['order'] : $subpk;
                    $subfilter  = isset($group['filter']) ? $group['filter'] : array();

                    if(count($subfilter))
                        $subfilter[0] .= " AND $foreignkey = ?";
                    else
                        $subfilter[0] = "$foreignkey = ?";

                    $subfilter[] = $this->mapper->{$primarykey};

                    $submapper = $this->mappers[$groupname];
                    foreach ($submapper->find($subfilter, array('order' => $orderfield)) as $idx => $row) {
                        $this->callHooks($this->afterLoadHook[$groupname], array($row, $this->mapper));

                        $idx++;
                        $row->copyTo("form.values.$groupname.$idx");

                        foreach ($group['table'] as $fieldname => $data) {
                            if ($data['type'] == 'checkbox') {
                                $this->f3->set("form.values.$groupname.$idx.$fieldname", explode(isset($data['delimiter']) ? $data['delimiter'] : ';', $row->{$fieldname}));
                            }

                            if ($data['type'] == 'number') {
                                $this->f3->set("form.values.$groupname.$idx.$fieldname", is_numeric($row->{$fieldname}) ? number_format($row->{$fieldname}, isset($data['digits']) ? $data['digits'] : 0, $this->f3->get('app.number.radixpoint'), $this->f3->get('app.number.thousands')) : null);
                            }

                            if ($data['type'] == 'static' && isset($data['format']) && in_array($data['format'], array('datetime', 'date', 'time')) && $row->{$fieldname} !== null) {
                                $this->f3->set("form.values.$groupname.$idx.$fieldname", $this->formatDatetime($row->{$fieldname}, $this->f3->get("app.{$data['format']}.format4"), $this->f3->get("app.{$data['format']}.format")));
                            }

                            if (in_array($data['type'], array('datetime', 'date', 'time')) && $row->{$fieldname} !== null) {
                                $this->f3->set("form.values.$groupname.$idx.$fieldname", $this->formatDatetime($row->{$fieldname}, $this->f3->get("app.{$data['type']}.format4"), $this->f3->get("app.{$data['type']}.format")));
                            }
                        }
                    }
                }
            }
        }

        //$f3->push('breadcrumbs', );

        echo Template::instance()->render('hwapx/admin/admin/form.html');
    }

    public function setMapper($mapper)
    {
        $this->mapper = $mapper;
    }

    public function setRules($rules)
    {
        $this->rules = array_merge($this->default_rules, $rules);
    }

    public static function instance($mapper = null, $rules = array(), $logger = null)
    {
        return new static($mapper, $rules, $logger);
    }

    public function save($id = null)
    {
        return $this->handle($id);
    }

    public function beforeSave($func, $table = 0)
    {
        $this->beforeSaveHook[$table][] = $func;
        return $this;
    }

    public function afterLoad($func, $table = 0)
    {
        $this->afterLoadHook[$table][] = $func;
        return $this;
    }

    public function afterSave($func, $table = 0)
    {
        $this->afterSaveHook[$table][] = $func;
        return $this;
    }

    public function onValidate($func)
    {
        $this->onValidateHook[] = $func;
        return $this;
    }

    public function onError($func)
    {
        $this->onErrorHook[] = $func;
        return $this;
    }

    public function onSucces($func)
    {
        $this->onSuccessHook[] = $func;
        return $this;
    }

    protected function callHooks($hooks, $args) {
        foreach ($hooks as $func) {
            call_user_func_array($func, $args);
        }
    }

    protected function formatDatetime($time, $from, $to = 'Y-m-d H:i:s') {
        $dt = \DateTime::createFromFormat($from, $time);
        if(!$dt) {
            $dt = new \Datetime($time);
        }
        return $dt->format($to);
    }

    protected function log($action, $table, $old_data, $new_data = null) {
        /*$mapper = new Mapper($this->f3->get('db'), 'hwapx_audit');
        $mapper->reset();
        $mapper->done_at = date('Y-m-d H:i:s');
        $mapper->done_by = $this->f3->get('SESSION.admin.id');
        $mapper->done_by_data = json_encode($this->f3->get('SESSION.admin'));
        $mapper->action    = $action;
        $mapper->table     = $table;
        $mapper->record_id = $old_data['id'];
        $mapper->old_data  = json_encode($old_data);
        $mapper->new_data  = json_encode($new_data);
        $mapper->server    = json_encode($_SERVER);
        $mapper->save();*/
    }
}
