<?php
/**
 * Implementation of hook_help().
 */
function calendar_help($section) {
  switch ($section) {
    case 'admin/modules#description':
      return t('Allows users to submit stories, articles or similar content.');
  }
}

/**
 * Implementation of hook_node_info().
 */
function calendar_node_info() {
  return array('calendar' => array(
    'name' => t('calendar event'), 
    'base' => 'calendar')
  );
}

/**
 * Implementation of hook_perm().
 */
function calendar_perm() {
  return array('create calendar events', 'edit own calendar events');
}

/**
 * Implementation of hook_access().
 */
function calendar_access($op, $node) {
  global $user;

  if ($op == 'create') {
    return user_access('create calendar events');
  }

  if ($op == 'update' || $op == 'delete') {
    if (user_access('edit own calendar events') && ($user->uid == $node->uid)) {
      return TRUE;
    }
  }
}

/**
 * Implementation of hook_menu().
 */
function calendar_menu($may_cache) {
  $items = array();

  if ($may_cache) {
    $items[] = array('path' => 'node/add/calendar', 'title' => t('calendar event'),
      'access' => user_access('create calendar events'));
    $items[] = array(
      'path' => 'calendar',
      'title' => t('Calendar'),
      'access' => user_access('access content'),
      'callback' => 'calendar_page',
    );
    $items[] = array(
      'path' => 'calendar/month',
      'title' => t('Month View'),
      'access' => user_access('access content'),
      'callback' => 'calendar_month_view',
    );
  }

  if(!$may_cache) {
    $stylesheet = drupal_get_path('theme', $GLOBALS['theme_key'] . '/calendar.css');
    if (! file_exists($stylesheet) ) {
      $stylesheet = drupal_get_path('module', 'calendar') . '/calendar.css';
    }
    theme_add_style($stylesheet);
  }

  return $items;
}

function calendar_block($op = 'list', $delta = 0, $edit = array()) {
  switch ($op) {
    case 'list' :
      $blocks[0]['info'] = t('Upcoming calendar events.');
      return $blocks;
      break;
    case 'view' :
      if (user_access('access content')) {
        switch ($delta) {
          case 0:
            $block['subject'] = t('Upcoming events');
            $block['content'] = calendar_block_upcoming();
            return $block;
        }
      }
      break;
    case 'configure' :
      switch ($delta) {
        case 0:
          $form['calendar']['calendar_block_amount'] = array(
            '#type' => 'textfield', 
            '#title' => t('Number of Events'), 
            '#description' => t('The number of events to show in the block'),
            '#default_value' => variable_get('calendar_block_amount', 3),             
          );            
          return $form;
      }
      break;
    case 'save':
      variable_set('calendar_block_amount', $edit['calendar_block_amount']);
      break;
  }
}

function calendar_block_upcoming(){
  $limit = variable_get('calendar_block_amount', 3);

  $result = calendar_select_nids(0,$limit);   
      
  while($row = db_fetch_object($result)){
    $node = node_load($row->nid);
    $output[] = theme('calendar_block_item',$node);
  }
  return ($output ? theme('item_list',$output) : t('No events to display'));
}

function theme_calendar_block_item(&$node){
  $date = format_date(strtotime($node->start_date), 'custom', 'M j');
  return '<h3>'.l($node->title, "node/$node->nid").'</h3>'
         . '<p class="date">'.$date.'</p>'
         . '<p class="teaser">'.$node->teaser .'</p>';
}  

/**
 * Implementation of hook_form().
 */
function calendar_form(&$node) {
  $form['title'] = array(
    '#type' => 'textfield', 
    '#title' => t('Title'), 
    '#required' => TRUE, 
    '#default_value' => $node->title, 
    '#weight' => -5
  );
  
  $form['#jscalendar_ifFormat'] = '%Y-%m-%d';
  $form['#jscalendar_showsTime'] = 'false';

  $form['start_date'] = array(
    '#type' => 'textfield',
    '#title' => t('Date'),
    '#default_value' => ($node->start_date ? $node->start_date : date('Y-m-d', time())),
    '#attributes' => array('class' => 'jscalendar'),
    '#required' => TRUE,
  );
  $form['time']['allday'] = array(
    '#type' => 'radios',
    '#title' => t('All Day Event'),
    '#default_value' => ($node->allday ? 'yes': 'no'),
    '#options' => array( 'yes'=>t('Yes'), 'no'=>t('No') ),
  );
  $form['time']['start'] = array(
    '#type' => 'time',
    '#default_value' => $node->start,
    '#title' => t('Start Time'),
  );    
  $form['time']['end'] = array(
    '#type' => 'time',
    '#default_value' => $node->end,
    '#title' => t('End Time'),
  );  
  $form['location'] = array(
    '#type' => 'textfield',
    '#default_value' => $node->location,
    '#title' => t('Location'),
  );  
      
  $form['body_filter']['body'] = array(
    '#type' => 'textarea', 
    '#title' => t('Body'), 
    '#default_value' => $node->body, 
    '#rows' => 20, 
    '#required' => TRUE
  );
  $form['body_filter']['format'] = filter_form($node->format);

  return $form;
}

function calendar_elements() {
  $type['time'] = array(
    '#input' => TRUE, 
    '#process' => array('expand_time' => array())
  );
  return $type;
}

/**
 * Format a time selection element.
 */
function theme_time($element) {
  $output = '<div class="container-inline">' . $element['#children'] . '</div>';
  return theme('form_element', $element['#title'], $output, $element['#description'], $element['#id'], $element['#required'], form_get_error($element));
}

/**
 * Roll out a single time element.
 */
function expand_time($element) {
  // Default to current date
  if (!isset($element['#value'])) {
    $element['#value'] = array('hour' => 12, 'minutes' => '00', 'ampm' => 'am');
  }elseif(!is_array($element['#value'])){
    $element['#value'] = array(
      'hour' => date('g',$element['#value']), 
      'minutes' => date('i',$element['#value']), 
      'ampm' => date('a',$element['#value']));
  }    

  $element['#tree'] = TRUE;

  $order = array('hour','minutes','ampm');
  // Determine the order of day, month, year in the site's chosen date format.
  // Output multi-selector for date
  foreach ($order as $type) {
    switch ($type) {
      case 'hour':
        $options = drupal_map_assoc(range(1, 12));
        break;
      case 'minutes':
        $options = drupal_map_assoc(array('00','15','30','45'));
        break;
      case 'ampm':
        $options = drupal_map_assoc(array('am','pm'));
        break;
    }
    $parents = $element['#parents'];
    $parents[] = $type;
    $element[$type] = array(
      '#type' => 'select',
      '#value' => $element['#value'][$type],
      '#attributes' => $element['#attributes'],
      '#options' => $options,
    );
  }

  return $element;
}

/**
 * Implementation of hook_validate().
 */
function calendar_validate(&$node) {
  //form_set_error('title','<pre>'.print_r($node,true).'</pre>');
}

/**
 * Implementation of hook_insert().
 */
function calendar_insert($node) {
  db_query("INSERT INTO {calendar} 
    (vid, nid, start_date, allday, start, end, location) 
    VALUES (%d, %d, '%s', %d, %d, %d, '%s' )", 
    $node->vid, $node->nid, $node->start_date, ($node->allday == 'yes' ? 1 : 0),
    _calendar_mktime($node->start), _calendar_mktime($node->end),
    $node->location);
}

/**
 * Implementation of hook_update().
 */
function calendar_update($node) {
  // if this is a new node or we're adding a new revision,
  if ($node->revision) {
    calendar_insert($node);
  }
  else {
    db_query("UPDATE {calendar} SET 
      start_date = '%s', allday = %d, start = %d, end = %d, location = '%s' 
      WHERE vid = %d", 
      $node->start_date, ($node->allday == 'yes' ? 1 : 0),
      _calendar_mktime($node->start), _calendar_mktime($node->end),
      $node->location, $node->vid);
  }
}

function _calendar_mktime($time){
  if($time['ampm'] != 'am' && $time['hour'] != 12){
    $time['hour'] += 12;
  }
  return mktime($time['hour'],$time['minutes']);
}

/**
 * Implementation of hook_nodeapi().
 *
 * When a node revision is deleted, we need to remove the corresponding record
 * from our table. The only way to handle revision deletion is by implementing
 * hook_nodeapi().
 */
function calendar_nodeapi(&$node, $op, $teaser, $page) {
  switch ($op) {
    case 'delete revision':
      // Notice that we're matching a single revision based on the node's vid.
      db_query('DELETE FROM {calendar} WHERE vid = %d', $node->vid);
      break;
  }
}

/**
 * Implementation of hook_delete().
 *
 * When a node is deleted, we need to remove all related records from out table.
 */
function calendar_delete($node) {
  // Notice that we're matching all revision, by using the node's nid.
  db_query('DELETE FROM {calendar} WHERE nid = %d', $node->nid);
}

/**
 * Implementation of hook_load().
 *
 * Now that we've defined how to manage the node data in the database, we
 * need to tell Drupal how to get the node back out. This hook is called
 * every time a node is loaded, and allows us to do some loading of our own.
 */
function calendar_load($node) {
  $additions = db_fetch_object(db_query(
    'SELECT start_date, allday, start, end, location FROM {calendar} WHERE vid = %d', $node->vid));
  return $additions;
}

/**
 * Implementation of hook_view().
 *
 * This is a typical implementation that simply runs the node text through
 * the output filters.
 */
function calendar_view(&$node, $teaser = FALSE, $page = FALSE) {
  $node = node_prepare($node, $teaser);
}

function theme_calendar_event_info(&$node){
  if($node->allday){
    $output = '<p class="calendar_info">'
            . '<span class="label">Date: </span>'
            . format_date($node->start_date,'custom','M j, Y').'<br />'
            . '<span class="label">Time: </span>(all day event)</span>'.'<br />'
            . '<span class="label">Location: </span>'.$node->location
            . '</p>';
  }else{
    $output = '<p class="calendar_info">'
            . '<span class="label">Date: </span>'
            . format_date($node->start_date,'custom','M j, Y').'<br />'
            . '<span class="label">Time: </span>' 
            . date('M j, Y',$node->start_date) . ' '
            . date('g:i a',$node->start) . ' - ' 
            . date('g:i a',$node->end).'<br />'
            . '<span class="label">Location: </span>'.$node->location
            . '</p>';
  }
  return $output;                
}

/**
 * Returns a db result set of nids starting after today, optionally
 * filtered by termid ($tid) and limited by $limit 
 */
function calendar_select_nids($tid=0,$limit=0){
  $sql = 'SELECT n.nid FROM {calendar} c INNER JOIN {node} n ON n.vid = c.vid ';
  $start = gmdate('Y-m-d');
  if($tid){
    $term = taxonomy_get_term($tid);
  }
  if($term && $term->vid == variable_get('calendar_vocabulary', '0')){
    $sql .= 'INNER JOIN {term_node} tn ON n.nid = tn.nid WHERE tn.tid = %d ';
    $sql .= 'AND c.start_date >= "%s" ORDER BY c.start_date, c.start, c.end';
    if($limit){
      $result = db_query_range($sql,$tid,$start,0,$limit);
    }else{
      $result = db_query($sql, $tid,$start);
    }
  }else{
    $sql .= 'WHERE c.start_date >= "%s" ORDER BY c.start_date, c.start, c.end';
    if($limit){
      $result = db_query_range($sql,$start,0,$limit);
    }else{
      $result = db_query($sql, $start);
    }
  }    
  return $result;
}      
  
function calendar_page($tid = false){
  if($tid){
    $term = taxonomy_get_term($tid);
    if($term && $term->vid == variable_get('calendar_vocabulary', '0')){
      drupal_set_title($term->name . ' Calendar');
    }
  }
  
  $result = calendar_select_nids($tid);   
      
  while($row = db_fetch_object($result)){
    $node = node_load($row->nid);
    $output .= node_view($node,true);
  }
  
  return ($output ? $output : t('No events to display'));
}

// Implementation of hook_settings()
function calendar_settings() {
  $vocs = taxonomy_get_vocabularies('calendar');
  foreach($vocs as $v){
    $options[$v->vid] = $v->name;
  }
  $form['calendar_vocabulary'] = array(
    '#type' => 'select', 
    '#title' => t('Calendar Vocabulary'), 
    '#description' => t('Select the vocabulary that contains the terms to use for different calendars'),
    '#default_value' => variable_get('calendar_vocabulary', '0'),
    '#options' => $options 
  );
  return $form;
}

function calendar_month_view(){
  $d = date('d');
  $m = date('m');
  $y = date('Y');
  $start = mktime(0,0,0,(int)$m+1, 1, $y);
  $start_day = date('w',$start);
  $month = date('F',$start);
  return theme('calendar_month', $month);
}

function theme_calendar_month($month,$offset=0,$view='month'){
  switch($view){
    case 'month':
      $output = <<<CAL
<table class="calendar-month">
  <caption>$month</caption>
  <thead>
    <tr>
      <th>Sun</th>
      <th>Mon</th>
      <th>Tue</th>
      <th>Wed</th>
      <th>Thu</th>
      <th>Fri</th>
      <th>Sat</th>
    </tr>
  </thead>
CAL;
      for($i=1; $i<=31; $i+=7){
        $output .= theme('calendar_week',$i,$offset,$view);
      }
      $output .= '<tfoot></tfoot></table>';
      break;
    default:
      break;
  }
  return $output;
}

function theme_calendar_week($start,$offset=0,$view='month'){
  switch($view){
    case 'month':
      for($i=$start-$offset; $i<$start-$offset+7; $i++){
        if($i <= 31 || $i <= 0){
          $output .= theme('calendar_day',$i,$view);
        }else{
          $output .= '<td></td>';
        }
      }
      $output = '<tr>'.$output.'</tr>';
      break;
    default:
      break;
  }
  return $output;
}

function theme_calendar_day($day,$view='month'){
  switch($view){
    case 'month':
      $output = '<td>'.$day.'</td>';
      break;
    default:
      break;
  }
  return $output;
}