// CrossWorks Tasking Library.
//
// Copyright (c) 2004 Rowley Associates Limited.
//
// This file may be distributed under the terms of the License Agreement
// provided with this software.
//
// THIS FILE IS PROVIDED AS IS WITH NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

#include "ctl_api.h"
#include "ctl_impl.h"

void 
ctl_events_init(CTL_EVENT_SET_t *e,
                CTL_EVENT_SET_t set)
{
  int enabled = ctl_global_interrupts_disable();
  *e = set;
  ctl_global_interrupts_set(enabled);
}

unsigned 
ctl_events_wait(CTL_EVENT_WAIT_TYPE_t type,
                CTL_EVENT_SET_t *eventSet,
                CTL_EVENT_SET_t events,
                CTL_TIMEOUT_t t, 
                CTL_TIME_t timeout)
{ 
  unsigned matched, waitNeeded;
  ctl_global_interrupts_disable();
  if (ctl_interrupt_count)
    ctl_handle_error(CTL_UNSUPPORTED_CALL_FROM_ISR);
  matched = (*eventSet) & events;
  if (type & CTL_STATE_EVENT_ANY_NOT_ALL)
    waitNeeded = (matched == 0);
  else
    waitNeeded = (matched != events);
  if (waitNeeded)
    {           
      switch (t)
        {
          case CTL_TIMEOUT_NONE:
            ctl_task_executing->timeout = 0;
            break;
          case CTL_TIMEOUT_ABSOLUTE:
            ctl_task_executing->timeout = timeout;
            type |= CTL_STATE_TIMER_WAIT;  
            break;
          case CTL_TIMEOUT_DELAY:
            ctl_task_executing->timeout = ctl_current_time + timeout;
            type |= CTL_STATE_TIMER_WAIT;  
            break;
        }     
      ctl_task_executing->v.wait_object = eventSet;
      ctl_task_executing->u.wait_events = events;
      ctl_task_executing->state = type;          
    }
  else
    {
      ctl_task_executing->v.timeout_occured = 0;  
      ctl_task_executing->u.wait_events = (*eventSet); // record pre auto clear value of eventSet
      if (type & CTL_STATE_EVENT_AUTO_CLEAR)
        (*eventSet) &= ~matched;
    }                
  ctl_private_reschedule();
  if (ctl_task_executing->v.timeout_occured)
    return 0;
  else
    return ctl_task_executing->u.wait_events;
}

void 
ctl_events_set_clear(CTL_EVENT_SET_t *eventSet,
                     CTL_EVENT_SET_t set,
                     CTL_EVENT_SET_t clear)
{
  CTL_TASK_t *t;
  int enabled = ctl_global_interrupts_disable();
  *eventSet = (*eventSet | set) & ~clear;
  for (t=ctl_task_list; t; t=t->next)
    if ((t->state & CTL_STATE_EVENT_WAIT) && (t->v.wait_object == eventSet))
      {
        unsigned waitNeeded;
        unsigned matched = (*eventSet) & t->u.wait_events;
        if (t->state & CTL_STATE_EVENT_ANY_NOT_ALL)
          waitNeeded = (matched == 0);
        else
          waitNeeded = (matched != t->u.wait_events);
        if (!waitNeeded)
          {
            t->v.timeout_occured = 0;
            t->u.wait_events = *eventSet; // record pre auto clear value of eventSet
            if (t->state & CTL_STATE_EVENT_AUTO_CLEAR)
              {
                (*eventSet) &= ~matched;
                t->state = CTL_STATE_RUNNABLE; 
                break; // don't need to check for any other tasks
              }
            else
              t->state = CTL_STATE_RUNNABLE;  
          }     
      }    
  if (ctl_interrupt_count==0)
    ctl_private_reschedule();
  else
    ctl_global_interrupts_set(enabled);
}
