/**
  * Parse and validate the coupon input file, building and returning an array of coupon data
  * to import into the database.
  *
  * The coupon data is broken into two portions:  the couple of defined fields
  * that make up the wp_posts table, and then the name-value meta data that is
  * inserted into the wp_postmeta table.  Within the meta data category, there
  * are known meta fields, such as 'discount_type' for instance, and then
  * arbitrary meta fields are allowed and identified by a CSV column title with
  * the prefix 'meta:'.
  *
  * @param array $parsed_data the raw data parsed from the CSV file
  * @param array $raw_headers the headers parsed from the CSV file
  * @param boolean $merging whether this is a straight import, or merge.  For
  *        the coupon import this will always be false.
  * @param int $record_offset number of records to skip before processing
  *
  * @return array associative array containing the key 'coupon' mapped to the parsed
  *         data, and key 'skipped' with a count of the skipped rows
  */
 private function parse_coupons($parsed_data, $raw_headers, $merging, $record_offset)
 {
     global $WC_CSV_Import, $wpdb;
     $results = array();
     // Count row
     $row = 0;
     // Track skipped records
     $skipped = 0;
     // first validate headers (non custom meta) and issue warnings for any unexpected ones
     foreach ($raw_headers as $key => $value) {
         if ('meta:' != substr($key, 0, 5)) {
             if (!in_array($key, $this->coupon_data_fields) && !in_array($key, $this->coupon_meta_fields)) {
                 $WC_CSV_Import->log->add(sprintf(__("> Warning: Unknown column named '%s' will be ignored.", WC_Customer_CSV_Import_Suite::TEXT_DOMAIN), esc_html($value)));
             }
         }
     }
     // Format coupon data
     foreach ($parsed_data as $item) {
         $row++;
         // skip record?
         if ($row <= $record_offset) {
             $WC_CSV_Import->log->add(sprintf(__('> Row %s - skipped due to record offset.', WC_Customer_CSV_Import_Suite::TEXT_DOMAIN), $row));
             continue;
         }
         $postmeta = $coupon = array();
         // give the line number and coupon code
         $WC_CSV_Import->log->add(sprintf(__('> Row %s%s - preparing for import.', WC_Customer_CSV_Import_Suite::TEXT_DOMAIN), $row, isset($item['coupon_code']) && $item['coupon_code'] ? ' - "' . $item['coupon_code'] . '"' : ''));
         // coupon code is required for merging or updating
         if (!isset($item['coupon_code']) || !$item['coupon_code']) {
             $WC_CSV_Import->log->add(__('> > Skipped. Missing coupon code.', WC_Customer_CSV_Import_Suite::TEXT_DOMAIN));
             $skipped++;
             continue;
         }
         // Check for existing coupons
         $coupon_found = $wpdb->get_var($wpdb->prepare("SELECT ID FROM {$wpdb->posts} WHERE post_type = 'shop_coupon' AND post_status = 'publish' AND post_title = %s", $item['coupon_code']));
         if ($merging) {
             // when merging we need that coupon to exist
             if (!$coupon_found) {
                 $WC_CSV_Import->log->add(sprintf(__("> > Skipped. Coupon code '%s' not found, unable to merge.", WC_Customer_CSV_Import_Suite::TEXT_DOMAIN), esc_html($item['coupon_code'])));
                 $skipped++;
                 continue;
             }
             // record the coupon id
             $coupon['id'] = $coupon_found;
         } else {
             // when inserting we need it to not exist
             if ($coupon_found) {
                 $WC_CSV_Import->log->add(sprintf(__("> > Skipped. Coupon code '%s' already exists.", WC_Customer_CSV_Import_Suite::TEXT_DOMAIN), esc_html($item['coupon_code'])));
                 $skipped++;
                 continue;
             }
         }
         // Get the set of possible coupon discount types
         $discount_types = SV_WC_Plugin_Compatibility::wc_get_coupon_types();
         // discount type is required
         if (!$merging && (!isset($item['discount_type']) || !$item['discount_type'])) {
             $WC_CSV_Import->log->add(__('> > Skipped. Missing discount type.', WC_Customer_CSV_Import_Suite::TEXT_DOMAIN));
             $skipped++;
             continue;
         }
         // Check for the discount type validity both by key and value (ie either 'fixed_cart' or 'Cart Discount'
         if (isset($item['discount_type']) && $item['discount_type']) {
             $discount_type_is_valid = false;
             foreach ($discount_types as $key => $value) {
                 if (0 === strcasecmp($key, $item['discount_type']) || 0 === strcasecmp($value, __($item['discount_type'], WC_Customer_CSV_Import_Suite::TEXT_DOMAIN))) {
                     $discount_type_is_valid = true;
                     $postmeta[] = array('key' => 'discount_type', 'value' => $key);
                     break;
                 }
             }
             if (!$discount_type_is_valid) {
                 $WC_CSV_Import->log->add(sprintf(__("> > Skipped. Unknown discount type '%s'.", WC_Customer_CSV_Import_Suite::TEXT_DOMAIN), esc_html($item['discount_type'])));
                 $skipped++;
                 continue;
             }
         }
         // build the coupon data object
         $coupon['post_title'] = apply_filters('woocommerce_coupon_code', $item['coupon_code']);
         $coupon['post_excerpt'] = isset($item['description']) ? $item['description'] : '';
         // Get any known coupon meta fields, and default any missing ones
         foreach ($this->coupon_meta_fields as $column) {
             switch ($column) {
                 case 'products':
                     // handle products: look up by sku
                 // handle products: look up by sku
                 case 'exclude_products':
                     $val = isset($item[$column]) ? $item[$column] : '';
                     $skus = array_filter(array_map('trim', explode(',', $val)));
                     $val = array();
                     foreach ($skus as $sku) {
                         // find by sku
                         $product_id = $wpdb->get_var($wpdb->prepare("SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key='_sku' AND meta_value='%s' LIMIT 1", $sku));
                         if (!$product_id) {
                             // unknown product
                             $WC_CSV_Import->log->add(sprintf(__('> > Skipped. Unknown product sku: %s.', WC_Customer_CSV_Import_Suite::TEXT_DOMAIN), esc_html($sku)));
                             $skipped++;
                             continue 4;
                             // break outer loop
                         }
                         $val[] = $product_id;
                     }
                     $postmeta[] = array('key' => 'products' == $column ? 'product_ids' : 'exclude_product_ids', 'value' => implode(',', $val));
                     break;
                 case 'product_categories':
                 case 'exclude_product_categories':
                     $val = isset($item[$column]) ? $item[$column] : '';
                     $product_cats = array_filter(array_map('trim', explode(',', $val)));
                     $val = array();
                     foreach ($product_cats as $product_cat) {
                         // validate product category
                         $term = term_exists($product_cat, 'product_cat');
                         if (!$term) {
                             // unknown category
                             $WC_CSV_Import->log->add(sprintf(__('> > Skipped. Unknown product category: %s.', WC_Customer_CSV_Import_Suite::TEXT_DOMAIN), esc_html($product_cat)));
                             $skipped++;
                             continue 4;
                             // continue main loop
                         }
                         $val[] = $term['term_id'];
                     }
                     $postmeta[] = array('key' => $column, 'value' => $val);
                     break;
                 case 'customer_emails':
                     $val = isset($item[$column]) ? $item[$column] : '';
                     $emails = array_filter(array_map('trim', explode(',', $val)));
                     $val = array();
                     foreach ($emails as $email) {
                         if (!is_email($email)) {
                             // invalid email
                             $WC_CSV_Import->log->add(sprintf(__('> > Skipped. Invalid email: %s.', WC_Customer_CSV_Import_Suite::TEXT_DOMAIN), esc_html($email)));
                             $skipped++;
                             continue 4;
                             // continue main loop
                         }
                         $val[] = $email;
                     }
                     $postmeta[] = array('key' => 'customer_email', 'value' => $val);
                     break;
                 case 'free_shipping':
                     // handle booleans, defaulting to 'no' on import  (not merge)
                 // handle booleans, defaulting to 'no' on import  (not merge)
                 case 'individual_use':
                 case 'apply_before_tax':
                 case 'exclude_sale_items':
                     $val = isset($item[$column]) && $item[$column] ? strtolower($item[$column]) : ($merging ? '' : 'no');
                     if ($val && 'yes' != $val && 'no' != $val) {
                         $WC_CSV_Import->log->add(sprintf(__("> > Skipped. Column '%s' must be 'yes' or 'no'.", WC_Customer_CSV_Import_Suite::TEXT_DOMAIN), esc_html($raw_headers[$column])));
                         $skipped++;
                         continue 3;
                         // continue main loop
                     }
                     $postmeta[] = array('key' => $column, 'value' => $val);
                     break;
                 case 'expiry_date':
                     $val = isset($item[$column]) ? $item[$column] : '';
                     if ($val && false === ($val = strtotime($val))) {
                         // invalid date format
                         $WC_CSV_Import->log->add(sprintf(__("> > Skipped. Invalid date format '%s'.", WC_Customer_CSV_Import_Suite::TEXT_DOMAIN), esc_html($item[$column])));
                         $skipped++;
                         continue 3;
                         // continue main loop
                     }
                     $postmeta[] = array('key' => $column, 'value' => $val ? date('Y-m-d', $val) : '');
                     break;
                 case 'usage_limit':
                     // handle integers
                 // handle integers
                 case 'usage_count':
                 case 'usage_limit_per_user':
                 case 'limit_usage_to_x_items':
                     $val = isset($item[$column]) ? $item[$column] : '';
                     if ('' !== $val && !is_numeric($val)) {
                         // invalid usage count value
                         $WC_CSV_Import->log->add(sprintf(__("> > Skipped. Invalid %s '%s'.", WC_Customer_CSV_Import_Suite::TEXT_DOMAIN), esc_html($raw_headers[$column]), esc_html($val)));
                         $skipped++;
                         continue 3;
                         // continue main loop
                     }
                     $postmeta[] = array('key' => $column, 'value' => $val);
                     break;
                 default:
                     $postmeta[] = array('key' => $column, 'value' => isset($item[$column]) ? $item[$column] : "");
             }
         }
         // Get any custom meta fields, as long as they have values
         foreach ($item as $key => $value) {
             if ('' !== $value) {
                 continue;
             }
             // Handle meta: columns - import as custom fields
             if ('meta:' == substr($key, 0, 5)) {
                 // Get raw meta key name
                 $meta_key = $raw_headers[$key];
                 $meta_key = trim(str_replace('meta:', '', $meta_key));
                 // Add to postmeta array
                 $postmeta[] = array('key' => esc_attr($meta_key), 'value' => $value);
             }
         }
         $coupon['postmeta'] = $postmeta;
         // the coupon array will now contain the necessary name-value pairs for the wp_posts table, and also any meta data in the 'postmeta' array
         $results[] = $coupon;
     }
     // Result
     return array($this->type => $results, 'skipped' => $skipped);
 }