<?php

namespace Velis\Model\DataObject;

use Velis\Arrays;
use Velis\Exception as VelisException;
use Velis\Output;

/**
 * Serialization functionality
 * @author Olek Procki <olo@velis.pl>
 */
trait SerializeTrait
{
    /**
     * List of fields exported in API responses
     * @var array
     */
    protected static $_apiFields = array();


    /**
     * List of permited fields exported by toJSON method
     * @var array
     */
    protected static $_jsonFields = array();


    /**
     * List of fields excluded by toJSON method
     * @var array
     */
    protected static $_jsonExcludedFields = array();



    /**
     * Converts object to JSON notation
     *
     * @param bool $unfiltered
     * @return string
     */
    public function toJson($unfiltered = false)
    {
        if (!$unfiltered && (static::$_jsonFields || static::$_jsonExcludedFields)) {
            return Output::jsonEncode($this->_filterJsonFields());
        } else {
            return Output::jsonEncode($this->getArrayCopy());
        }
    }


    /**
     * Returns filtered data for JSON encoding
     * @return array
     */
    protected function _filterJsonFields()
    {
        if (static::$_jsonFields && static::$_jsonExcludedFields) {
            $class = get_called_class();
            VelisException::raise(
                'Cannot have both '
                    . $class . '::$_jsonFields and '
                    . $class . '::$_jsonExcludedFields defined!'
            );
        }

        if (static::$_jsonFields) {
            if (!is_array(static::$_jsonFields)) {
                VelisException::raise(
                    get_called_class() . '::$_jsonFields must be an array'
                );
                return $this->getArrayCopy();
            }

            return Arrays::extractFields(
                $this->getArrayCopy(),
                array_intersect(
                    array_keys($this->getArrayCopy()),
                    static::$_jsonFields
                )
            );
        } elseif (static::$_jsonExcludedFields) {
            if (!is_array(static::$_jsonExcludedFields)) {
                VelisException::raise(
                    get_called_class() . '::$_jsonExcludedFields must be an array'
                );
                return $this->getArrayCopy();
            }

            return Arrays::extractFields(
                $this->getArrayCopy(),
                array_diff(
                    array_keys($this->getArrayCopy()),
                    static::$_jsonExcludedFields
                )
            );
        } else {
            return $this->getArrayCopy();
        }
    }


    /**
     * Returns API data only
     *
     * @return array
     */
    public function getApiData()
    {
        return Arrays::extractFields(
            $this,
            $this->_getApiFields()
        );
    }


    /**
     * Returns object API fields
     * @return array
     */
    protected function _getApiFields()
    {
        if (static::$_apiFields) {
            return static::$_apiFields;
        }

        $columns = self::$_db->getColumns($this->_getTableName());
        $fields  = array_keys($columns);
        $complexTypeColumns = array_keys(
            Arrays::byValue($columns, 'data_type', 'USER-DEFINED')
        );

        if (!$complexTypeColumns) {
            return $fields;
        } else {
            $fields = [];
            foreach ($columns as $field => $colData) {
                if (!in_array($field, $complexTypeColumns)) {
                    $fields[] = $field;
                } else {
                    $typeFields = $colData['type_fields'];
                    foreach ($typeFields as $sufix) {
                        $fields[] = $field . '_' . $sufix;
                    }
                }
            }
        }
        return $fields;
    }
}
