<?php
defined( 'ABSPATH' ) or exit;   // show a blank page if try to access this file directly

require_once( EWZ_PLUGIN_DIR . "classes/ewz-layout.php");
require_once( EWZ_PLUGIN_DIR . "classes/validation/ewz-input.php");

/* Validation for the Layouts page */

class Ewz_Field_Input extends Ewz_Input
{

    /**
     * @param array<mixed> $form_data
     *
     * @return void
     */
    function __construct( $form_data ) {
        parent::__construct( $form_data );

        /*** rules('type') specifies the function called to validate, defined either here or in ewz-input.php */
        /*** rules('val') defines extra args to pass */

        $this->rules = array(
            'field_id'       => array( 'type' => 'to_seq',      'req' => false, 'val' => ['len' => 10] ),
            'field_header'   => array( 'type' => 'to_string',   'req' => true,  'val' => ['len' => 50] ),
            'field_type'     => array( 'type' => 'limited',     'req' => true,  'val' => array( 'img', 'str', 'opt', 'rad', 'chk', 'lab' ) ),
            'field_ident'    => array( 'type' => 'ident',       'req' => true,  'val' => ['len' => 15] ),
            'fdata'          => array( 'type' => 'v_fdata',     'req' => true,  'val' => '' ),
            'ss_column'      => array( 'type' => 'to_int1',     'req' => false, 'val' => ['len' => 5] ),
            'required'       => array( 'type' => 'to_bool',     'req' => false, 'val' => '' ),
            'append'         => array( 'type' => 'to_bool',     'req' => false, 'val' => '' ),
            'all_items'      => array( 'type' => 'to_bool',     'req' => false, 'val' => '' ),
                             );
        $this->validate();
    }

    /**
     * @return boolean  true on success
     */
    function validate(){
        parent::validate();

        if ( !array_key_exists( 'required', $this->input_data ) ) {
            $this->input_data['required'] = false;
        }
        if ( !array_key_exists( 'append', $this->input_data ) ) {
            $this->input_data['append'] = false;
        }
        if( 'lab' ==  $this->input_data['field_type'] ){
            if( !array_key_exists( 'showlabel', $this->input_data['fdata'] ) || !$this->input_data['fdata']['showlabel'] ){
                $this->input_data['fdata']['showlabel'] = false;
            }
        }
        if ( !array_key_exists( 'all_items', $this->input_data ) ) {
            $this->input_data['all_items'] = false;
        }
        if( 'img' ==  $this->input_data['field_type'] ){
            if( !array_key_exists( 'canrotate', $this->input_data['fdata'] ) || !$this->input_data['fdata']['canrotate'] ){
                $this->input_data['fdata']['canrotate'] = false;
            }
            if( !array_key_exists( 'mustfill', $this->input_data['fdata'] ) || !$this->input_data['fdata']['mustfill'] ){
                $this->input_data['fdata']['mustfill'] = false;
            }
        }
        return true;
   }

    //****** All v_.... functions must return a boolean or raise an exception **************/
    /**
     * @param array<mixed> $fdata
     * @param string $arg
     * @return boolean  true on success
     */
    function v_fdata( &$fdata, $arg='' ){
        if( !is_array( $fdata ) ){
             throw new EWZ_Exception( 'Invalid value for fdata' );
        }

        switch ( $this->input_data['field_type'] ) {
        case 'opt':
            self::valid_opt_input( $fdata );
            break;
        case 'str':
            self::valid_str_input( $fdata );
            break;
        case 'img':
            self::valid_img_input( $fdata );
            break;
        case 'rad':
            self::valid_rad_input( $fdata );
            break;
        case 'chk':
            self::valid_chk_input( $fdata );
            break;
        case 'lab':
            self::valid_lab_input( $fdata );
            break;
        default:   throw new EWZ_Exception( 'Invalid value for field type: ' . $this->input_data['field_type'] );
        }

        return true;
    }

    /**
     * Validate checkbox field input
     *
     * @param  array<mixed>    $field     input checkbox field to check
     * @return boolean  true or raise an exception
     */
    private static function valid_chk_input( &$field )
    {
        $all_field_data = array('chkmax', 'chklabel', 'xchklabel');
        foreach ( $field as $name => $val ) {
            if( !is_string( $val ) ){
                 throw new EWZ_Exception( "Bad input data format for $name ");
            }
            if ( !in_array( $name, $all_field_data ) ) {
                throw new EWZ_Exception( "Bad input data for $name ");
            }

            if ( 'chkmax' == $name ) {
                if ( !preg_match( '/^\d*$/', $val ) ) {
                    throw new EWZ_Exception( "Invalid value for $name: '$val'" );
                }
                if( !$val ){
                    $field[$name] = 0;  // changing, cant use $val on left
                }
                $field[$name] = (int)$val;      // changing, cant use $val on left
            }
            if ( 'chklabel' == $name ) {
                if ( preg_match( '/[^A-Za-z0-9_\.\-\ ]/', $val ) ) {
                    throw new EWZ_Exception( "Invalid value for checkbox label" );
                }
            }
            if ( 'xchklabel' == $name ) {
                if ( preg_match( '/[^A-Za-z0-9_\.\-\ ]/', $val ) ) {
                    throw new EWZ_Exception( "Invalid value for checkbox label" );
                }
            }
        }
        return true;
    }

    /**
     * Validate radiobutton field input
     *
     * @param  array<mixed>  $field     input radiobutton field to check
     * @return boolean  true or raise an exception
     */
    private static function valid_rad_input( &$field )
    {
        $all_field_data = array( 'radlabel', 'xradlabel');

        foreach ( $field as $name => $val ) {
            if( !is_string( $val ) ){
                 throw new EWZ_Exception( "Bad input data format for $name ");
            }
            if ( !in_array( $name, $all_field_data ) ) {
                throw new EWZ_Exception( "Bad input data for $name ");
            }
            if ( 'radlabel' == $name ) {
                if ( preg_match( '/[^A-Za-z0-9_\.\-\ ]/', $val ) ) {
                    throw new EWZ_Exception( "Invalid value for radiobutton label" );
                }
            }
            if ( 'xradlabel' == $name ) {
                if ( preg_match( '/[^A-Za-z0-9_\.\-\ ]/', $val ) ) {
                    throw new EWZ_Exception( "Invalid value for radiobutton label" );
                }
            }
        }
        return true;
    }

    /**
     * Validate string field input
     *
     * @param  array<mixed>  $field     input string field to check
     * @return  boolean  true on success
     */
    private static function valid_str_input( &$field )
    {
        $req_field_data = array('maxstringchars');
        $all_field_data = array('maxstringchars', 'fieldwidth', 'ss_col_fmt', 'textrows');
        foreach ( $req_field_data as $req ) {
            if ( !array_key_exists( $req, $field )|| preg_match( '/^ *$/', $field[$req] ) ) {
                throw new EWZ_Exception( "Missing required item $req for a text input" );
            }
        }
        foreach ( $field as $name => $val ) {
            if( !is_string( $val ) ){
                 throw new EWZ_Exception( "Bad input data format for $name ");
            }
            if ( !in_array( $name, $all_field_data ) ) {
                throw new EWZ_Exception( "Bad input data for $name ");
            }

            if ( 'maxstringchars' == $name ) {
                if ( !preg_match( '/^\d*$/', $val ) ) {
                    throw new EWZ_Exception( "Invalid value for $name: '$val'" );
                }
                if( !$val ){
                    $field[$name] = EWZ_MAX_STRING_LEN;  // changing, cant use $val on left
                }
                $field[$name] = (int)$val;      // changing, cant use $val on left

            }
            if ( ( 'fieldwidth' == $name ) ) {
                if ( !preg_match( '/^\d*$/', $val ) ) {
                    throw new EWZ_Exception( "Invalid value for $name: '$val'" );
                }
                if( !$val ){
                    $field[$name] = EWZ_MAX_FIELD_WIDTH;  // changing, cant use $val on left
                }
                $field[$name] = ( int )$val;   // changing, cant use $val
            }
            if ( ( 'textrows' == $name ) ) {
                if ( !preg_match( '/^\d*$/', $val ) ) {
                    throw new EWZ_Exception( "Invalid value for $name: '$val'" );
                }
                if( !$val ){
                    $field[$name] =1;  // changing, cant use $val on left
                }
                $field[$name] = ( int )$val;   // changing, cant use $val
            }
            if ( 'ss_col_fmt' == $name ){
                if ( !preg_match( '/^-1$|^\d*$/', $val ) ) {
                    throw new EWZ_Exception( "Invalid value for $name: '$val'" );
                }
                $field[$name] = ( int )$val;    // changing, cant use $val on left
            }
        }
        return true;
    }

    /**
     * Validate image field input
     *
     * @param  array<mixed>  $field  input image field to check
     * @return boolean  true on success
     */
    private static function valid_img_input( &$field )
    {

        $imgtypes = array( 'image/jpeg', 'image/gif', 'image/png' );
        $req_field_data = array( 'max_img_w', 'max_img_h', 'ss_col_w', 'ss_col_h', 'ss_col_o', 'max_img_size',  'allowed_image_types' );
        $opt_field_data = array( 'canrotate', 'mustfill','min_longest_dim');

        foreach ( $req_field_data as $req ) {
            if( is_string( $field[$req] ) ){
                if ( !array_key_exists( $req, $field ) || preg_match( '/^ *$/', $field[$req] ) ) {
                    throw new EWZ_Exception( "Missing required item $req " );
                }
            } else {
                if ( !array_key_exists( $req, $field ) || ( count( $field[$req] ) == 0 ) ) {
                    throw new EWZ_Exception( "Missing required item $req " );
                }
            }
        }
        foreach ( $field as $name => $val ) {
            if ( !in_array( $name, $req_field_data ) &&
                 !in_array( $name, $opt_field_data ) )
                {
                    throw new EWZ_Exception( "Bad input data '$name' for image field");
                }
            if ( $name == 'max_img_w' || $name == 'max_img_h' ){
                if( !is_string( $val ) ){
                    throw new EWZ_Exception( "Bad input data format for $name ");
                }
                if ( !preg_match( '/^\d*$/', $val ) ) {
                    throw new EWZ_Exception( "Invalid value for $name: '$val'" );
                }
                if( !$val ){
                    $field[$name] = EWZ_DEFAULT_DIM;
                }
                $field[$name] = ( int ) $val;

            } elseif ( $name == 'min_longest_dim' ) {
                if( !is_string( $val ) ){
                    throw new EWZ_Exception( "Bad input data format for $name ");
                }
                if ( !preg_match( '/^\d*$/', $val ) ) {
                    throw new EWZ_Exception( "Invalid value for $name: '$val'" );
                }
                if( !$val ){
                    $val = EWZ_DEFAULT_MIN_LONGEST;
                }
                $field[$name] = ( int ) $val;
            } elseif ( $name == 'max_img_size' ) {
                if( !is_string( $val ) ){
                    throw new EWZ_Exception( "Bad input data format for $name ");
                }
                if ( !preg_match( '/^\d?\d?\.?\d+$/', $val ) ) {
                    throw new EWZ_Exception( "Invalid value for $name: '$val'"  );
                }
                if( !$val ){
                    $val = EWZ_MAX_SIZE_MB;
                }
                $field[$name] = floatval( $val );
                if( $field[$name] > EWZ_MAX_SIZE_MB ){
                     throw new EWZ_Exception( "Maximum upload file size must be no more than " . EWZ_MAX_SIZE_MB . "MB"  );
                }
            } elseif ( in_array( $name, array( 'canrotate', 'mustfill')  ) ) {
                if( !is_string( $val ) ){
                    throw new EWZ_Exception( "Bad input data format for $name ");
                }
                if ( $val != "1" ) {
                    throw new EWZ_Exception( "Invalid value for $name: '$val'" );
                }
                $field[$name] = true;

            } elseif ( $name == 'allowed_image_types' ) {
                if ( !is_array( $val ) ) {
                    throw new EWZ_Exception( "Invalid value '$val' for allowed image type"  );
                } elseif ( !count( $val ) ) {
                    throw new EWZ_Exception( 'Missing allowed image types' );
                } else {
                    foreach ( $val as $imgtype ) {
                        if ( !(in_array( $imgtype, $imgtypes )) ) {
                            throw new EWZ_Exception( "Invalid value '$imgtype' for image type"  );
                        }
                    }
                }
            } elseif( 'ss_col_w' == $name  ||  'ss_col_h' == $name || 'ss_col_o' == $name ){
                if ( isset( $val ) && !preg_match( '/^-1$|^\d*$/', $val ) ) {
                    throw new EWZ_Exception( "Invalid value for $name: '$val'" );
                }
                $field[$name] = ( int ) $val;
            }
            if ( $name == 'max_img_size' && $val > EWZ_MAX_SIZE_MB ) {
                throw new EWZ_Exception( "Max image size '$val' too large" );
            }
        }
        return true;
    }

    /**
     * Validate option-list field input
     *
     * @param  array<mixed> $fdata   input option field to check
     *
     * @return boolean  true on success
     */
    private static function valid_opt_input( &$fdata )
    {
        $req_field_data = array('label', 'value', 'maxnum');
        foreach ( $req_field_data as $req ) {
            foreach ( $fdata['options'] as $key => $val ) {
                if ( !array_key_exists( $req, $val ) ||  preg_match( '/^ *$/', $val[$req] )  ) {
                    throw new EWZ_Exception( "Missing required item $req for option list");
                }
            }
        }
        foreach ( $fdata as $name => $val ) {
            if ( !in_array( $name, array( 'options', 'selecttext') ) ) {
                throw new EWZ_Exception( "Bad input data $name" );
            }
            if( 'selecttext' == $name ){
                if ( !preg_match( '/^[0-9_\-a-zA-Z]*$/', $name ) ) {
                    throw new EWZ_Exception( "Invalid value for blank selection text: $name");
                }
            }
            if( 'options' == $name ){
               foreach ( $val as $key => $optarr ) {
                   // key is label, value or maxnum
                   foreach ( $optarr as $optkey => $optval ) {
                       if( !is_string( $optval ) ){
                           throw new EWZ_Exception( "Bad input data format for $optkey ");
                       }
                       switch ( $optkey ) {
                           case 'maxnum':
                               if ( !preg_match( '/^\d*$/', $optval ) ) {
                                   throw new EWZ_Exception( "Invalid value for field option $optkey : $optval");
                               }
                               $fdata[$name][$key][$optkey] = ( int )$optval;  // changing, cant use optval
                               break;
                           case 'value':
                               if ( preg_match( '/[^A-Za-z0-9_\.\-]/', $optval ) ) {
                                   throw new EWZ_Exception( "Invalid value for field option $optkey : $optval" );
                               }
                               break;
                           case 'label':
                               if ( preg_match( '/[^A-Za-z0-9_\.\- ]/', $optval ) ) {
                                   throw new EWZ_Exception( "Invalid value for field option $optkey : $optval" );
                               }
                               break;
                           default:
                                   throw new EWZ_Exception( "Invalid value for field option $optkey : $optval" );
                       }
                   }
               }
            }
        }
        return true;
    }
    /**
     * Validate label field input
     *
     * @param  array<mixed>  $field     input label field to check
     * @return boolean   true if no issues
     */
    private static function valid_lab_input( &$field )
    {
        $req_field_data = array('label');
        $all_field_data = array('label','showlabel');
        foreach ( $req_field_data as $req ) {
            if ( !array_key_exists( $req, $field )|| preg_match( '/^ *$/', $field[$req] ) ) {
                throw new EWZ_Exception( "Missing required item $req for a text input" );
            }
        }
        foreach ( $field as $name => $val ) {
            if( !is_string( $val ) ){
                 throw new EWZ_Exception( "Bad input data format for $name ");
            }
            if ( !in_array( $name, $all_field_data ) ) {
                throw new EWZ_Exception( "Bad input data for $name ");
            }

            if ( 'label' == $name ) {
                if( !self::to_string( $val, ['len' => 50] ) ){               // also html_entity_decodes the string
                    throw new EWZ_Exception( "Invalid format for a label field." );
                }
                $val = str_replace('\\', '', $val );  // messes up stripslashes and serialize. Its hard
                                                      // to see a legit use for this, so easier to just remove
                $field[$name] = $val;
            }
            elseif ( 'showlabel' == $name ) {
                if( !is_string( $val ) ){
                    throw new EWZ_Exception( "Bad input data format for $name ");
                }
                if ( $val != "1" ) {
                    throw new EWZ_Exception( "Invalid value for $name: '$val'" );
                }
                $field[$name] = true;
            }
        }
        return true;
    }

}
