<?php

namespace Velis\Model\DataObject;

use ArrayObject;
use Velis\Arrays;
use Velis\Dojo\Data as DojoData;
use Velis\Exception as VelisException;
use Velis\Model\DataObject;
use Velis\Output;

/**
 * Collections functions functionality
 * @author Olek Procki <olo@velis.pl>
 */
trait CollectionTrait
{
    /**
     * Sort option
     * @var string
     */
    protected static $_sortMode = self::SORT_ID;


    /**
     * List of fields available in DataStoreItem (via Velis\Dojo\Data)
     * @var array
     */
    protected static $_dataStoreItemFields = [];


    /**
     * Returns collection in JSON
     *
     * @param array $collection
     * @param bool $unfiltered
     * @param string $collectionName
     * @return string
     * @throws VelisException
     */
    public static function toJsonCollection($collection, $unfiltered = false, $collectionName = null)
    {
        $items = [];

        if (is_array($collection)) {
            foreach ($collection as $key => $item) {
                if (!$item instanceof DataObject) {
                    VelisException::raise(
                        get_called_class() . '::toJsonCollection() collection items must be instances of DataObject'
                    );
                    continue;
                }
                $items[$key] = $item->_filterJsonFields();
            }
        }

        if (!$collectionName) {
            return Output::jsonEncode($items);
        } else {
            $data = array();
            $data[$collectionName] = $items;

            return Output::jsonEncode($data);
        }
    }


    /**
     * Returns JSON collection for API response
     *
     * @param array $collection
     * @param bool $encode
     * @return string|array
     * @throws VelisException
     */
    public static function toApiCollection($collection, $encode = false)
    {
        $items = array();
        if (is_array($collection)) {
            foreach ($collection as $key => $item) {
                if (!$item instanceof DataObject) {
                    VelisException::raise(
                        get_called_class() . '::toApiCollection() collection items must be instances of DataObject'
                    );
                    continue;
                }
                $items[$key] = $item->getApiData();
            }
        }

        if ($encode) {
            return Output::jsonEncode($items);
        } else {
            return $items;
        }
    }


    /**
     * Checks if object is element of collection
     *
     * @param DataObject[] $collection
     * @return bool
     */
    public function belongs(array $collection)
    {
        foreach ($collection as $item) {
            if ($item->id() == $this->id()) {
                return true;
            }
        }

        return false;
    }


    /**
     * Returns collection id list
     *
     * @param array $collection
     * @return array
     */
    public static function getCollectionIds(array $collection)
    {
        $idList = [];
        $instance = new static();

        foreach ($collection as $item) {
            if ($item instanceof static) {
                $idList[] = $item->id();
            } elseif (is_array($item)) {
                if (array_key_exists($instance->_getPrimaryKeyField(), $item)) {
                    $idList[] = $item[$instance->_getPrimaryKeyField()];
                }
            } elseif ($item instanceof ArrayObject) {
                if ($item->offsetExists($instance->_getPrimaryKeyField())) {
                    $idList[] = $item[$instance->_getPrimaryKeyField()];
                }
            }
        }

        return $idList;
    }


    /**
     * Returns list in json/datastore
     * @param DataObject[] $collection
     * @param string $label
     * @param int|null $totalCount
     * @return DojoData
     */
    public static function getDataStore(array $collection, $label = 'name', $totalCount = null)
    {
        $instance = new static();

        $dataContainer = new DojoData(
            is_array($instance->_getPrimaryKeyField()) ? 'id' : $instance->_getPrimaryKeyField(),
            $collection,
            $label
        );

        if ($totalCount !== null) {
            $dataContainer->setMetadata('numRows', $totalCount);
        }

        return $dataContainer;
    }


    /**
     * Returns data ready to use with datastore (\Velis\Dojo\Data)
     *
     * @param bool $unfiltered
     * @return array
     */
    public function getDataStoreItem($unfiltered = false): array
    {
        if (
            !$unfiltered
            && static::$_dataStoreItemFields
            && is_array(static::$_dataStoreItemFields)
        ) {
            $allowedFields = static::$_dataStoreItemFields;

            // dojo's default label field
            if (!in_array('name', $allowedFields)) {
                $allowedFields[] = 'name';
            }

            return Arrays::extractFields(
                $this,
                array_intersect(
                    array_keys($this->getArrayCopy()),
                    $allowedFields
                )
            );
        } else {
            return $this->getArrayCopy();
        }
    }


    /**
     * Sorts elements
     * @param DataObject[] $collection
     * @return array
     */
    public static function sort($collection)
    {
        $result = $collection;
        usort($result, array(__CLASS__, 'compareTwo'));

        $sorted = [];
        foreach ($result as $item) {
            $sorted[$item->identifier()] = $item;
        }

        return $sorted;
    }


    /**
     * Returns collection of objects with only name field
     * @param array $items
     * @param bool $assoc
     * @param string $column
     * @return array
     * @throws VelisException
     */
    public static function listNames($items, $assoc = false, $column = 'name')
    {
        $instance = new static();
        $keyField = $instance->_getPrimaryKeyField();
        if (Arrays::hasColumn($items, $keyField)) {
            $items = Arrays::getColumn($items, $keyField);
        }

        $result = self::listAll([$keyField => $items], null, [$keyField, $column]);

        if ($assoc) {
            return array_column((array) $result, $column, $keyField);
        }

        return $result;
    }
}
