<?php
// $Id$

/**
 * @file
 * Allows discounts to products and orders based on various criteria.
 *
 * Developed by Marshal Newrock (http://idealso.com/ | marshal@idealso.com)
 */

/**
 * Yes/No constants.  Allows to easily change the case later.
 */
define('UC_DISCOUNTS_YES', t('yes'));
define('UC_DISCOUNTS_NO', t('no'));

/*******************************************************************************
 * Hook Functions (Drupal)
 ******************************************************************************/

/**
 * Implementation of hook_perm().
 */
function uc_discounts_perm() {
  return array('administer discounts');
}

/**
 * Implementation of hook_menu()
 */
function uc_discounts_menu($may_cache) {
  $items = array();
  $access_administer_discounts = user_access('administer discounts');

  if ($may_cache) {
    $items[] = array(
      'path' => 'admin/store/discounts',
      'title' => t('Discounts'),
      'description' => t('Administer discounts'),
      'access' => $access_administer_discounts,
      'callback' => 'uc_discounts_show_discounts',
      'type' => MENU_NORMAL_ITEM,
    );
    $items[] = array(
      'path' => 'admin/store/discounts/add',
      'title' => t('Add Discount'),
      'access' => $access_administer_discounts,
      'callback' => 'drupal_get_form',
      'callback arguments' => 'uc_discounts_discount_form',
      'type' => MENU_CALLBACK,
    );
    $items[] = array(
      'path' => 'admin/store/discounts/valuehtml',
      'access' => $access_administer_discounts,
      'callback' => 'uc_discounts_get_condition_value_html',
    );
  }
  else {
    $discount_id = arg(3);
    if (arg(0) == 'admin' && arg(1) == 'store' && arg(2) == 'discounts' && is_numeric($discount_id)) {
      // conditions
      if (arg(4) == 'conditions') {
        $discount = uc_discounts_load_discount($discount_id);
        $items[] = array(
          'path' => "admin/store/discounts/$discount_id/conditions",
          //'title' => check_plain($discount->name),
          'callback' => 'drupal_goto',
          'callback arguments' => "admin/store/discounts/$discount_id",
          'access' => $access_administer_discounts,
          'type' => MENU_CALLBACK,
        );
        $items[] = array(
          'path' => "admin/store/discounts/$discount_id/conditions/add",
          'description' => check_plain($discount->name),
          'callback' => 'drupal_get_form',
          'callback arguments' => array('uc_discounts_condition_form', $discount_id, 0),
          'access' => $access_administer_discounts,
          'type' => MENU_CALLBACK,
        );
        $condition_id = arg(5);
        if (is_numeric($condition_id)) {
          $items[] = array(
            'path' => "admin/store/discounts/$discount_id/conditions/$condition_id",
            'callback' => 'drupal_get_form',
            'callback arguments' => array('uc_discounts_condition_form', $discount_id, $condition_id),
            'access' => $access_administer_discounts,
            'type' => MENU_CALLBACK,
          );
          $items[] = array(
            'path' => "admin/store/discounts/$discount_id/conditions/$condition_id/delete",
            'callback' => 'uc_discounts_condition_delete',
            'callback arguments' => array($discount_id, $condition_id),
            'access' => $access_administer_discounts,
            'type' => MENU_CALLBACK,
          );
        }
      }
      // actions
      elseif (arg(4) == 'actions') {
        $discount = uc_discounts_load_discount($discount_id);
        $items[] = array(
          'path' => "admin/store/discounts/$discount_id/actions",
          //'title' => check_plain($discount->name),
          'callback' => 'drupal_goto',
          'callback arguments' => "admin/store/discounts/$discount_id",
          'access' => $access_administer_discounts,
          'type' => MENU_CALLBACK,
        );
        $items[] = array(
          'path' => "admin/store/discounts/$discount_id/actions/add",
          'description' => check_plain($discount->name),
          'callback' => 'drupal_get_form',
          'callback arguments' => array('uc_discounts_action_form', $discount_id, 0),
          'access' => $access_administer_discounts,
          'type' => MENU_CALLBACK,
        );
        $action_id = arg(5);
        if (is_numeric($action_id)) {
          $items[] = array(
            'path' => "admin/store/discounts/$discount_id/actions/$action_id",
            'callback' => 'drupal_get_form',
            'callback arguments' => array('uc_discounts_action_form', $discount_id, $action_id),
            'access' => $access_administer_discounts,
            'type' => MENU_CALLBACK,
          );
          $items[] = array(
            'path' => "admin/store/discounts/$discount_id/actions/$action_id/delete",
            'callback' => 'uc_discounts_action_delete',
            'callback arguments' => array($discount_id, $action_id),
            'access' => $access_administer_discounts,
            'type' => MENU_CALLBACK,
          );
        }
      }
      // just the discounts
      else {
        $items[] = array(
          'path' => 'admin/store/discounts/' . $discount_id,
          'description' => t('View discount') . $discount_id,
          'callback' => 'uc_discounts_discount_view',
          'callback arguments' => $discount_id,
          'access' => $access_administer_discounts,
          'type' => MENU_CALLBACK,
        );
        $items[] = array(
          'path' => "admin/store/discounts/$discount_id/view",
          'title' => t('view'),
          'type' => MENU_DEFAULT_LOCAL_TASK,
          'weight' => -10,
        );
        $items[] = array(
          'path' => "admin/store/discounts/$discount_id/edit",
          'title' => t('edit'),
          'callback' => 'drupal_get_form',
          'callback arguments' => array('uc_discounts_discount_form', $discount_id),
          'access' => $access_administer_discounts,
          'weight' => 1,
          'type' => MENU_LOCAL_TASK,
        );
        $items[] = array(
          'path' => "admin/store/discounts/$discount_id/delete",
          'callback' => 'drupal_get_form',
          'callback arguments' => array('uc_discounts_discount_delete_form', $discount_id),
          'access' => $access_administer_discounts,
          'type' => MENU_CALLBACK,
        );
      }
    }
  }

  return $items;
}

/**
 * Format an "item: value" list, with item being <strong>
 *
 * @param $items
 *   Array of items, with each item having "label" and "value"
 * @ingroup themeable
 **/
function theme_uc_discounts_list($items) {
  $output = '<div class="border">';
  foreach ($items as $item) {
    if (!isset($item['label']) || !isset($item['value'])) {
      continue;
    }
    $output .= '<strong>' . t($item['label']) . '</strong>: ';
    $output .= check_plain($item['value']);
    $output .= "<br />\n";
  }
  $output .= "</div>\n";

  $element = array(
    '#children' => $output,
  );

  return theme('fieldset', $element);
}

/*******************************************************************************
 * Hook Functions (Ubercart)
 ******************************************************************************/

/**
 * Implementation of hook_line_item()
 */
function uc_discounts_line_item() {
  $items[] = array(
    'id' => 'discount',
    'title' => t('Discounts'),
    'callback' => 'uc_line_item_discounts',
    'weight' => 2,
    'stored' => FALSE,
    'default' => FALSE,
    'calculated' => TRUE,
    'display_only' => FALSE,
  );

  return $items;
}

function uc_line_item_discounts($op, $arg1) {
  switch ($op) {
	 case 'load':
	 case 'display':
	 	 
    $lines[] = array(
      'id' => 'discount',
      'title' => t('Discount'),
      'amount' => $arg1->discounts['amount'],
    );
    return $lines;
		
		
    case 'cart-preview':
			
      $apply_discounts = uc_discounts_apply_discounts($arg1);
			
      if (empty($apply_discounts)) {
        return;
      }
      $discount_total = 0;
			$script = '';
			
      foreach ($apply_discounts as $discount) {
				$discount_total -= $discount['amount'];
			}
			
			if ($discount_total != 0) {
				$script .= "set_line_item('discounts', '". t('Total discount') ."', ". $discount_total .", 1);\n";
			}
			
			if ($script) {
				drupal_add_js("\$(document).ready( function() { ". $script ." } );", 'inline');
			}
		
		break;	   
	}
}


/**
 * Implementation of hook_checkout_pane
 */
function uc_discounts_checkout_pane() {
  $panes[] = array(
    'id' => 'discounts',
    'title' => t('Discounts'),
    'desc' => t('Show discounts for the order'),
    'callback' => 'uc_checkout_pane_discounts',
    'weight' => 1.5,
  );
  return $panes;
}

/**
 * Add the discount pane to the checkout screen
 */
function uc_checkout_pane_discounts($op, &$arg1, $arg2) {
  // get discounts to apply to this order

  switch ($op) {	
    case 'view':
      // there is no order at this time.  just display discounts
      $apply_discounts = uc_discounts_apply_discounts();
      if (empty($apply_discounts)) {
        return;
      }

			$description = t('The following discounts will be applied to your order:');			
			
      // create discounts table
      $header = array(
        t('Discount'),
        array('data' => t('Amount'), 'class' => 'text-right'),
      );

      $rows = array();
      $discount_total = 0;
      foreach ($apply_discounts as $discount) {
        $rows[] = array(
          $discount['description'],
          array('data' => uc_currency_format($discount['amount']), 'class' => 'text-right'),
        );
        $discount_total += $discount['amount'];
      }

      $rows[] = array(
        '',
        array(
          'data' => '<strong>'.t('Subtotal').':</strong> '.uc_currency_format($discount_total),
          'colspan' => 2,
          'class' => 'text-right',
        ),
      );
      $table = theme('table', $header, $rows);
 			$contents['discounts'] = array('#value' => $table );
									
      return array('description' => $description, 'contents' => $contents, 'next-button' => TRUE);

		case 'process':
      $apply_discounts = uc_discounts_apply_discounts();
      if (empty($apply_discounts)) {
        return;
      }
      $discount_total = 0;
      foreach ($apply_discounts as $discount) {
				$arg1->discounts['description'] = $discount['description'];
				$arg1->discounts['amount'] = $discount['amount'];
			}
			
		return TRUE;

    case 'review':

      // clear any existing line items
      $review[] = array('title' => t('Total discount'), 'data' => $arg1->discounts['amount']);
      return $review;

  }
}

/**
 * Implementation of hook_order
 */
function uc_discounts_order($op, &$arg1, $arg2) {
  switch ($op) {
    // TOTAL
    case 'total':
      return $arg1->discounts['amount'];
			
    // Load the discount from the database.
    case 'load':
      $result = db_query("SELECT * FROM {uc_order_line_items} WHERE order_id = %d AND type = 'discount'", $arg1->order_id);
      if ($data = db_fetch_object($result)) {
        $arg1->discounts['description'] = $data->title;
        $arg1->discounts['amount'] = $data->amount;
       }
      break;

    // SAVE
    case 'save':
      // clear any existing discount line items from this order
      db_query("DELETE FROM {uc_order_line_items} WHERE order_id=%d AND type='discount'", $arg1->order_id);

      $apply_discounts = uc_discounts_apply_discounts();
      if (empty($apply_discounts)) {
        return;
      }
      $discount_total = 0;
      foreach ($apply_discounts as $discount) {
        // store as negative
        $discount_total -= $discount['amount'];

      }
			db_query("INSERT INTO {uc_order_line_items} (line_item_id, order_id, type, title, amount, weight) VALUES ('', %s, 'discount', '%s', %s, 1)", $arg1->order_id, t('toto'), $discount_total);

      return $discount_total;

    // DELETE
    case 'delete':
      db_query("DELETE FROM {uc_order_line_items} WHERE order_id=%d AND type='discount'", $arg1->order_id);
      break;

  }
}

/**
 * Implementation of hook_order_pane().
 */
function uc_discounts_order_pane() {
  $panes[] = array(
    'id' => 'discounts',
    'callback' => 'uc_order_pane_discounts',
    'title' => t('See discounts'),
    'desc' => t('See the discounts applied to an order.'),
    'class' => 'abs-left',
    'weight' => 5,
    'show' => array('view'),
  );

  return $panes;
}

function uc_order_pane_discounts($op, $arg1) {
  switch ($op) {
    case 'view':
      if (empty($arg1->discounts['amount'])) {
        $discounts = t('None specified');
      } else {
        $discounts = $arg1->discounts['amount'];
      }
      return t('Discount applied : !discounts', array('!discounts' => $discounts));
  }
}


/*******************************************************************************
 * Callback Functions, Forms, and Tables
 ******************************************************************************/

/**
 * Check order against all active discounts
 */
function uc_discounts_apply_discounts() {
  // cache discounts.  return if already set
  static $apply_discounts = NULL;
  if ($apply_discounts !== NULL) {
    return $apply_discounts;
  }

  $cart = uc_cart_get_contents();
  $cart_copy = array();
  foreach ($cart as $cart_item) {
    $cart_copy[] = drupal_clone($cart_item);
  }

  // get and store total price
  $total_price = 0;
  foreach ($cart_copy as $product) {
    $total_price += $product->price * $product->qty;
  }

  // check against order discounts
  $apply_discounts = array();
  $discount_list = uc_discounts_get_discounts();

  $condition_info = _uc_discounts_get_conditions();
  $action_info = _uc_discounts_get_actions();

  foreach ($discount_list as $discount) {
    // skip discounts which may not be applied with other discounts
    if ($discount->is_exclusive && !empty($apply_discounts)) {
      continue;
    }
    // load conditions for the discount
    $condition_list = uc_discounts_get_discount_conditions($discount->id);
    $condition_groups = array();
    foreach ($condition_list as $condition) {
      // skip if this condition group has already failed
      if (isset($condition_groups[$condition->condition_group]) && $condition_groups[$condition->condition_group] == FALSE) {
        continue;
      }

      // check if condition matches
      if (isset($condition_info[$condition->property]['check_callback'])) {
        $check_callback = $condition_info[$condition->property]['check_callback'];
        $condition_groups[$condition->condition_group] = $check_callback($condition, $total_price, $cart_copy);
      }
    }

    // did any condition groups match
    if (array_search(TRUE, $condition_groups) !== FALSE) {
      $action_list = uc_discounts_get_discount_actions($discount->id);
      foreach ($action_list as $action) {
        if (!isset($action_info[$action->property]['apply_callback'])) {
          continue;
        }
        $apply_callback = $action_info[$action->property]['apply_callback'];
        $discount_amount = $apply_callback($op, $action, $total_price, $cart_copy);
        if ($discount_amount > 0) {
          $apply_discounts[$discount->id] = array(
            'description' => check_plain($discount->description),
            'amount' => $discount_amount,
          );
          $total_price -= $discount_amount;
        }
      }
    }
  }

  // refresh the cart - somehow, the price modules are affecting it
  //$cart = uc_cart_get_contents(NULL, 'rebuild');
  return $apply_discounts;
}

/**
 * Generate a page which shows all of the discounts
 */
function uc_discounts_show_discounts() {
  $discounts = uc_discounts_table();
  $discounts .= l('Add Discount', 'admin/store/discounts/add');

  $element = array(
    '#title' => t('Discounts'),
    '#children' => $discounts,
  );

  return theme('fieldset', $element);
}

/**
 * View the information for a discount
 *
 * @param $discount_id
 * @return
 *   formatted block with discount info
 */
function uc_discounts_discount_view($discount_id) {
  $discount = uc_discounts_load_discount($discount_id);

  // must be a valid discount
  if ($discount->id == 0) {
    drupal_goto('/admin/store/discounts');
  }

  drupal_set_title($discount->name);
  $output = uc_discounts_discount_info($discount);
  $output .= uc_discounts_conditions_table($discount->id);
  $output .= uc_discounts_actions_table($discount->id);

  return $output;
}

/**
 * Format the information for the list of discounts in a table.
 *
 * @return
 *   themed table
 */
function uc_discounts_table() {
  $header = array(
    t('Name'),
    t('Active'),
    t('Weight'),
    t('End Processing'),
    t('Exclusive'),
    t('Start Time'),
    t('End Time'),
    //array('data' => t('Actions'), 'colspan' => 3),
  );

  $caption = t('Discounts');

  $rows = array();
  $discount_list = uc_discounts_get_discounts(TRUE);
  foreach ($discount_list as $discount) {
    $rows[] = array(
      l($discount->name, 'admin/store/discounts/'.$discount->id),
      $discount->is_active ? UC_DISCOUNTS_YES : UC_DISCOUNTS_NO,
      $discount->weight,
      $discount->do_end_processing ? UC_DISCOUNTS_YES : UC_DISCOUNTS_NO,
      $discount->is_exclusive ? UC_DISCOUNTS_YES : UC_DISCOUNTS_NO,
      format_date($discount->start_time),
      format_date($discount->end_time),
      //l(t('View'), 'admin/store/discounts/'.$discount->id),
      //l(t('Edit'), 'admin/store/discounts/'.$discount->id.'/edit'),
      //l(t('Delete'), 'admin/store/discounts/'.$discount->id.'/delete'),
    );
  }
  $table = theme('table', $header, $rows);

  return $table;
}

/**
 * Format discount information
 *
 * @param $discount
 * @return
 *   formatted block of discount info
 */
function uc_discounts_discount_info($discount) {
  $rows = array(
    array(
      'label' => 'Name',
      'value' => $discount->name,
    ),
    array(
      'label' => 'Description',
      'value' => $discount->description,
    ),
    array(
      'label' => 'Max Discounts',
      'value' => $discount->max_discounts,
    ),
    array(
      'label' => 'End Processing',
      'value' => $discount->do_end_processing ? UC_DISCOUNTS_YES : UC_DISCOUNTS_NO,
    ),
    array(
      'label' => 'Exclusive',
      'value' => $discount->is_exclusive ? UC_DISCOUNTS_YES : UC_DISCOUNTS_NO,
    ),
    array(
      'label' => 'Weight',
      'value' => $discount->weight,
    ),
    array(
      'label' => 'Start Time',
      'value' => format_date($discount->start_time),
    ),
    array(
      'label' => 'End Time',
      'value' => format_date($discount->end_time),
    ),
    array(
      'label' => 'Active',
      'value' => $discount->is_active ? UC_DISCOUNTS_YES : UC_DISCOUNTS_NO,
    ),
  );

  $output = theme('uc_discounts_list', $rows);

  return $output;
}

/**
 * Provides the basic discount form
 *
 * Loads the specified discount id, and pre-populated the form based on this.
 * If this is for a new discount, 0 is passed, getting default values.
 *
 * @param $discount_id
 */
function uc_discounts_discount_form($discount_id = 0) {
  $discount = uc_discounts_load_discount($discount_id);

  $start_time = getdate($discount->start_time);
  $default_start_time = array(
    'month' => $start_time['mon'],
    'day' => $start_time['mday'],
    'year' => $start_time['year'],
  );

  $end_time = getdate($discount->end_time);
  $default_end_time = array(
    'month' => $end_time['mon'],
    'day' => $end_time['mday'],
    'year' => $end_time['year'],
  );

  if ($discount->id == 0) {
    drupal_set_title(t('New Discount'));
  }
  else {
    drupal_set_title($discount->name);
  }

  $form['discount_id'] = array(
    '#type' => 'value',
    '#value' => $discount->id,
  );
  $form['discount_name'] = array(
    '#title' => t('Discount Name'),
    '#type' => 'textfield',
    '#description' => t('Name of this discount.'),
    '#required' => TRUE,
    '#default_value' => $discount->name,
  );
  $form['description'] = array(
    '#title' => t('Description'),
    '#type' => 'textfield',
    '#description' => t('A brief description.  This will also show up on invoices.'),
    '#required' => TRUE,
    '#default_value' => $discount->description,
  );
  $form['max_discounts'] = array(
    '#title' => t('Max Discounts'),
    '#type' => 'textfield',
    '#description' => t('This discount can be applied up to this many times in a single order.  Set to 0 for unlimited'),
    '#default_value' => 1,
    '#size' => 4,
    '#required' => TRUE,
    '#default_value' => $discount->max_discounts,
  );
  $form['do_end_processing'] = array(
    '#title' => t('End Processing'),
    '#type' => 'checkbox',
    '#description' => t('If set and this discount matches, no further discounts will be processed'),
    '#default_value' => $discount->do_end_processing,
  );
  $form['is_exclusive'] = array(
    '#title' => t('Exclusive'),
    '#type' => 'checkbox',
    '#description' => t('If checked, this discount will not be processed if any other discounts before this have matched.  If no other discounts have matched and this one matches, no further discounts will be processed'),
    '#default_value' => $discount->is_exclusive,
  );
  $form['weight'] = array(
    '#title' => t('Weight'),
    '#type' => 'weight',
    '#description' => t('Determines the order in which discounts are applied'),
    '#default_value' => 0,
    '#size' => 4,
    '#required' => TRUE,
    '#default_value' => $discount->weight,
  );
  $form['start_time'] = array(
    '#title' => t('Start Date'),
    '#type' => 'date',
    '#description' => 'Start date for this discount',
    '#required' => TRUE,
    '#default_value' => $default_start_time,
  );
  $form['end_time'] = array(
    '#title' => t('End Date'),
    '#type' => 'date',
    '#description' => 'End date for this discount',
    '#required' => TRUE,
    '#default_value' => $default_end_time,
  );
  $form['is_active'] = array(
    '#title' => t('Active'),
    '#type' => 'checkbox',
    '#description' => 'If not active, the discount is not available regardless of the start and end times.',
    '#default_value' => 1,
    '#default_value' => $discount->is_active,
  );
  if ($discount->id == 0) {
    $form['submit'] = array(
      '#type' => 'submit',
      '#value' => t('Save'),
    );
  }
  else {
    $form['submit'] = array(
      '#type' => 'submit',
      '#value' => t('Update'),
    );
    $form['delete_button'] = array(
      '#type' => 'submit',
      '#name' => 'delete',
      '#value' => t('Delete'),
    );
  }

  return $form;
}

/**
 * discounts form submission
 */
function uc_discounts_discount_form_submit($form_id, $form_values) {
  if ($form_values['discount_id'] == 0) {
    $id = db_next_id('{uc_discounts}_id');
    $sql = "INSERT INTO {uc_discounts}
      (name, description, max_discounts, do_end_processing, is_exclusive,
      weight, start_time, end_time, is_active, id)
      VALUES ('%s', '%s', %d, %d, %d, %d, %d, %d, %d, %d)";
  }
  else {
    $id = $form_values['discount_id'];
    $sql = "UPDATE {uc_discounts} SET name='%s', description='%s',
      max_discounts=%d, do_end_processing=%d, is_exclusive=%d, weight=%d,
      start_time=%d, end_time=%d, is_active=%d WHERE id=%d";
  }

  $start_time = mktime(0, 0, 0,
    $form_values['start_time']['month'],
    $form_values['start_time']['day'],
    $form_values['start_time']['year']
  );
  $end_time = mktime(23, 59, 59,
    $form_values['end_time']['month'],
    $form_values['end_time']['day'],
    $form_values['end_time']['year']
  );
  db_query($sql,
    $form_values['discount_name'],
    $form_values['description'],
    $form_values['max_discounts'],
    $form_values['do_end_processing'],
    $form_values['is_exclusive'],
    $form_values['weight'],
    $start_time,
    $end_time,
    $form_values['is_active'],
    $id
  );

  return "admin/store/discounts/$id";
}

/**
 * discounts form validation
 */
function uc_discounts_discount_form_validate($form_id, $form_values) {
  if (isset($form_values['delete'])) {
    drupal_goto('admin/store/discounts/'.$form_values['discount_id'].'/delete');
  }
  if (!isset($form_values['discount_id'])) {
    drupal_set_message(t('Invalid form submission'), 'error');
    drupal_goto('admin/store/discounts');
  }

  $start_time = mktime(0, 0, 0,
    $form_values['start_time']['month'],
    $form_values['start_time']['day'],
    $form_values['start_time']['year']
  );
  $end_time = mktime(23, 59, 59,
    $form_values['end_time']['month'],
    $form_values['end_time']['day'],
    $form_values['end_time']['year']
  );
  $today = time();
  if ($end_time <= $start_time) {
    form_set_error('end_time', t('End date can not be before the start date'));
  }
  if ($end_time < $today) {
    form_set_error('end_time', t('Setting the end date in the past is pointless.'));
  }
}

/**
 * Discount deletion form.
 *
 * @param $discount_id
 *   Must be a valid and existing discount
 */
function uc_discounts_discount_delete_form($discount_id) {
  if ($discount == 0) {
    drupal_goto('admin/store/discounts');
  }

  $discount = uc_discounts_load_discount($discount_id);
  if (!$discount) {
    drupal_goto('admin/store/discounts');
  }

  drupal_set_title(t('Delete !name', array('!name' => $discount->name)));

  $form['discount_id'] = array(
    '#type' => 'value',
    '#value' => $discount->id,
  );
  $form['discount_name'] = array(
    '#type' => 'value',
    '#value' => $discount->name,
  );
  $form['discount_info'] = array(
    '#value' => uc_discounts_discount_info($discount),
  );
  $output = confirm_form($form,
    t('Delete !name?', array('!name' => $discount->name)),
    'admin/store/discounts/'.$discount->id,
    '',
    t('Delete'),
    t('Cancel')
  );

  return $output;
}

/**
 * discounts deletion form submissions
 */
function uc_discounts_discount_delete_form_submit($form_id, $form_values) {
  if ($form_values['confirm']) {
    db_query('DELETE FROM {uc_discounts} WHERE id=%d', $form_values['discount_id']);
    drupal_set_message(t('Discount "!name" has been deleted', array('!name' => $form_values['discount_name'])));
  }

  return 'admin/store/discounts';
}

/**
 * Format the information for the list of conditions in a table.
 *
 * @param $discount_id
 * @return
 *   themed table
 */
function uc_discounts_conditions_table($discount_id) {
  $condition_list = uc_discounts_get_discount_conditions($discount_id);

  $header = array(
    t('Group'),
    t('Weight'),
    t('Property'),
    t('Op'),
    t('Item'),
    t('Value'),
    array('data' => t('Actions'), 'colspan' => 2),
  );

  $properties = _uc_discounts_get_condition_form_values();
  $condition_info = _uc_discounts_get_conditions();

  $rows = array();
  $base_url = 'admin/store/discounts/'.$discount_id.'/conditions';
  foreach ($condition_list as $condition) {
    // get item name
    if (isset($condition_info[$condition->property]['item_name_callback'])) {
      $item_name_callback = $condition_info[$condition->property]['item_name_callback'];
      $item = $item_name_callback($condition->item_id);
    }
    else {
      $item = '';
    }

    // format value
    if (isset($condition_info[$condition->property]['value_format_callback'])) {
      $value_format_callback = $condition_info[$condition->property]['value_format_callback'];
      $value = $value_format_callback($condition->value);
    }
    else {
      $value = $condition->value;
    }

    $rows[] = array(
      $condition->condition_group,
      $condition->weight,
      $properties[$condition->property],
      $condition->op,
      $item,
      $value,
      l(t('edit'), $base_url.'/'.$condition->id),
      l(t('delete'), $base_url.'/'.$condition->id.'/delete'),
    );
  }
  $table = theme('table', $header, $rows);
  $table .= l('Add Condition', $base_url.'/add');

  $element = array(
    '#title' => t('Conditions'),
    '#collapsible' => TRUE,
    '#children' => $table,
  );

  return theme('fieldset', $element);
}

/**
 * Provides the discount condition form
 *
 * Loads the specified condition id, and pre-populated the form based on this.
 * If this is for a new condition, 0 is passed, getting default values.
 * Otherwise, this checks to make sure that the condition belongs to the
 * specified discount.
 *
 * @param $discount_id
 * @param acondition_id
 */
function uc_discounts_condition_form($discount_id, $condition_id, $form_values = NULL) {
  $condition = uc_discounts_load_condition($condition_id);
  if (!$condition) {
    drupal_goto('admin/store/discounts');
  }

  // make sure condition belongs to the right discount
  if ($condition_id > 0 && $condition->discount_id != $discount_id) {
    drupal_goto('admin/store/discounts');
  }

  $discount = uc_discounts_load_discount($discount_id);
  $properties = _uc_discounts_get_condition_form_values();

  // get condition property if set
  if (!empty($condition->property)) {
    $condition_property = $condition->property;
  }
  elseif (isset($form_values['property'])) {
    $condition_property = $form_values['property'];
  }
  else {
    $condition_property = '';
  }

  // make sure condition property is valid
  $condition_info = _uc_discounts_get_conditions();
  if ($condition_property && !isset($condition_info[$condition_property])) {
    $condition_property = '';
  }

  // set page title
  if ($condition_id == '') {
    drupal_set_title(t('@name - Add Condition', array('@name' => $discount->name)));
  }
  else {
    drupal_set_title(t('@name - Edit Condition', array('@name' => $discount->name)));
  }

  // values for all stages
  $form['#multistep'] = TRUE;
  $form['condition_id'] = array(
    '#type' => 'value',
    '#value' => $condition->id,
  );
  $form['discount_id'] = array(
    '#type' => 'value',
    '#value' => $discount_id,
  );

  if ($condition_property) {
    $step = 2;
  }
  else {
    $step = 1;
  }
  $form['step'] = array(
    '#type' => 'hidden',
    '#value' => $step + 1,
  );

  // stage 1 - select property
  if ($step == 1) {
    $form['#redirect'] = FALSE;
    $form['property'] = array(
      '#type' => 'select',
      '#title' => t('Property'),
      '#options' => $properties,
    );
    $form['submit'] = array(
      '#type' => 'submit',
      '#value' => t('Next'),
    );
  }

  // stage 2 - everything else
  else {
    // get item field fields for specific condition types
    if (isset($condition_info[$condition_property]['value_field_callback'])) {
      $value_field_callback = $condition_info[$condition_property]['value_field_callback'];
      $value_field = $value_field_callback();
    }
    else {
      $value_field = array(
        '#type' => 'value',
        '#value' => '',
      );
    }

    // item field is not required
    if (isset($condition_info[$condition_property]['item_field_callback'])) {
      $item_field_callback = $condition_info[$condition_property]['item_field_callback'];
      $item_field = $item_field_callback();
    }
    else {
      $item_field = array(
        '#type' => 'value',
        '#value' => 0,
      );
    }

    $form['property'] = array(
      '#type' => 'value',
      '#value' => $condition_property,
    );
    $form['display_property'] = array(
      '#type' => 'item',
      '#title' => t('Property'),
      '#value' => $properties[$condition_property],
    );
    $form['weight'] = array(
      '#type' => 'weight',
      '#title' => t('Weight'),
      '#default_value' => $condition->weight,
    );
    $form['condition_group'] = array(
      '#type' => 'select',
      '#title' => t('Condition Group'),
      '#description' => t('All conditions in a condition group must be satisfied in order to match.  At least one condition group must be satisfied for the discount to be applied.'),
      '#options' => drupal_map_assoc(range(1, 10)),
      '#default_value' => $condition->condition_group,
    );
    $form['comparison'] = array(
      '#type' => 'select',
      '#title' => t('Comparison Operator'),
      '#options' => _uc_discounts_get_comparisons($condition_info[$condition_property]['compare_type']),
      '#default_value' => $condition->op,
    );
    $form['item'] = $item_field;
    $form['item']['#default_value'] = $condition->item_id;
    $form['value'] = $value_field;
    $form['value']['#default_value'] = $condition->value;
    $form['submit'] = array(
      '#type' => 'submit',
      '#value' => t('Submit'),
    );
  }

  return $form;
}

/**
 * conditions form validation
 */
function uc_discounts_condition_form_validate($form_id, $form_values) {
  if ($form_values['step'] < 3) {
    return;
  }
  // check if operator is valid for this property
  $property = $form_values['property'];
  $op = $form_values['comparison'];
  $condition_info = _uc_discounts_get_conditions();

  $valid_compare_types = _uc_discounts_get_comparisons($condition_info[$property]['compare_type']);
  if (!in_array($op, $valid_compare_types)) {
    form_set_error('comparison', t('@property may not have a comparison operator of @op', array('@property' => $property, '@op' => $op)));
  }

  // check value based on compare_type
  switch ($condition_info[$property]['compare_type']) {
    case 'integer':
      // this will also catch negative integers, but that's okay here
      if (!ctype_digit($form_values['values'])) {
        form_set_error('value', t('Value must be an integer'));
      }
      break;
    case 'numeric':
      if (!is_numeric($form_values['values'])) {
        form_set_error('value', t('Value must be a number'));
      }
      break;
    case 'money':
      $pattern = '/^\d*(\.\d{2})?$/';
      if (!is_numeric($form_values['value']) || !preg_match($pattern, $form_values['value'])) {
        form_set_error('value', t('Value must be a valid monetary number, with no commas'));
      }
      break;
    // no validation for text values
  }

}

/**
 * conditions form submission
 */
function uc_discounts_condition_form_submit($form_id, $form_values) {
  if ($form_values['step'] < 3) {
    return;
  }

  if ($form_values['condition_id'] == 0) {
    $id = db_next_id('{uc_discounts_conditions}_id');
    $sql = "INSERT INTO {uc_discounts_conditions}
      (discount_id, condition_group, property, op, item_id, value, weight, id)
      VALUES (%d, %d, '%s', '%s', %d, '%s', %d, %d)";
  }
  else {
    $id = $form_values['condition_id'];
    $sql = "UPDATE {uc_discounts_conditions} SET
      discount_id=%d, condition_group=%d, property='%s', op='%s',
      item_id=%d, value='%s', weight=%d WHERE id=%d";
  }

  db_query($sql,
    $form_values['discount_id'],
    $form_values['condition_group'],
    $form_values['property'],
    $form_values['comparison'],
    $form_values['item'],
    $form_values['value'],
    $form_values['weight'],
    $id
  );

  return 'admin/store/discounts/'.$form_values['discount_id'];
}

/**
 * Condition deletion form.
 *
 * @param $discount_id
 *   Must be a valid and existing discount
 * @param $condition_id
 *   Must be a valid condition which belongs to the specified discount
 */
function uc_discounts_condition_delete($discount_id, $condition_id) {
  // must be a valid condition, belonging to the specified discount
  $condition = uc_discounts_load_condition($condition_id);
  if (!$condition || $condition->discount_id != $discount_id) {
    drupal_goto('admin/store/discounts');
  }

  db_query("DELETE FROM {uc_discounts_conditions} WHERE id=%d", $condition_id);

  drupal_goto("admin/store/discounts/$discount_id");
}

/**
 * Format the information for the list of actions in a table.
 *
 * @param $discount_id
 * @return
 *   themed table
 */
function uc_discounts_actions_table($discount_id) {
  $action_list = uc_discounts_get_discount_actions($discount_id);
  $properties = _uc_discounts_get_action_form_values();
  $action_info = _uc_discounts_get_actions();

  $header = array(
    t('Property'),
    t('Weight'),
    t('Item'),
    t('Qty'),
    t('Amount'),
    array('data' => t('Actions'), 'colspan' => 2),
  );

  $rows = array();
  $base_url = "admin/store/discounts/$discount_id/actions/";
  foreach ($action_list as $action) {
    // get item name
    if (isset($action_info[$action->property]['item_name_callback'])) {
      $item_name_callback = $action_info[$action->property]['item_name_callback'];
      $item = $item_name_callback($action->item_id);
    }
    else {
      $item = $action->item_id;
    }

    // format amount
    if (isset($action_info[$action->property]['amount_callback'])) {
      $amount_callback = $action_info[$action->property]['amount_callback'];
      $amount = $amount_callback($action->amount);
    }
    else {
      $amount = $action->amount;
    }

    $rows[] = array(
      $properties[$action->property],
      $action->weight,
      $item,
      $action->qty == 0 ? '' : $action->qty,
      $amount,
      l(t('edit'), $base_url.$action->id),
      l(t('delete'), $base_url.$action->id.'/delete'),
    );
  }
  $table = theme('table', $header, $rows);
  $table .= l('Add Action', $base_url.'add');

  $element = array(
    '#title' => t('Actions'),
    '#collapsible' => TRUE,
    '#children' => $table,
  );

  return theme('fieldset', $element);
}

/**
 * Provides the discount action form
 *
 * Loads the specified action  id, and pre-populated the form based on this.
 * If this is for a new action, 0 is passed, getting default values.
 * Otherwise, this checks to make sure that the action belongs to the
 * specified discount.
 *
 * @param $discount_id
 * @param $action_id
 */
function uc_discounts_action_form($discount_id, $action_id, $form_values = NULL) {
  $action = uc_discounts_load_action($action_id);
  if (!$action) {
    drupal_goto('admin/store/discounts');
  }

  // make sure action belongs to the right discount
  if ($action_id > 0 && $action->discount_id != $discount_id) {
    drupal_goto('admin/store/discounts');
  }

  $discount = uc_discounts_load_discount($discount_id);
  $properties = _uc_discounts_get_action_form_values();

  // get action property if set
  if (!empty($action->property)) {
    $action_property = $action->property;
  }
  elseif (isset($form_values['property'])) {
    $action_property = $form_values['property'];
  }
  else {
    $action_property = '';
  }

  // make sure action property is valid
  $action_info = _uc_discounts_get_actions();
  if ($action_property && !isset($action_info[$action_property])) {
    $action_property = '';
  }

  // set page title
  if ($action_id == 0) {
    drupal_set_title(t('@name - Add Action', array('@name' => $discount->name)));
  }
  else {
    drupal_set_title(t('@name - Edit Action', array('@name' => $discount->name)));
  }

  // values for all stages
  $form['#multistep'] = TRUE;
  $form['action_id'] = array(
    '#type' => 'value',
    '#value' => $action->id,
  );
  $form['discount_id'] = array(
    '#type' => 'value',
    '#value' => $discount_id,
  );

  if ($action_property) {
    $step = 2;
  }
  else {
    $step = 1;
  }
  $form['step'] = array(
    '#type' => 'hidden',
    '#value' => $step + 1,
  );

  // stage 1 - select property
  if ($step == 1) {
    $form['#redirect'] = FALSE;
    $form['property'] = array(
      '#type' => 'select',
      '#title' => t('Property'),
      '#options' => $properties,
    );
    $form['submit'] = array(
      '#type' => 'submit',
      '#value' => t('Next'),
    );
  }

  // stage 2 - everything else
  else {
    // get item field
    if (isset($action_info[$action_property]['item_field_callback'])) {
      $item_field_callback = $action_info[$action_property]['item_field_callback'];
      $item_field = $item_field_callback();
      $item_field['#default_value'] = $action->item_id;
    }
    else {
      $item_field = array(
        '#type' => 'value',
        '#value' => 0,
      );
    }

    // does this form have a qty field
    if (empty($action_info[$action_property]['has_qty_field'])) {
      $qty_field = array(
        '#type' => 'value',
        '#value' => 0,
      );
    }
    else {
      $qty_field = array(
        '#type' => 'textfield',
        '#title' => t('Qty'),
        '#maxlength' => 4,
        '#size' => 4,
        '#default_value' => $action->qty > 0 ? $action->qty : '',
      );
    }

    $form['property'] = array(
      '#type' => 'value',
      '#value' => $action_property,
    );
    $form['display_property'] = array(
      '#type' => 'item',
      '#title' => t('Property'),
      '#value' => $properties[$action_property],
    );
    $form['weight'] = array(
      '#type' => 'weight',
      '#title' => t('Weight'),
      '#default_value' => $action->weight,
    );
    $form['item_id'] = $item_field;
    $form['qty'] = $qty_field;
    $form['amount'] = array(
      '#type' => 'textfield',
      '#title' => t('Amount'),
      '#maxlength' => 10,
      '#size' => 10,
      '#required' => TRUE,
      '#description' => t('Enter either a dollar amount or a percentage'),
      '#default_value' => $action->amount,
    );
    $form['submit'] = array(
      '#type' => 'submit',
      '#value' => t('Submit'),
    );
  }

  return $form;
}

/**
 * action form submission
 */
function uc_discounts_action_form_submit($form_id, $form_values) {
  if ($form_values['step'] < 3) {
    return;
  }

  if ($form_values['action_id'] == 0) {
    $id = db_next_id('{uc_discounts_actions}_id');
    $sql = "INSERT INTO {uc_discounts_actions}
      (discount_id, property, item_id, qty, amount, weight, id)
      VALUES (%d, '%s', '%s', %d, '%s', %d, %d)";
  }
  else {
    $id = $form_values['action_id'];
    $sql = "UPDATE {uc_discounts_actions} SET
      discount_id=%d, property='%s', item_id='%s', qty=%d,
      amount='%s', weight=%d WHERE id=%d";
  }

  db_query($sql,
    $form_values['discount_id'],
    $form_values['property'],
    $form_values['item_id'],
    $form_values['qty'],
    $form_values['amount'],
    $form_values['weight'],
    $id
  );

  return 'admin/store/discounts/'.$form_values['discount_id'];
}

/**
 * Action deletion form.
 *
 * @param $discount_id
 *   Must be a valid and existing discount
 * @param $action_id
 *   Must be a valid action which belongs to the specified discount
 */
function uc_discounts_action_delete($discount_id, $action_id) {
  $action = uc_discounts_load_action($action_id);
  if (!$action || $action->discount_id != $discount_id) {
    drupal_goto('admin/store/discounts');
  }

  db_query('DELETE FROM {uc_discounts_actions} WHERE id=%d', $action_id);

  drupal_goto('admin/store/discounts/'.$discount_id);
}

/*******************************************************************************
 * Module and Helper Functions
 ******************************************************************************/

/**
 * Load a discount
 *
 * @param $discount_id
 * @return
 *   discount object.  If discount_id is 0, return the default values
 */
function uc_discounts_load_discount($discount_id) {
  if ($discount_id == 0) {
    // return object with default values
    $discount = new stdClass;
    $discount->id = 0;
    $discount->name = '';
    $discount->description = '';
    $discount->max_discounts = 0;
    $discount->do_end_processing = 0;
    $discount->is_exclusive = 0;
    $discount->weight = 0;
    $discount->start_time = time();
    $discount->end_time = time();
    $discount->is_active = 0;
  }
  else {
    $result = db_query('SELECT * FROM {uc_discounts} WHERE id=%d', $discount_id);
    $discount = db_fetch_object($result);
    if (!$discount) {
      drupal_goto('/admin/store/discounts');
    }
  }

  return $discount;
}

/**
 * Load a condition for an order discount
 *
 * @param $condition_id
 */
function uc_discounts_load_condition($condition_id) {
  if ($condition_id == 0) {
    // return object with default values
    $condition = new stdClass;
    $condition->id = 0;
    $condition->discount_id = 0;
    $condition->condition_group = 0;
    $condition->property = '';
    $condition->op = '';
    $condition->item_id = 0;
    $condition->value = '';
    $condition->weight = 0;
  }
  else {
    $result = db_query('SELECT * FROM {uc_discounts_conditions} WHERE id=%d', $condition_id);
    $condition = db_fetch_object($result);
    if (!$condition) {
      drupal_goto('admin/store/discounts');
    }
  }

  return $condition;
}

/**
 * Load an action for a discount
 *
 * @param $action_id
 */
function uc_discounts_load_action($action_id) {
  if ($action_id == 0) {
    // return object with default values
    $action = new stdClass;
    $action->id = 0;
    $action->discount_id = 0;
    $action->property = '';
    $action->item_id = 0;
    $action->qty = 0;
    $action->amount = '';
  }
  else {
    $result = db_query('SELECT * FROM {uc_discounts_actions} WHERE id=%d', $action_id);
    $action = db_fetch_object($result);
    if (!$action) {
      drupal_goto('admin/store/discounts');
    }
  }

  return $action;
}

/**
 * Fetch a list of discounts
 *
 * @param $get_all
 *   If false, only retrieve current active discounts, based on start and
 *   end time
 * @return
 *   array of discounts
 */
function uc_discounts_get_discounts($get_all = FALSE) {
  if ($get_all) {
    $result = db_query("SELECT * FROM {uc_discounts} ORDER BY weight");
  }
  else {
    $now = time();
    $result = db_query(
      "SELECT * FROM {uc_discounts} WHERE start_time <= %d AND end_time >= %d ORDER BY weight",
      $now, $now
    );
  }
  $discounts = array();
  while ($row = db_fetch_object($result)) {
    $discounts[$row->id] = $row;
  }
  return $discounts;
}

/**
 * Retrieve the conditions for a given discount.
 *
 * @param $discount_id
 * @return
 *   array of conditions
 */
function uc_discounts_get_discount_conditions($discount_id) {
  $sql = "SELECT * FROM {uc_discounts_conditions} WHERE discount_id=%d ";
  $sql .= "ORDER BY condition_group, weight";
  $result = db_query($sql, $discount_id);
  $conditions = array();
  while ($row = db_fetch_object($result)) {
    $conditions[$row->id] = $row;
  }
  return $conditions;
}

/**
 * Retrieve the actions for a given discount.
 *
 * @param $discount_id
 * @return
 *   array of actions
 */
function uc_discounts_get_discount_actions($discount_id) {
  $sql = "SELECT * FROM {uc_discounts_actions} WHERE discount_id=%d ";
  $sql .= "ORDER BY weight";
  $result = db_query($sql, $discount_id);
  $actions = array();
  while ($row = db_fetch_object($result)) {
    $actions[$row->id] = $row;
  }
  return $actions;
}

/**
 * Compare two values based on a compare type and comparison operator
 *
 * @param $compare_type
 *   the type of comparison, for checking if the $op is valid
 * @param $property
 *   the value to be checked
 * @param $op
 *   comparison operator
 * @param $value
 *   the target value for comparison
 */
function uc_discounts_compare($compare_type, $property, $op, $value) {
  switch ($compare_type) {
    case 'integer':
      $property = (int)$property;
      $value = (int)$value;
      break;
    case 'numeric':
      $property = (float)$property;
      $value = (float)$value;
      break;
    case 'money':
      $property = sprintf('%.2f', $property);
      $value = sprintf('%.2f', $value);
      break;
    case 'date':
      // for now, assume in correct Ymd format
      break;
    // no changes needed for text
    default:
      $compare_type = 'text';
  }

  // "not equal" is not valid for numeric comparisons
  $valid_compare_types = _uc_discounts_get_comparisons($compare_type);
  if (!in_array($op, $valid_compare_types)) {
    drupal_set_message("Illegal $compare_type comparison operator '$op' used", 'error');
    return FALSE;
  }

  switch ($op) {
    case '>':
      return ($property > $value);
    case '>=':
      return ($property >= $value);
    case '<':
      return ($property < $value);
    case '<=':
      return ($property <= $value);
    case '=':
      return ($property == $value);
    case '!=':
      return ($property != $value);
    default:
      drupal_set_message('Unknown comparison operator '.check_plain($op).' used', 'error');
      return FALSE;
  }
}

/**
 * Load all of the conditions provided by additional discount modules
 *
 * @return array
 */
function _uc_discounts_get_conditions() {
  static $conditions = NULL;
  if ($conditions === NULL) {
    $conditions = array();
    $condition_list = module_invoke_all('discounts_condition');
    foreach ($condition_list as $condition_info) {
      $property = $condition_info['property'];
      $conditions[$property] = $condition_info;
    }
  }
  return $conditions;
}

/**
 * Return an array of condition property names and descriptions for use as
 * options in a select form field.
 */
function _uc_discounts_get_condition_form_values() {
  $condition_list = _uc_discounts_get_conditions();
  $form_values = array();
  foreach ($condition_list as $condition) {
    $condition_name = $condition['property'];
    $form_values[$condition_name] = $condition['description'];
    asort($form_values);
  }
  return $form_values;
}

/**
 * Load all of the actions provided by additional discount modules
 *
 * @return array
 */
function _uc_discounts_get_actions() {
  static $actions = NULL;
  if ($actions === NULL) {
    $actions = array();
    $action_list = module_invoke_all('discounts_action');
    foreach ($action_list as $action_info) {
      $property = $action_info['property'];
      $actions[$property] = $action_info;
    }
  }
  return $actions;
}

/**
 * Return an array of action property names and descriptions for use as
 * options in a select form field.
 */
function _uc_discounts_get_action_form_values() {
  $action_list = _uc_discounts_get_actions();
  $form_values = array();
  foreach ($action_list as $action) {
    $action_name = $action['property'];
    $form_values[$action_name] = $action['description'];
    asort($form_values);
  }
  return $form_values;
}

/**
 * return a list of valid comparison operators for the compare type
 *
 * The generated array is suitable for use in a select form field
 */
function _uc_discounts_get_comparisons($compare_type) {
  switch ($compare_type) {
    case 'integer':
    case 'numeric':
    case 'money':
    case 'date':
      $valid_compare_types = array(
        '<' => '<',
        '<=' => '<=',
        '>' => '>',
        '>=' => '>=',
        '=' => '=',
      );
      break;
    // default type is text
    default:
      $valid_compare_types = array(
        '=' => '=',
        '!=' => '!=',
      );
  }

  return $valid_compare_types;
}


