<?php

/**
 * Model representing an event or an event instance.
 *
 * @author       Time.ly Network, Inc.
 * @since        2.0
 * @instantiator Ai1ec_Factory_Event.create_event_instance
 * @package      Ai1EC
 * @subpackage   Ai1EC.Model
 */
class Ai1ec_Event extends Ai1ec_Base {

    /**
     * @var Ai1ec_Event_Entity Data store object reference.
     */
    protected $_entity    = null;

    /**
     * @var array Map of fields that require special care during set/get
     *            operations. Values have following meanings:
     *            [0]  - both way care required;
     *            [1]  - only `set` operations require care;
     *            [-1] - only `get` (for storage) operations require care.
     */
    protected $_swizzable = array(
        'cost'             => 0,
        'start'            => -1,
        'end'              => -1,
        'timezone_name'    => -1,
        'recurrence_dates' => 1,
        'exception_dates'  => 1,
    );

    /**
     * @var array Runtime properties
     */
    protected $_runtime_props = array();

    /**
     * @var bool|null Boolean cache-definition indicating if event is multiday.
     */
    protected $_is_multiday = null;

    /**
     * Wrapper to get property value.
     *
     * @param string $property Name of property to get.
     * @param mixed  $default  Default value to return.
     *
     * @return mixed Actual property.
     */
    public function get( $property, $default = null ) {
        return $this->_entity->get( $property, $default );
    }

    /**
     * Get properties generated at runtime
     *
     * @param string $property
     *
     * @return string
     */
    public function get_runtime( $property, $default = '' ) {
        return isset( $this->_runtime_props[$property] ) ?
            $this->_runtime_props[$property] :
            $default;
    }

    /**
     * Set properties generated at runtime
     *
     * @param string $property
     * @param string $value
     */
    public function set_runtime( $property, $value ) {
        $this->_runtime_props[$property] = $value;
    }

    /**
     * Handle property initiation.
     *
     * Decides, how to extract value stored in permanent storage.
     *
     * @param string $property Name of property to handle
     * @param mixed  $value    Value, read from permanent storage
     *
     * @return bool Success
     */
    public function set( $property, $value ) {
        if (
            isset( $this->_swizzable[$property] ) &&
            $this->_swizzable[$property] >= 0
        ) {
            $method = '_handle_property_construct_' . $property;
            $value  = $this->{$method}( $value );
        }
        $this->_entity->set( $property, $value );
        return $this;
    }

    /**
     * Set the event is all day, during the specified number of days
     *
     * @param number $length
     */
    public function set_all_day( $length = 1 ) {
        // set allday as true
        $this->set( 'allday', true );
        $start = $this->get( 'start' );
        // reset time component
        $start->set_time( 0, 0, 0 );
        $end = $this->_registry->get( 'date.time', $start );
        // set the correct length
        $end->adjust_day( $length );
        $this->set( 'end', $end );
    }

    /**
     * Set the event as if it has no end time
     */
    public function set_no_end_time() {
        $this->set( 'instant_event', true );
        $start = $this->get( 'start' );
        $end   = $this->_registry->get( 'date.time', $start );
        $end->set_time(
            $start->format( 'H' ),
            $start->format( 'i' ) + 15,
            $start->format( 's' )
        );
        $this->set( 'end', $end );
    }

    /**
     * Set object fields from arbitrary array.
     *
     * @param array $data Supposedly map of fields to initiate.
     *
     * @return Ai1ec_Event Instance of self for chaining.
     */
    public function initialize_from_array( array $data ) {

        // =======================================================
        // = Assign each event field the value from the database =
        // =======================================================
        foreach ( $this->_entity->list_properties() as $property ) {
            if ( 'post' !== $property && isset( $data[$property] ) ) {
                $this->set( $property, $data[$property] );
                unset( $data[$property] );
            }
        }
        if ( isset( $data['post'] ) ) {
            $this->set( 'post', (object)$data['post'] );
        } else {
            // ========================================
            // = Remaining fields are the post fields =
            // ========================================
            $this->set( 'post', (object)$data );
        }
        return $this;
    }

    /**
     * Delete the events from all tables
     */
    public function delete() {
        // delete post (this will trigger deletion of cached events, and
        // remove the event from events table)
        wp_delete_post( $this->get( 'post_id' ), true );
    }

    /**
     * Initialize object from ID.
     *
     * Attempts to retrieve entity from database and if succeeds - uses
     * {@see self::initialize_from_array} to initiate actual values.
     *
     * @param int      $post_id  ID of post (event) to initiate.
     * @param int|bool $instance ID of event instance, false for base event.
     *
     * @return Ai1ec_Event Instance of self for chaining.
     *
     * @throws Ai1ec_Event_Not_Found_Exception If entity is not locatable.
     */
    public function initialize_from_id( $post_id, $instance = false ) {
        $post = get_post( $post_id );
        if ( ! $post || $post->post_status == 'auto-draft' ) {
            throw new Ai1ec_Event_Not_Found_Exception(
                'Post with ID \'' . $post_id .
                '\' could not be retrieved from the database.'
            );
        }
        $post_id = (int)$post_id;
        $dbi     = $this->_registry->get( 'dbi.dbi' );

        $left_join  = '';
        $select_sql = '
            e.post_id,
            e.timezone_name,
            e.recurrence_rules,
            e.exception_rules,
            e.allday,
            e.instant_event,
            e.recurrence_dates,
            e.exception_dates,
            e.venue,
            e.country,
            e.address,
            e.city,
            e.province,
            e.postal_code,
            e.show_map,
            e.contact_name,
            e.contact_phone,
            e.contact_email,
            e.contact_url,
            e.cost,
            e.ticket_url,
            e.ical_feed_url,
            e.ical_source_url,
            e.ical_organizer,
            e.ical_contact,
            e.ical_uid,
            e.longitude,
            e.latitude,
            e.show_coordinates,
            GROUP_CONCAT( ttc.term_id ) AS categories,
            GROUP_CONCAT( ttt.term_id ) AS tags
        ';

        if (
            false !== $instance &&
            is_numeric( $instance ) &&
            $instance > 0
        ) {
            $select_sql .= ', IF( aei.start IS NOT NULL, aei.start, e.start ) as start,' .
                           '  IF( aei.start IS NOT NULL, aei.end,   e.end )   as end ';

            $instance = (int)$instance;
            $this->set( 'instance_id', $instance );
            $left_join = 'LEFT JOIN ' . $dbi->get_table_name( 'ai1ec_event_instances' ) .
                ' aei ON aei.id = ' . $instance . ' AND e.post_id = aei.post_id ';
        } else {
            $select_sql .= ', e.start as start, e.end as end, e.allday ';
            if ( -1 === (int)$instance ) {
                $select_sql .= ', aei.id as instance_id ';
                $left_join   = 'LEFT JOIN ' .
                    $dbi->get_table_name( 'ai1ec_event_instances' ) .
                    ' aei ON e.post_id = aei.post_id ' .
                    'AND e.start = aei.start AND e.end = aei.end ';
            }
        }

        // =============================
        // = Fetch event from database =
        // =============================
        $query = 'SELECT ' . $select_sql . '
            FROM ' . $dbi->get_table_name( 'ai1ec_events' ) . ' e
                LEFT JOIN ' .
                    $dbi->get_table_name( 'term_relationships' ) . ' tr
                    ON ( e.post_id = tr.object_id )
                LEFT JOIN ' . $dbi->get_table_name( 'term_taxonomy' ) . ' ttc
                    ON (
                        tr.term_taxonomy_id = ttc.term_taxonomy_id AND
                        ttc.taxonomy = \'events_categories\'
                    )
                LEFT JOIN ' . $dbi->get_table_name( 'term_taxonomy' ) . ' ttt
                    ON (
                        tr.term_taxonomy_id = ttt.term_taxonomy_id AND
                        ttt.taxonomy = \'events_tags\'
                    )
                ' . $left_join . '
            WHERE e.post_id = ' . $post_id . '
            GROUP BY e.post_id';

        $event = $dbi->get_row( $query, ARRAY_A );
        if ( null === $event || null === $event['post_id'] ) {
            throw new Ai1ec_Event_Not_Found_Exception(
                'Event with ID \'' . $post_id .
                '\' could not be retrieved from the database.'
            );
        }

        $event['post'] = $post;
        return $this->initialize_from_array( $event );
    }

    public function getenddate() {
        $end = $this->get( 'end' );
        if ( $this->is_allday() ) {
            $end->set_time(
                $end->format( 'H' ),
                $end->format( 'i' ),
                $end->format( 's' ) - 1
            );
        }
        return $end;
    }
    /**
     * Returns enddate specific info.
     *
     * @return array Date info structure.
     */
    public function getenddate_info() {
        $end = $this->getenddate();
        return array(
            'month'   => $this->get( 'end' )->format_i18n( 'M' ),
            'day'     => $this->get( 'end' )->format_i18n( 'j' ),
            'weekday' => $this->get( 'end' )->format_i18n( 'D' ),
            'year'    => $this->get( 'end' )->format_i18n( 'Y' ),
        );
    }

    /**
     * Create new event object, using provided data for initialization.
     *
     * @param Ai1ec_Registry_Object $registry  Injected object registry.
     * @param int|array|null        $data      Look up post with id $data, or
     *                                         initialize fields with associative
     *                                         array $data containing both post
     *                                         and event fields.
     * @param int|bool              $instance  Optionally instance ID. When ID
     *                                         value is -1 then it is
     *                                         retrieved from db.
     *
     * @throws Ai1ec_Invalid_Argument_Exception When $data is not one
     *                                          of int|array|null.
     * @throws Ai1ec_Event_Not_Found_Exception  When $data relates to
     *                                          non-existent ID.
     *
     */
    function __construct(
        Ai1ec_Registry_Object $registry,
        $data     = null,
        $instance = false
    ) {
        parent::__construct( $registry );
        $this->_entity = $this->_registry->get( 'model.event.entity' );
        if ( null === $data ) {
            return; // empty object
        } else if ( is_numeric( $data ) ) {
            $this->initialize_from_id( $data, $instance );
        } else if ( is_array( $data ) ) {
            $this->initialize_from_array( $data );
        } else {
            throw new Ai1ec_Invalid_Argument_Exception(
                'Argument to constructor must be integer, array or null' .
                ', not ' . var_export( $data, true )
            );
        }

        if ( $this->is_allday() ) {
            try {
                $timezone = $this->_registry->get( 'date.timezone' )
                    ->get( $this->get( 'timezone_name' ) );
                $this->_entity->set_preferred_timezone( $timezone );
            } catch ( Exception $excpt ) {
                //  ignore
            }
        }
    }

    /**
     * Twig method for retrieving avatar.
     *
     * @param  bool   $wrap_permalink Whether to wrap avatar in <a> element or not
     *
     * @return string Avatar markup
     */
    public function getavatar( $wrap_permalink = true ) {
        return $this->_registry->
            get( 'view.event.avatar' )->get_event_avatar(
                $this,
                $this->_registry->get( 'view.calendar.fallbacks' )->get_all(),
                '',
                $wrap_permalink
            );
    }

    /**
     * Returns whether Event has geo information.
     *
     * @return bool True or false.
     */
    public function has_geoinformation() {
        $latitude  = floatval( $this->get( 'latitude') );
        $longitude = floatval( $this->get( 'longitude' ) );
        return (
            (
                $latitude >= 0.000000000000001 ||
                $latitude <= -0.000000000000001
            ) &&
            (
                $longitude >= 0.000000000000001 ||
                $longitude <= -0.000000000000001
            )
        );
    }

    protected function _handle_property_construct_recurrence_dates( $value ) {
        if ( $value ) {
            $this->_entity->set( 'recurrence_rules', 'RDATE=' . $value );
        }
        return $value;
    }

    protected function _handle_property_construct_exception_dates( $value ) {
        if ( $value ) {
            $this->_entity->set( 'exception_rules', 'EXDATE=' . $value );
        }
        return $value;
    }

    /**
     * Handle `cost` value reading from permanent storage.
     *
     * @param string $value Value stored in permanent storage
     *
     * @return bool Success: true, always
     */
    protected function _handle_property_construct_cost( $value ) {
        $test_value = false;
        if (
            isset( $value[1] ) && (
                ':' === $value[1] || ';' === $value[1]
            )
        ) {
            $test_value = unserialize( $value );
        }
        $cost = $is_free = NULL;
        if ( false === $test_value ) {
            $cost    = trim( $value );
            $is_free = false;
        } else {
            extract( $test_value, EXTR_IF_EXISTS );
        }
        $this->_entity->set( 'is_free', (bool)$is_free );
        return (string)$cost;
    }

    public function get_uid_pattern() {
        static $format = null;
        if ( null === $format ) {
            $site_url = parse_url( ai1ec_get_site_url() );
            $format   = 'ai1ec-%d@' . $site_url['host'];
            if ( isset( $site_url['path'] ) ) {
                $format .= $site_url['path'];
            }
        }
        return $format;
    }

    /**
     * Get UID to be used for current event.
     *
     * The generated format is cached in static variable within this function
     * to re-use when generating UIDs for different entries.
     *
     * @return string Generated UID.
     *
     * @staticvar string $format Cached format.
     */
    public function get_uid() {
        $ical_uid = $this->get( 'ical_uid' );
        if ( ! empty( $ical_uid ) ) {
            return $ical_uid;
        }
        return sprintf( $this->get_uid_pattern(), $this->get( 'post_id' ) );
    }

    /**
     * Check if event is free.
     *
     * @return bool Free status.
     */
    public function is_free() {
        return (bool)$this->get( 'is_free' );
    }

    /**
     * Check if event is taking all day.
     *
     * @return bool True for all-day long events.
     */
    public function is_allday() {
        return (bool)$this->get( 'allday' );
    }

    /**
     * Check if event has virtually no time.
     *
     * @return bool True for instant events.
     */
    public function is_instant() {
        return (bool)$this->get( 'instant_event' );
    }

    /**
     * Check if event is taking multiple days.
     *
     * Uses object-wide variable {@see self::$_is_multiday} to store
     * calculated value after first call.
     *
     * @return bool True for multiday events.
     */
    public function is_multiday() {
        if ( null === $this->_is_multiday ) {
            $start = $this->get( 'start' );
            $end   = $this->get( 'end' );
            $diff  = $end->diff_sec( $start );
            $this->_is_multiday = $diff > 86400 &&
                $start->format( 'Y-m-d' ) !== $end->format( 'Y-m-d' );
        }
        return $this->_is_multiday;
    }

    /**
     * Get the duration of the event
     *
     * @return number
     */
    public function get_duration() {
        $duration = $this->get_runtime( 'duration', null );
        if ( null === $duration ) {
            $duration = $this->get( 'end' )->format() -
                $this->get( 'start' )->format();
            $this->set_runtime( 'duration', $duration );
        }
        return $duration;
    }

    /**
     * Create/update entity representation.
     *
     * Saves the current event data to the database. If $this->post_id exists,
     * but $update is false, creates a new record in the ai1ec_events table of
     * this event data, but does not try to create a new post. Else if $update
     * is true, updates existing event record. If $this->post_id is empty,
     * creates a new post AND record in the ai1ec_events table for this event.
     *
     * @param  bool  $update  Whether to update an existing event or create a
     *                        new one
     * @param  bool  $backward_compatibility The (wpdb) ofr the new wordpress 4.4
     * now inserts NULL as null values. The previous version, if you insert a NULL
     * value in an int value, the values saved would be 0 instead of null.
     * @return int            The post_id of the new or existing event.
     */
    function save( $update = false, $backward_compatibility = true ) {
        do_action( 'ai1ec_pre_save_event', $this, $update );
        if ( ! $update ) {
            $response = apply_filters( 'ai1ec_event_save_new', $this );
            if ( is_wp_error( $response ) ) {
                throw new Ai1ec_Event_Create_Exception(
                    'Failed to create event: ' . $response->get_error_message()
                );
            }
        }

        $dbi        = $this->_registry->get( 'dbi.dbi' );
        $columns    = $this->prepare_store_entity();
        $format     = $this->prepare_store_format( $columns, $backward_compatibility );
        $table_name = $dbi->get_table_name( 'ai1ec_events' );
        $post_id    = $columns['post_id'];

        if ( $this->get( 'end' )->is_empty() ) {
            $this->set_no_end_time();
        }
        if ( $post_id ) {
            $success = false;
            if ( ! $update ) {
                $success = $dbi->insert(
                    $table_name,
                    $columns,
                    $format
                );
            } else {
                $success = $dbi->update(
                    $table_name,
                    $columns,
                    array( 'post_id' => $columns['post_id'] ),
                    $format,
                    array( '%d' )
                );
            }
            if ( false === $success ) {
                return false;
            }

        } else {
            // ===================
            // = Insert new post =
            // ===================
            $post_id = wp_insert_post( $this->get( 'post' ), false );
            if ( 0 === $post_id ) {
                return false;
            }
            $this->set( 'post_id', $post_id );
            $columns['post_id'] = $post_id;

            // =========================
            // = Insert new event data =
            // =========================
            if ( false === $dbi->insert( $table_name, $columns, $format ) ) {
                return false;
            }
        }

        $taxonomy = $this->_registry->get(
            'model.event.taxonomy',
            $post_id
        );
        $cats = $this->get( 'categories' );
        if (
            is_array( $cats ) &&
            ! empty( $cats )
        ) {
            $taxonomy->set_categories( $cats );
        }
        $tags = $this->get( 'tags' );
        if (
            is_array( $tags ) &&
            ! empty( $tags )
        ) {
            $taxonomy->set_tags( $tags );
        }

        if (
            $feed = $this->get( 'feed' ) &&
            isset( $feed->feed_id )
        ) {
            $taxonomy->set_feed( $feed );
        }

        // give other plugins / extensions the ability to do things
        // when saving, like fetching authors which i removed as it's not core.
        do_action( 'ai1ec_save_event' );

        $instance_model = $this->_registry->get( 'model.event.instance' );
        $instance_model->recreate( $this );

        do_action( 'ai1ec_event_saved', $post_id, $this, $update );
        return $post_id;
    }

    /**
     * Prepare fields format flags to use in database operations.
     *
     * @param array $columns Array of columns with data to insert.
     *
     * @return array List of format flags to use in integrations with DBI.
     */
    public function prepare_store_format( array &$columns, $backward_compatibility = true ) {
        $format = array(
            '%d',  // post_id
            '%d',  // start
            '%d',  // end
            '%s',  // timezone_name
            '%d',  // allday
            '%d',  // instant_event
            '%s',  // recurrence_rules
            '%s',  // exception_rules
            '%s',  // recurrence_dates
            '%s',  // exception_dates
            '%s',  // venue
            '%s',  // country
            '%s',  // address
            '%s',  // city
            '%s',  // province
            '%s',  // postal_code
            '%d',  // show_map
            '%s',  // contact_name
            '%s',  // contact_phone
            '%s',  // contact_email
            '%s',  // contact_url
            '%s',  // cost
            '%s',  // ticket_url
            '%s',  // ical_feed_url
            '%s',  // ical_source_url
            '%s',  // ical_uid
            '%d',  // show_coordinates
            '%f',  // latitude
            '%f',  // longitude
        );

        if ( $backward_compatibility ) {
            $columns_count = count( $columns );
            if ( count( $format ) !== $columns_count ) {
                throw new Ai1ec_Event_Not_Found_Exception(
                    'Data columns count differs from format columns count'
                );
            }
            $index = 0;
            foreach ( $columns as $key => $value ) {
                if ( '%d' === $format[ $index ] ) {
                    if ( is_null( $value ) ) {
                        $columns[ $key ] = 0;
                    }
                }
                $index++;
            }
        }

        return $format;
    }

    /**
     * Prepare event entity {@see self::$_entity} for persistent storage.
     *
     * Creates an array of database fields and corresponding values.
     *
     * @return array Map of fields to store.
     */
    public function prepare_store_entity() {
        $entity = array(
            'post_id'          => $this->storage_format( 'post_id' ),
            'start'            => $this->storage_format( 'start' ),
            'end'              => $this->storage_format( 'end' ),
            'timezone_name'    => $this->storage_format( 'timezone_name' ),
            'allday'           => $this->storage_format( 'allday' ),
            'instant_event'    => $this->storage_format( 'instant_event' ),
            'recurrence_rules' => $this->storage_format( 'recurrence_rules' ),
            'exception_rules'  => $this->storage_format( 'exception_rules' ),
            'recurrence_dates' => $this->storage_format( 'recurrence_dates' ),
            'exception_dates'  => $this->storage_format( 'exception_dates' ),
            'venue'            => $this->storage_format( 'venue' ),
            'country'          => $this->storage_format( 'country' ),
            'address'          => $this->storage_format( 'address' ),
            'city'             => $this->storage_format( 'city' ),
            'province'         => $this->storage_format( 'province' ),
            'postal_code'      => $this->storage_format( 'postal_code' ),
            'show_map'         => $this->storage_format( 'show_map' ),
            'contact_name'     => $this->storage_format( 'contact_name' ),
            'contact_phone'    => $this->storage_format( 'contact_phone' ),
            'contact_email'    => $this->storage_format( 'contact_email' ),
            'contact_url'      => $this->storage_format( 'contact_url' ),
            'cost'             => $this->storage_format( 'cost' ),
            'ticket_url'       => $this->storage_format( 'ticket_url' ),
            'ical_feed_url'    => $this->storage_format( 'ical_feed_url' ),
            'ical_source_url'  => $this->storage_format( 'ical_source_url' ),
            'ical_uid'         => $this->storage_format( 'ical_uid' ),
            'show_coordinates' => $this->storage_format( 'show_coordinates' ),
            'latitude'         => $this->storage_format( 'latitude',  '' ),
            'longitude'        => $this->storage_format( 'longitude', '' ),
        );
        return $entity;
    }

    /**
     * Compact field for writing to persistent storage.
     *
     * @param string $field   Name of field to compact.
     * @param mixed  $default Default value to use for undescribed fields.
     *
     * @return mixed Value or $default.
     */
    public function storage_format( $field, $default = null ) {
        $value = $this->_entity->get( $field, $default );
        if (
            isset( $this->_swizzable[$field] ) &&
            $this->_swizzable[$field] <= 0
        ) {
            $value = $this->{ '_handle_property_destruct_' . $field }( $value );
        }
        return $value;
    }

    /**
     * Allow properties to be modified after cloning.
     *
     * @return void
     */
    public function __clone() {
        $this->_entity = clone $this->_entity;
    }

    /**
     * Decode timezone to use for event.
     *
     * Following algorythm is used to detect a value:
     *     - take value provided in input;
     *     - if empty - take value associated with start time;
     *     - if empty - take current environment timezone.
     *
     * @param string $timezone_name Timezone provided in input.
     *
     * @return string Timezone name to use for event in future.
     */
    protected function _handle_property_destruct_timezone_name(
        $timezone_name
    ) {
        if ( empty( $timezone_name ) ) {
            $timezone_name = $this->get( 'start' )->get_timezone();
            if ( empty( $timezone_name ) ) {
                $timezone_name = $this->_registry->get( 'date.timezone' )
                    ->get_default_timezone();
            }
        }
        return $timezone_name;
    }

    /**
     * Format datetime to UNIX timestamp for storage.
     *
     * @param Ai1ec_Date_Time $start Datetime object to compact.
     *
     * @return int UNIX timestamp.
     */
    protected function _handle_property_destruct_start( Ai1ec_Date_Time $start ) {
        return $start->format_to_gmt();
    }

    /**
     * Format datetime to UNIX timestamp for storage.
     *
     * @param Ai1ec_Date_Time $end Datetime object to compact.
     *
     * @return int UNIX timestamp.
     */
    protected function _handle_property_destruct_end( Ai1ec_Date_Time $end ) {
        return $end->format_to_gmt();
    }

    /**
     * Handle `cost` writing to permanent storage.
     *
     * @param string $cost Value of cost.
     *
     * @return string Serialized value to store.
     */
    protected function _handle_property_destruct_cost( $cost ) {
        $cost = array(
            'cost'    => $cost,
            'is_free' => false,
        );
        if ( $this->get( 'is_free' ) ) {
            $cost['is_free'] = true;
        }
        return serialize( $cost );
    }

    /**
     * Get the submitter information array
     * @return array (
     *    is_organizer => 1 if the organizer is the submitter,
     *    email => if is_organizer is 0, them this property has the email of the submitter,
     *    name => if is_organizer is 0, them this property has the name of the submitter
     * )
     */
    public function get_submitter_info() {
        $post_id        = $this->get( 'post_id' );
        if ( empty( $post_id ) ) {
            return null;
        }
        $submitter_info = get_post_meta(
            $post_id,
            '_submitter_info',
            true
        );
        if ( false == ai1ec_is_blank( $submitter_info ) ) {
            $submitter_info = json_decode( $submitter_info, true );
            if ( is_array( $submitter_info ) ) {
                return $submitter_info;
            }
        }
        return null;
    }

    /**
     * Save the submitter information into post metadata
     */
    public function save_submitter_info( $is_submitter, $submitter_email, $submitter_name ) {
        $post_id        = $this->get( 'post_id' );
        if ( empty( $post_id ) ) {
            throw new Exception( 'Post id empty' );
        }
        $save = false;
        if ( 1 === intval( $is_submitter ) ) {
            $submitter_info['is_organizer'] = 1;
            if ( false === ai1ec_is_blank( $this->get( 'contact_email' ) ) ) {
                $save = true;
            }
        } else {
            $submitter_info['is_organizer'] = 0;
            if ( false === ai1ec_is_blank( $submitter_email ) ) {
                $submitter_info['email'] = trim( $submitter_email );
                $submitter_info['name']  = trim( $submitter_name );
                $save                    = true;
            }
        }
        if ( $save ) {
            update_post_meta( $post_id, '_submitter_info', json_encode( $submitter_info ) );
        }
    }

}
