<?php
define('UC_COUPON_PURCHASE_RECIPIENT_ATTRIBUTE', 'uc_coupon_purchase_recipient');

/**
 * Implements hook_permission().
 */
function uc_coupon_purchase_permission() {
  return array(
    'view purchased coupons' => array(
      'title' => t('view purchased coupons'),
      'description' => t('View list of the coupons a user has purchased.'),
    ),
  );
}

/**
 * Implements hook_menu().
 */
function uc_coupon_purchase_menu() {
  $items = array();

  $items['user/%user/coupons'] = array(
    'title' => 'Coupons & Credits',
    'description' => 'View your discount coupons and credits.',
    'page callback' => 'uc_coupon_purchase_view',
    'access callback' => 'uc_coupon_purchase_can_view',
    'page arguments' => array(1),
    'access arguments' => array(1),
    'type' => MENU_LOCAL_TASK,
    'file' => 'uc_coupon_purchase.pages.inc',
    'weight' => 5,
  );

  $items['user/%user/coupons/%uc_coupon'] = array(
    'title' => 'Print preview',
    'page callback' => 'uc_coupon_purchase_print',
    'access callback' => 'uc_coupon_purchase_can_view',
    'page arguments' => array(1, 3),
    'access arguments' => array(1, 3),
    'type' => MENU_LOCAL_TASK,
    'file' => 'uc_coupon_purchase.pages.inc',
    'weight' => 5,
  );

  $items['user/%user/coupons/%uc_coupon/codes'] = array(
    'title' => 'Download codes',
    'page callback' => 'uc_coupon_codes_csv',
    'access callback' => 'uc_coupon_purchase_can_view',
    'page arguments' => array(3),
    'access arguments' => array(1, 3),
    'type' => MENU_CALLBACK,
    'file' => 'uc_coupon.admin.inc',
    'file path' => drupal_get_path('module', 'uc_coupon'),
    'weight' => 5,
  );

  return $items;
}

/**
 * Access callback for user/%/coupons.
 *
 * User must have "view purchased coupons" permission, current user must have a
 * coupon to view, other users must have "administer users" permission.
 *
 * If a coupon is specified, user must have purchased that coupon.
 */
function uc_coupon_purchase_can_view($account, $coupon = NULL) {
  global $user;

  if (user_access('view purchased coupons')) {
    if (user_access('administer users') || ($account->uid == $user->uid && uc_coupon_purchase_has_coupon($account, $coupon))) {
      return TRUE;
    }
  }

  return FALSE;
}

/**
 * Implementation of hook_uc_coupon_actions for purchased coupons
 */
function uc_coupon_purchase_uc_coupon_actions($coupon) {
  $actions = array();
  if (!user_access('manage store coupons')) {
    global $user;
    $account =  (arg(0) == 'user') ? user_load(arg(1)) : $user;

    if (uc_coupon_purchase_can_view($account, $coupon)) {
      $actions[] = array(
        'url' => "user/$account->uid/coupons/$coupon->cid/view",
        'icon' => drupal_get_path('module', 'uc_store') . '/images/order_view.gif',
        'title' => t('View coupon: @name', array('@name' => $coupon->name)),
      );

      $actions[] = array(
        'url' => "user/$account->uid/coupons/$coupon->cid/print",
        'icon' => drupal_get_path('module', 'uc_store') . '/images/print.gif',
        'title' => t('Print coupon: @name', array('@name' => $coupon->name)),
      );

      if ($coupon->bulk) {
        $actions[] = array(
          'url' => 'admin/store/coupons/' . $coupon->cid . '/codes',
          'icon' => drupal_get_path('module', 'uc_store') . '/images/menu_reports_small.gif',
          'title' => t('Download codes as CSV: @name', array('@name' => $coupon->name)),
        );
      }
    }
  }
  return $actions;
}

/**
 * Check whether a given user has purchased any coupon, or the specific supplied coupon.
 */
function uc_coupon_purchase_has_coupon($account, $coupon = NULL) {
  $query = db_select('uc_coupon_purchase_users', 'u')->condition('u.uid', $account->uid);
  $query->addExpression('COUNT(*)');
  // Make sure coupon is valid before looking for it.
  if (!empty($coupon) && !empty($coupon->cid)) {
    $query->condition('u.cid', $coupon->cid, 'AND');
  }
  return $query->execute()->fetchField();
}

/**
 * Implements hook_uc_product_feature().
 */
function uc_coupon_purchase_uc_product_feature() {
  $features[] = array(
    'id' => 'coupon',
    'title' => t('Coupon creation'),
    'callback' => 'uc_coupon_purchase_feature_form',
    'delete' => 'uc_coupon_purchase_feature_delete',
    'settings' => 'uc_coupon_purchase_feature_settings',
  );

  return $features;
}

/**
 * Form builder for hook_uc_product_feature().
 */
function uc_coupon_purchase_feature_form($form, $form_state, $node, $feature) {
  $models = array(
    NULL => t('Any'),
    $node->model => $node->model,
  );
  if (module_exists('uc_attribute')) {
    $adjustments = db_query("SELECT model FROM {uc_product_adjustments} WHERE nid = :nid", array(':nid' => $node->nid));
    foreach ($adjustments as $adjustment) {
      if (!in_array($adjustment->model, $models)) {
        $models[$adjustment->model] = $adjustment->model;
      }
    }
  }

  if (!empty($feature)) {
    $data = db_query("SELECT * FROM {uc_coupon_purchase} WHERE pfid = :pfid", array(':pfid' => $feature['pfid']))->fetchObject();
    $form['pfid'] = array(
      '#type' => 'value',
      '#value' => $feature['pfid'],
    );
    if (!$data) {
      $data = new stdClass();
    }
  }
  else {
    $data = new stdClass();
  }

  $coupons = array();
  $result = db_query("SELECT cid, name, code FROM {uc_coupons} WHERE status = :status", array(':status' => 0));
  foreach ($result as $coupon) {
    $coupons[$coupon->cid] = $coupon->name . ' (' . $coupon->code . ')';
  }

  if (!$coupons) {
    drupal_set_message(t('No inactive coupons found. Please <a href="!url">create an inactive coupon</a> to use as a base coupon for purchases.', array('!url' => url('admin/store/coupons/add'))), 'error');
    drupal_goto('node/' . arg(1) . '/edit/features');
  }

  $form['title'] = array(
    '#type' => 'markup',
    '#markup' => '<h2>' . t('Coupon creation') . '</h2>',
  );
  $form['nid'] = array(
    '#type' => 'value',
    '#value' => $node->nid,
  );
  $form['model'] = array(
    '#type' => 'select',
    '#title' => t('Model/SKU'),
    '#default_value' => isset($data->model) ? $data->model : NULL,
    '#description' => t('Select the model/SKU of the product that cause a coupon to be created.'),
    '#options' => $models,
  );
  $form['base_cid'] = array(
    '#type' => 'select',
    '#title' => t('Base coupon'),
    '#default_value' => isset($data->base_cid) ? $data->base_cid : reset(array_keys($coupons)),
    '#description' => t('Select the coupon that will be cloned when a user purchases this product. Only inactive coupons are listed; the purchased coupon will automatically be activated.'),
    '#options' => $coupons,
  );

  return uc_product_feature_form($form, $form_state, $node, $feature);
}

/**
 * Submit handler for uc_coupon_purchase_feature form.
 */
function uc_coupon_purchase_feature_form_submit($form, &$form_state) {
  $coupon = uc_coupon_load($form_state['values']['base_cid']);

  $description = (empty($form_state['values']['model'])) ? t('<strong>SKU:</strong> Any<br/>') : t('<strong>SKU:</strong> !sku<br/>', array('!sku' => $form_state['values']['model']));
  $description .=  t('<strong>Base coupon:</strong> !coupon<br/>', array('!coupon' => $coupon->name . ' (' . $coupon->code . ')'));

  $data = array(
    'pfid' => !empty($form_state['values']['pfid']) ? $form_state['values']['pfid'] : 0,
    'nid' => $form_state['values']['nid'],
    'fid' => 'coupon',
    'description' => $description,
  );

  $form_state['redirect'] = uc_product_feature_save($data);

  if (!empty($form_state['values']['pfid'])) {
    db_delete('uc_coupon_purchase')
      ->condition('pfid', $form_state['values']['pfid'])
      ->execute();
  }

  $id = db_insert('uc_coupon_purchase')
    ->fields(array(
      'pfid' => $data['pfid'],
      'nid' => $form_state['values']['nid'],
      'model' => $form_state['values']['model'],
      'base_cid' => $form_state['values']['base_cid'],
    ))
    ->execute();
}

/**
 * Delete callback for product feature.
 */
function uc_coupon_purchase_feature_delete($pfid) {
  db_delete('uc_coupon_purchase')
    ->condition('pfid', $pfid)
    ->execute();
}

/**
 * Form builder for feature settings.
 */
function uc_coupon_purchase_feature_settings() {
  foreach (uc_order_status_list('general') as $status) {
    $statuses[$status['id']] = $status['title'];
  }

  $form['uc_coupon_purchase_order_status'] = array(
    '#type' => 'select',
    '#title' => t('Order status'),
    '#default_value' => variable_get('uc_coupon_purchase_order_status', 'completed'),
    '#description' => t('Select the order status that will cause the coupon to be created. Be aware that if payments are processed automatically, this happens before anonymous customers have an account created. This order status should not be reached before the user account exists.'),
    '#options' => $statuses,
  );

  return $form;
}

/**
 * Implements hook_form_FORM_ID_alter() for uc_coupon_add_form().
 *
 * Modify the coupon add form to include coupon purchase options.
 */
function uc_coupon_purchase_form_uc_coupon_add_form_alter(&$form) {
  $value = isset($form['#uc_coupon']) ? $form['#uc_coupon'] : new stdClass();

  $form['uc_coupon_purchase'] = array(
    '#type' => 'fieldset',
    '#title' => t('Coupon purchase options'),
    '#description' => t('These settings alter the generation of purchased coupons based on this coupon.'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
    '#weight' => -1,
  );
  $form['uc_coupon_purchase']['purchase_suffix_length'] = array(
    '#type' => 'select',
    '#title' => t('Purchased coupon code suffix length'),
    '#default_value' => isset($value->data['purchase_suffix_length']) ? $value->data['purchase_suffix_length'] : 12,
    '#description' => t('A random string of at least this many characters will be appended to the base coupon code to ensure uniqueness.'),
    '#options' => drupal_map_assoc(range(4, 30)),
  );
  $form['uc_coupon_purchase']['purchase_relative'] = array(
    '#type' => 'checkbox',
    '#title' => t('Alter coupon date restrictions relative to the purchase date.'),
    '#description' => t('Checking this box will make purchased coupons valid for the same time span as the base coupon, otherwise coupons will always be valid between the same dates.'),
    '#default_value' => isset($value->data['purchase_relative']) ? $value->data['purchase_relative'] : FALSE,
  );

  $form['status']['#description'] .= '<br />' . t('Only inactive coupons can be selected as the base for a purchased coupon, subsequent purchased coupons will automatically be activated.');

  $form['#validate'][] = 'uc_coupon_purchase_uc_coupon_add_form_validate';
  $form['#entity_builders'][] = 'uc_coupon_purchase_build_coupon';
}

/**
 * Extra validation for uc_coupon_add_form.
 */
function uc_coupon_purchase_uc_coupon_add_form_validate($form, &$form_state) {
  if ($form_state['values']['purchase_relative'] && !$form_state['values']['use_validity']) {
    form_set_error('purchase_relative', t('You must specify date restrictions to use the relative date feature with purchased coupons.'));
  }
}

/**
 * Entity builder callback for the uc_coupon edit form.
 */
function uc_coupon_purchase_build_coupon($type, $coupon, $form, $form_state) {
  if ($type == 'uc_coupon') {
    $edit = $form_state['values'];
    if (isset($edit['purchase_suffix_length'])) {
      $coupon->data['purchase_suffix_length'] = $edit['purchase_suffix_length'];
    }
    if (isset($edit['purchase_relative']) && $edit['purchase_relative']) {
      $coupon->data['purchase_relative'] = TRUE;
    }
  }
}

/**
 * Implements hook_uc_coupon_delete().
 */
function uc_coupon_purchase_uc_coupon_delete($coupon) {
  db_delete('uc_coupon_purchase_users')
    ->condition('cid', $coupon->cid)
    ->execute();
}

/**
 * Implements hook_uc_order().
 */
function uc_coupon_purchase_uc_order($op, $order, $status) {
  switch ($op) {
    case 'update':
      if ($status == variable_get('uc_coupon_purchase_order_status', 'completed') &&
          $order->order_status != $status &&
          $order->uid > 0 &&
          ($order_user = user_load($order->uid)) !== FALSE) {
        foreach ($order->products as $product) {
          $result = db_query("SELECT * FROM {uc_coupon_purchase} WHERE nid = :nid", array(':nid' => $product->nid));
          foreach ($result as $row) {
            if ($row->model == $product->model || empty($row->model)) {
              $coupon = uc_coupon_load($row->base_cid);

              // Append purchaser data to the coupon name.
              $purchaser = trim(!empty($order->billing_company) ? $order->billing_company : ($order->billing_first_name . ' ' . $order->billing_last_name));
              if (empty($purchaser)) {
                $purchaser = "Anonymous";
              }
              $coupon->name .= ' ' . t('purchased by !name', array('!name' => $purchaser));
              $coupon->data['order_id'] = $order->order_id;

              // Create new coupon and link to purchaser.
              $new_coupon = uc_coupon_purchase_create($coupon, $product->qty, $order->uid);
              uc_order_comment_save($order->order_id, 0, t('Coupon created with code %code.', array('%code' => $new_coupon->code)));

              // Fire hook and conditional actions to send emails for this coupon purchase.
              module_invoke_all('uc_coupon_purchase', $order, $new_coupon, $product);
              rules_invoke_event('uc_coupon_purchase', $order, $new_coupon, $product);
            }
          }
        }
      }
      break;
  }
}

/**
 * Create newly purchased coupons, based on the supplied coupon object.
 */
function uc_coupon_purchase_create($coupon, $qty, $uid = NULL) {
  $suffix_length = isset($coupon->data['purchase_suffix_length']) ? $coupon->data['purchase_suffix_length'] : 12;

  // If more than one coupon will be generated, ensure it is a bulk coupon.
  if ($qty > 1 || $coupon->bulk) {
    $coupon->bulk = TRUE;

    if (!isset($coupon->data['bulk_number'])) {
      $coupon->data['bulk_number'] = 1;
    }
    $coupon->data['bulk_number'] *= $qty;

    if (!isset($coupon->data['bulk_length'])) {
      $coupon->data['bulk_length'] = 8;
    }

    // Adjust the suffix so single and bulk coupons are the same length where possible.
    $suffix_length = max($suffix_length - $coupon->data['bulk_length'], 4);
  }

  // Adjust the validity dates.
  if (isset($coupon->data['purchase_relative'])) {
    $diff = $coupon->valid_until - $coupon->valid_from;
    $coupon->valid_from = gmmktime();
    $coupon->valid_until = $coupon->valid_from + $diff;
  }

  // Create a new, active coupon with the suffix applied.
  $coupon->data['base_cid'] = $coupon->cid;
  unset($coupon->cid);
  $coupon->code .= strtoupper(user_password($suffix_length));
  $coupon->status = TRUE;
  uc_coupon_save($coupon);

  // Associate this coupon with its owner.
  if (!is_null($uid)) {
    $record = array('uid' => $uid, 'cid' => $coupon->cid);
    drupal_write_record('uc_coupon_purchase_users', $record);
  }

  return $coupon;
}

/**
 * Implements hook_views_api().
 */
function uc_coupon_purchase_views_api() {
  return array(
    'api' => '2.0',
    'path' => drupal_get_path('module', 'uc_coupon_purchase') . '/views',
  );
}

/**
 * Helper function to create the product attribute specifying gift certificate
 * recipients. Will only create the attribute if it does not already exist.
 *
 * @return
 *   The recipient attribute.
 */
function _uc_coupon_purchase_create_recipient_attribute() {
  if (!$attribute = _uc_coupon_purchase_load_recipient_attribute()) {
    $attribute = array(
      'name' => UC_COUPON_PURCHASE_RECIPIENT_ATTRIBUTE,
      'label' => t('Gift Certificate Recipient'),
      'description' => t('The e-mail address of the recipient of this gift certificate'),
      'required' => FALSE,
      'display' => 0,
      'ordering' => 0,
    );
    $attribute = (object) $attribute;
    uc_attribute_save($attribute);
    // Flush the cache.
    $attribute = _uc_coupon_purchase_load_recipient_attribute(TRUE);
  }
  return $attribute;
}

/**
 * Helper function to load the attribute which we use for gift certificate recipients.
 *
 * @reset
 *   TRUE to flush the cache.
 * @return
 *   The loaded attribute object, or FALSE if not found. 
 */
function _uc_coupon_purchase_load_recipient_attribute($reset = FALSE) {
  static $attribute = NULL;
  
  if (!module_exists('uc_attribute')) {
    return FALSE;
  }
  elseif (is_null($attribute) || $reset) {
    $aid = db_query('SELECT aid FROM {uc_attributes} WHERE name = :name', array(':name' => UC_COUPON_PURCHASE_RECIPIENT_ATTRIBUTE))->fetchField(0);
    $attribute = $aid ? uc_attribute_load($aid) : FALSE;
  }
  return $attribute;
}

/**
 * Implements hook_form_FORM_ID_alter() for uc_attribute_form.
 */
function uc_coupon_purchase_form_uc_attribute_form_alter(&$form, &$form_state) {
  // If our reserved attribute is being edited, prevent the name from being changed.
  if (!empty($form['aid']['#value']) 
      && ($attribute = uc_attribute_load($form['aid']['#value']))
      && $attribute->name === UC_COUPON_PURCHASE_RECIPIENT_ATTRIBUTE) {
    $form['name']['#disabled'] = TRUE;
    $form['name']['#description'] .= ' ' . t('This attribute is used by uc_coupon_purchase to send additional notifications to purchased coupon recipients. The name cannot be altered.');
  }
  $form['#validate'][] = 'uc_coupon_purchase_attribute_form_validate';
}

/**
 * Extra validation callback for uc_attribute_form.
 */
function uc_coupon_purchase_attribute_form_validate($form, $form_state) {
  // For a new attribute, prevent use of the reserved name.
  if (empty($form['aid']) && $form_state['values']['name'] === UC_COUPON_PURCHASE_RECIPIENT_ATTRIBUTE) {
    form_set_error('name', t('The name is reserved for use by uc_coupon_purchase.'));
  }
}

/**
 * Implements hook_form_FORM_ID_alter() for uc_prodcut_add_to_cart_form.
 */
function uc_coupon_purchase_form_uc_product_add_to_cart_form_alter(&$form, &$form_state) {
  $form['#validate'][] = 'uc_coupon_purchase_add_to_cart_form_validate';
}

/**
 * Extra validation callback for uc_product_add_to_cart_form.
 */
function uc_coupon_purchase_add_to_cart_form_validate($form, $form_state) {
  // Ensure that the recipient (if specified) is a valid e-mail address.
  $attribute = _uc_coupon_purchase_load_recipient_attribute();
  if ($attribute && !empty($form['attributes'][$attribute->aid])) {
    $email = trim($form_state['values']['attributes'][$attribute->aid]);
    if (!empty($email) && !valid_email_address($email)) {
      form_set_error("attributes][{$attribute->aid}", t('You must specify a valid e-mail address.'));
    }
  }
}

/**
 * Implements hook_form_FORM_ID_alter() for uc_coupon_settings_form.
 */
function uc_coupon_purchase_form_uc_coupon_settings_form_alter(&$form, &$form_state) {
  $form['uc_coupon_purchase'] = array(
    '#type' => 'fieldset',
    '#title' => t('Coupon purchase settings'),
  );
  
  $form['uc_coupon_purchase']['uc_coupon_purchase_recipient'] = array(
    '#type' => 'item',
    '#title' => t('Gift coupon recipient field'),
    'uc_coupon_purchase_recipient_enabled' => array(
      '#type' => 'checkbox',
      '#title' => t('Enable the "uc_coupon_purchase_recipient" product attribute.'),
      '#description' => t('When checked, a "uc_coupon_purchase_recipient" product attribute wil be created.
        When you attach this attribute to a product with the "create coupon" feature, customers will be able
        to enter the e-mail address of a recipient. You can then configure a rule to send a customized
        e-mail notification to the recipient.'),
      '#default_value' => variable_get('uc_coupon_purchase_recipient_enabled', FALSE),
      '#access' => module_exists('uc_attribute'),
    ),
    '#description' => module_exists('uc_attribute') ? NULL : t('You must enable the uc_attribute module to activate this feature.'),
  ); 
  $form['#submit'][] = 'uc_coupon_purchase_coupon_settings_form_submit';
}

/**
 * Submit handler for uc_coupon_settings_form.
 */
function uc_coupon_purchase_coupon_settings_form_submit($form, $form_state) {
  // Create the recipient attribute.
  if (!empty($form_state['values']['uc_coupon_purchase_recipient_enabled'])) {
    if (module_exists('uc_attribute')) {
      if (!$attribute = _uc_coupon_purchase_load_recipient_attribute()) {
        $attribute = _uc_coupon_purchase_create_recipient_attribute();
        drupal_set_message(t('The "@name" attribute has been created', array('@name' => $attribute->name)));
      }
      
    }
  }
}

// ****** NOTE THE FOLLOWING CAN HOPEFULLY BE REMOVED ONCE THIS FUNCTIONALITY
//        IS INCLUDED IN UBERCART CORE. See http://drupal.org/node/1632540.
function uc_coupon_purchase_entity_property_info_alter(&$info) {
  $r = db_query('SELECT name, label from {uc_attributes}');
  foreach ($r as $row) {
    $entry = array(
      'type' => 'text',
      'label' => $row->name, // Use the name, because the label can differ among products.
      'description' => $row->label,
      'getter callback' => 'uc_coupon_purchase_attribute_property_getter',
    );
    foreach (array('uc_order_product', 'uc_cart_item') as $type) {
      if (isset($info[$type]) && !isset($info[$type]['properties'][$row->name])) {
        $info[$type]['properties'][$row->name] = $entry;
      }
    }
  }
}

function uc_coupon_purchase_attribute_property_getter($product, $options, $name, $type, $info) {
  if (!empty($product->data['attributes'])) {
    if ($type == 'uc_cart_item') {
      $product = clone $product;
      uc_attribute_uc_order_product_alter($product, NULL);
    }
    $attributes = uc_attribute_load_product_attributes($product->nid);
    $key = FALSE;
    foreach ($attributes as $aid => $attribute) {
      if ($attribute->name == $name) {
        $key = $attribute->label;
        break;
      }
    }
    return ($key && !empty($product->data['attributes'][$key])) ? implode(',', $product->data['attributes'][$key]) : NULL; 
  }
}
