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

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

class Ewz_Followup_Input extends Ewz_Input
{
   /**
    * @var array<mixed>   $fdata;
    */
    protected $fdata;
   /**
    * @var boolean $required;
    */
    protected $required;
   /**
    * @var String   $field_type;
    */
    protected $field_type;
   /**
    * @var String   $field_header;
    */
    protected $field_header;
  /**
    * @var array<String>   $Xmaxnums;
    */
    protected $Xmaxnums;
  /**
    * @var int   $chkmax;
    */
    protected $chkmax;

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

        $this->fdata = $followup_data['fdata'];
        $this->required = $followup_data['required'];
        $this->field_type = $followup_data['field_type'];
        $this->field_header = $followup_data['field_header'];
        $this->Xmaxnums = $followup_data['Xmaxnums'];
        $this->chkmax = isset( $followup_data['fdata']['chkmax'] ) ?  $followup_data['fdata']['chkmax'] : 0;

        /*** 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(
                             'ewzuploadnonce'   => array( 'type' => 'unonce',    'req' =>  true,  'val' => '' ),
                             '_wp_http_referer' => array( 'type' => 'to_string', 'req' =>  false, 'val' => '' ),
                             'rdata'            => array( 'type' => 'v_rdata',   'req' => true,  'val' => '' ),
                             );
        $this->validate();
    }

    /**
     * Validate "rdata" input
     *
     * @param   mixed $value  input value, may be changed by function
     * @param   string $arg    extra input value
     * @return  true  ( exception raised if not valid )
     */
    protected function v_rdata( &$value, $arg = '' ){
        $optcounts = array();
        $chkcount = array();
        if( !is_array( $value ) ){
             throw new EWZ_Exception( 'Invalid value for rdata' );
        }
        foreach ( $value as $rownum => &$input_row ) {
            foreach ( $input_row as $item_id => &$val ) {
                if ( ( !isset($val) || $val === '' ) && $this->required ) {
                    $rownum1 = $rownum + 1;
                    throw new EWZ_Exception( sprintf(__("%s is required ( row %d )", 'entrywizard'), $this->field_header, $rownum1 ) );
                }
                switch ( $this->field_type ) {
                case 'str':
                        self::validate_str_data( $this->fdata, $val );  // may change 2nd arg
                        break;
                case 'opt':
                        if( !isset( $optcounts[$val] ) ){
                            $optcounts[$val] = 1;
                        } else {
                            ++$optcounts[$val];
                        }
                        $this->validate_opt_data( $this->Xmaxnums, $val, $optcounts[$val] );
                        break;
                case 'rad':
                    if( !isset( $chkcount[$val] ) ){
                        $chkcount[$val] = 0;
                    }
                    if( $val ){
                        ++$chkcount[$val];
                    }
                    self::validate_rad_data( $value[$rownum][$item_id], $chkcount[$val] );  // may change 1st arg
                    break;
                case 'chk':
                    if( !isset( $chkcount[$val] ) ){
                        $chkcount[$val] = 0;
                    }
                    if( $val == 'on' ){
                        ++$chkcount[$val];
                    }
                    self::validate_chk_data(  $this->chkmax, $value[$rownum][$item_id], $chkcount[$val] );  // may change it's input
                    break;
                default:
                    throw new EWZ_Exception( sprintf( __("Invalid field type '%s'",'entrywizard') , $this->field_type ));
                }
           }
            unset( $val );
        }
        unset( $input_row );
        return true;
    }


    /**
     * Validate  string input
     *
     * Check length constraints in $fdata. If not satisfied, return a message.
     *
     * @param   array<mixed>   $fdata   input field data
     * @param   mixed   $val     extra input value
     * @return  boolean  true on success
     */
    private static function validate_str_data( $fdata, &$val )
    {
        if( !self::to_string( $val, array('len' => 9000 ) ) ){               // also encodes the string
            throw new EWZ_Exception( __("Invalid format for text input",'entrywizard') );
        }
        $val = str_replace('\\', '', $val );  // messes up stripslashes and serialize. Its hard
                                              // to see a legit use for this, so easier to just remove

        if ( isset( $fdata['maxstringchars'] )
             && $fdata['maxstringchars']
             && ( strlen( str_replace("\r", '', $val ) ) > $fdata['maxstringchars'] )  // newlines returned as \n\r, counted as 1 char by html
             ) {
            $val = substr( $val, 0, $fdata['maxstringchars'] );
            throw new EWZ_Exception( sprintf(__("Text starting '%s' is too long. Limit is %d (including newlines)",'entrywizard'),
                                             $val,  $fdata['maxstringchars']  ) );
        }
        return true;
    }

   /**
     * Validate option input
     *
     * Check $val is one of the options in $fdata, and there are not too many items with that value
     * already selected.
     *
     * @param   array<string>   $Xmaxnums
     * @param   mixed   $val  input from a select list
     * @param   int     $optcount
     * @return  boolean
     */
    private function validate_opt_data( $Xmaxnums, $val, $optcount )
    {
        // if the item was required, it would be caught earlier, so allow blank here
        if( !$val ){
            return true;
        }
        if( !is_string( $val ) ){
            throw new EWZ_Exception( __("Invalid format for option input" , 'entrywizard') );
        }

        if ( array_key_exists( $val, $Xmaxnums ) && $Xmaxnums[$val] ) {
            if ( intval($Xmaxnums[$val]) < $optcount ) {
                throw new EWZ_Exception( sprintf(__("Too many '%s' values for %s", 'entrywizard'), $val , $this->field_header  ) );
            }
        }
        foreach ( $this->fdata['options'] as $option ) {
            if ( $val == $option['value'] ) {
                return true;
            }
        }
        throw new EWZ_Exception( sprintf(__("Invalid value '%s' for field '%s'", 'entrywizard'), $val, $this->field_header ) );
    }

    /**
     * Validate radio button data
     *
     * @param   mixed   $val   input data, string convertible to boolean, gets changed to  true or false
     * @param  integer  $count current count of items checked via this button
     * @return boolean  true,  exception if data not valid
     */
    private static function validate_rad_data(  &$val, $count ){
        if( !self::to_bool( $val, '' ) ){               // changes $val to int 0 or 1
            throw new EWZ_Exception( (__("Invalid value for radiobutton input", 'entrywizard') ) );
        }
        if ( 1 < $count ) {
            throw new EWZ_Exception( __("More than one radiobutton checked: ", 'entrywizard') );
        }
        return true;
    }

    /**
     * Validate checkbox data
     *
     * @param    int    $chkmax max number of checked items allowed ( ignored if 0 )
     * @param    mixed  $val    input data, string convertible to boolean, gets changed to true or false
     * @param    int    $count  current count of items with this value for this checkbox
     * @return   boolean true,  exception if data not valid
     */
    private static function validate_chk_data( $chkmax, &$val, $count ){
        if( !self::to_bool( $val, '' ) ){               // changes $val to true or false
            throw new EWZ_Exception( __("Invalid value for checkbox input",'entrywizard' ) );
        }
        if ( $chkmax ) {
            if ( intval( $chkmax ) < $count ) {
                throw new EWZ_Exception(  __("Too many items checked",'entrywizard' )  );
            }
        }
        return true;
    }
}
