<?php
namespace App\Library\Xml;

use App\Models\Title;
use App\Models\Description;
use App\Models\License;
use App\Models\Person;
use App\Models\File;
use App\Models\GeolocationBox;
use App\Models\Coverage;
use App\Models\Subject;

/**
 * DatasetExtension short summary.
 *
 * DatasetExtension description.
 *
 * @version 1.0
 * @author kaiarn
 */
trait DatasetExtension
{
    protected $externalFields = array(
        'TitleMain' => array(
            'model' => Title::class,
            'options' => array('type' => ['main', 'alternative', 'sub', 'translated', 'other']),
            'fetch' => 'eager'
        ),
        'TitleAbstract' => array(
            'model' => Description::class,
            'options' => array('type' => ['abstract', 'methods', 'technical_info', 'series_information', 'other']),
            'fetch' => 'eager'
        ),
        'Licence' => array(
            'model' => License::class,
            'through' => 'link_documents_licences',
            'relation' => 'licenses',
            'fetch' => 'eager'
        ),
        'PersonAuthor' => array(
            'model' => Person::class,
            'through' => 'link_documents_persons',
            'pivot' => array('role' => 'author'),
                            //'sort_order' => array('sort_order' => 'ASC'),   // <-- We need a sorted authors list.
            //'sort_field' => 'SortOrder',
            'relation' => 'persons',
            'fetch' => 'eager'
        ),
        'PersonContributor' => array(
            'model' => Person::class,
            'through' => 'link_documents_persons',
            'pivot' => array('role' => 'contributor'),
            //                'sort_order' => array('sort_order' => 'ASC'),   // <-- We need a sorted authors list.
            //'sort_field' => 'SortOrder',
            'relation' => 'persons',
            'fetch' => 'eager'
        ),
        'Subject' => array(
            'model' => Subject::class,
            'relation' => 'subjects',
            'fetch' => 'eager'
        ),
        'File' => array(
            'model' => File::class,
            'relation' => 'files',
            'fetch' => 'eager'
        ),
        // 'GeolocationBox' => array(
        //     'model' => GeolocationBox::class,
        //     'relation' => 'geolocation',
        //     'fetch' => 'eager'
        // ),
        'Coverage' => array(
            'model' => Coverage::class,
            'relation' => 'coverage',
            'fetch' => 'eager'
        ),
    );

    protected $internalFields = array();

    protected $fields = array();

    protected function initFields()
    {
        $fields = array(
            "Id",
            "ContributingCorporation",
            "CreatingCorporation",
            "Language",
            "PublishedDate", "PublishedYear",
            "PublisherName", "PublisherPlace",
            "PublicationState",
            "ServerDateCreated",
            "ServerDateModified",
            "ServerDatePublished",
            "ServerDateDeleted",
            "ServerState",
            "Type",
            "BelongsToBibliography",
            "EmbargoDate"
        );

        foreach ($fields as $fieldname) {
            $field = new Field($fieldname);
            $this->addField($field);
        }

        foreach (array_keys($this->externalFields) as $fieldname) {
            $field = new Field($fieldname);
            $field->setMultiplicity('*');
            $this->addField($field);
        }

             // Initialize available date fields and set up date validator
            // if the particular field is present
        $dateFields = array(
            'ServerDateCreated', 'PublishedDate',
            'ServerDateModified', 'ServerDatePublished', 'ServerDateDeleted', 'EmbargoDate'
        );
        foreach ($dateFields as $fieldName) {
            $this->getField($fieldName)
                ->setValueModelClass('Carbon');
        }

            // $this->_fetchValues();
    }

    /**
     * Get a list of all fields attached to the model. Filters all fieldnames
     * that are defined to be inetrnal in $internalFields.
     *
     * @see    Opus_Model_Abstract::internalFields
     * @return array    List of fields
     */
    public function describe()
    {
        return array_diff(array_keys($this->fields), $this->internalFields);
    }

    public function addField(Field $field)
    {
        $fieldname = $field->getName();
        if (isset($fieldname, $this->externalFields[$fieldname])) {
            $options = $this->externalFields[$fieldname];

            // set ValueModelClass if a through option is given
            if (isset($options['model'])) {
                $field->setValueModelClass($options['model']);
            }
            // set LinkModelClass if a through option is given
            //if (isset($options['through']))
            //{
            //    $field->setLinkModelClass($options['through']);
            //}
        }

        $this->fields[$field->getName()] = $field;
        $field->setOwningModelClass(get_class($this));
        return $this;
    }

    // public function getField($name)
    // {
    //     return $this->_getField($name);
    // }

    /**
     * Return a reference to an actual field.
     *
     * @param string $name Name of the requested field.
     * @return Field The requested field instance. If no such instance can be found, null is returned.
     */
    public function getField($name)
    {
        if (isset($this->fields[$name])) {
            return $this->fields[$name];
        } else {
            return null;
        }
    }

    public function fetchValues()
    {
        $this->initFields();
        foreach ($this->fields as $fieldname => $field) {
            if (isset($this->externalFields[$fieldname]) === true) {
                $fetchmode = 'lazy';
                if (isset($this->externalFields[$fieldname]['fetch']) === true) {
                    $fetchmode = $this->externalFields[$fieldname]['fetch'];
                }

                if ($fetchmode === 'lazy') {
                    // Remember the field to be fetched later.
                    $this->_pending[] = $fieldname;
                    // Go to next field
                    continue;
                } else {
                    // Immediately load external field if fetching mode is set to 'eager'
                    $this->loadExternal($fieldname);
                }
            } else {
                // Field is not external an gets handled by simply reading
                $property_name = self::convertFieldnameToColumn($fieldname);
                //$test = $this->server_date_created;
                $fieldval = $this->{$property_name};

                // explicitly set null if the field represents a model except for dates
                if (null !== $field->getValueModelClass()) {
                    if (true === empty($fieldval)) {
                        $fieldval = null;
                    } else {
                        $fieldval = new \Carbon\Carbon($fieldval);
                    }
                }

                $field->setValue($fieldval);
            }
        }
    }

    public static function convertFieldnameToColumn($fieldname)
    {
        return strtolower(preg_replace('/(?!^)[[:upper:]]/', '_\0', $fieldname));
    }

    protected function loadExternal($fieldname)
    {
        $field = $this->fields[$fieldname];

        $modelclass = $field->getLinkModelClass();
        if (!isset($modelclass)) {
            // For handling a value model, see 'model' option.
            $modelclass = $field->getValueModelClass();
        }

        $tableclass = new $modelclass();//::getTableGatewayClass();
         //   $table = Opus_Db_TableGateway::getInstance($tableclass);
        $select = $tableclass->query();//->where("document_id", $this->id);;

        // If any declared constraints, add them to query
        if (isset($this->externalFields[$fieldname]['options'])) {
            $options = $this->externalFields[$fieldname]['options'];
            foreach ($options as $column => $value) {
                // $searchString = ',';
                // if (strpos($value, $searchString) !== false) {
                // $arr = explode(",", $value);
                if (is_array($value)) {
                    $arr = $value;
                    $select->whereIn($column, $arr);
                } else {
                    $select = $select->where($column, $value);
                }
            }
        }


        // Get dependent rows
        $result = array();
        $datasetId = $this->id;

        $rows = array();
        if (isset($this->externalFields[$fieldname]['through'])) {
            $relation = $this->externalFields[$fieldname]['relation'];
            //$rows = $select->datasets
            ////->orderBy('name')
            //->get();
            //$licenses = $select->with('datasets')->get();
            //$rows =  $supplier->datasets;
            $rows = $this->{$relation};
            //if (isset($this->externalFields[$fieldname]['pivot']))
            //{
            //  $pivArray = $this->externalFields[$fieldname]['pivot'];
            //  $rows = $rows->wherePivot('role', $pivArray['role']);
            //}
        } else {
            $rows = $select->whereHas('dataset', function ($q) use ($datasetId) {
                $q->where('id', $datasetId);
            })->get();
        }

        foreach ($rows as $row) {
            // //$newModel = new $modelclass($row);
            // $result[] = $row;//->value;

            $attributes = array_keys($row->getAttributes());
            $objArray = [];
            foreach ($attributes as $property_name) {
                $fieldName = self::convertColumnToFieldname($property_name);
                // $field =new Field($fieldName);
                $fieldval = $row->{$property_name};
                // $field->setValue($fieldval);
                // $this->_mapField($field, $dom, $rootNode);
                $objArray[$fieldName] = $fieldval;
            }
            $result[] = $objArray;
        }

         // Set the field value
        $field->setValue($result);
    }

     //snakeToCamel
    public static function convertColumnToFieldname($columnname)
    {
         //return lcfirst(str_replace(' ', '', ucwords(str_replace('_', ' ', $columnname))));
        return str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $columnname)));
    }
}
