<?php

/**
 * CGBetterUploads API
 *
 * @package CGBetterUploads
 */

namespace CGBetterUploads;

/**
 * A class to represent a single field definition.
 *
 * This is an immutable class
 *
 * @property-read int $id An automatically assigned id for this field definition
 * @property-read string $name The name of this property definition
 * @property-read string $type The type of the property definition.
 * @property-read int $iorder The sort position of this property definition
 */
class FieldDefinition
{
    /**
     * @ignore
     */
    private $_data = ['id'=>null, 'name'=>null, 'type'=>null, 'iorder'=>null ];

    /**
     * @ignore
     */
    private $_extra;

    /**
     * @ignore
     */
    public function __get( string $key )
    {
        switch( $key ) {
        case 'id':
        case 'iorder':
            if( !is_null($this->_data[$key]) ) return (int) $this->_data[$key];
            break;
        case 'name':
        case 'type':
            if( !is_null($this->_data[$key]) ) return trim($this->_data[$key]);
            break;
        case 'options_text':
            if( isset($this->_extra[$key]) ) return trim($this->_extra[$key]);
        case 'options':
            // if we have options, we parse them
            return $this->get_options();
        case 'short_type':
            return basename(str_replace('\\','/',$this->type));
        default:
            throw new \LogicException("$key is not a gettable property of ".__CLASS__);
        }
    }

    /**
     * Get extra data with this field definition.
     *
     * @param string $key
     * @param string $dflt The default value to return if the data does not exist.
     * @return mixed
     */
    public function getExtraData( string $key, string $dflt = null )
    {
        $key = trim($key);
        if( !$key ) throw new \InvalidArgumentException("Invalid key passed to ".__METHOD__);
        if( isset($this->_extra[$key]) ) return $this->_extra[$key];
        return $dflt;
    }

    /**
     * Associate extra data with this field definition.
     *
     * @param string $key
     * @param mixed $val The data to associate
     * @return FieldDefinition
     */
    public function setExtraData( string $key, $val ) : FieldDefinition
    {
        $key = trim($key);
        if( !$key ) throw new \InvalidArgumentException("Invalid key passed to ".__METHOD__);
        $obj = clone $this;
        $obj->_extra[$key] = $val;
        return $obj;
    }

    /**
     * @ignore
     */
    public function __set( string $key, $val )
    {
        throw new \LogicExceptin("$key is not a settable property of ".__CLASS__);
    }

    /**
     * @ignore
     */
    public function get_options()
    {
        $txt = $this->options_text;
        if( !$txt ) return;
        $txt = html_entity_decode($txt);

        // split by lines
        $out = null;
        $lines = explode("\n", $txt);
        foreach( $lines as $line ) {
            $line = trim($line);
            if( !$line ) continue;
            $key = $txt = null;
            if( strpos($line,'=') === FALSE ) {
                $key = $txt = $line;
            } else {
                list($key,$txt) = explode('=',$line,2);
                $key = trim($key);
                $txt = trim($txt);
                if( !$key && $txt ) {
                    // handles =foo
                    $key = $txt;
                }
                else if( $key && !$txt ) {
                    // handles foo=
                    $txt = $key;
                }
            }
            $out[$key] = $txt;
        }
        return $out;
    }

    /**
     * Get the extra data encoded as a string
     *
     * @internal
     * @return string The json_encoded extra data
     */
    public function get_extra() : string
    {
        return json_encode($this->_extra);
    }

    /**
     * Adjust the order of this field definition
     *
     * @internal
     * @param int $iorder The sort order.  Must be greater than 0
     * @return FieldDefinition
     */
    public function withIOrder( int $iorder ) : FieldDefinition
    {
        if( $iorder < 1 ) throw new \InvalidArgumentException("Invalid value passed to ".__METHOD__);

        $obj = clone $this;
        $obj->_data['iorder'] = $name;
        return $obj;
    }

    /**
     * Adjust the name of the field definition
     *
     * @param string $name
     * @return FieldDefinition
     */
    public function withName( string $name ) : FieldDefinition
    {
        $name = trim($name);
        if( !$name ) throw new \InvalidArgumentException("Invalid name passed to ".__METHOD__);

        $obj = clone $this;
        $obj->_data['name'] = $name;
        return $obj;
    }

    /**
     * Output the record as an array
     *
     * @internal
     * @return array
     */
    public function to_array() : array
    {
        $out = $this->_data;
        $out['extra'] = $this->get_extra();
        return $out;
    }

    /**
     * Construct a field definition object from an array
     *
     * @internal
     * @param array $in
     * @return FieldDefinition
     */
    public static function from_array( array $in ) : FieldDefinition
    {
        $obj = new self;
        foreach( $in as $key => $val ) {
            switch( $key ) {
            case 'id':
            case 'iorder':
                $obj->_data[$key] = (int) $val;
                break;
            case 'name':
            case 'type':
                $obj->_data[$key] = trim($val);
                break;
            case 'extra':
                $obj->_extra = json_decode($val,TRUE);
                break;
            }
        }
        return $obj;
    }

} // class
