#ifndef SCOOP_H
#define SCOOP_H

/*****************************************************************************/
/* SCOOP LIBRARY / AUTHOR FABRICE OUDERT / GNU GPL V3                        */
/* https://code.google.com/p/arduino-scoop-cooperative-scheduler-arm-avr/    */
/* VERSION 1.2  NEW YEAR PACK 10/01/2013                                     */
/* ENJOY AND USE AT YOUR OWN RISK  :)                                        */
/* SHOULD READ USER GUIDE FIRST (@!@)                                        */
/*****************************************************************************/

/******** PREPROCESSING CONDITIONS ********/

// SCoopTRACE enables using trace("x") function in the user program, or even tracing the scheduler behavior, with following values:
// 0=disable source code for trace function, disable "scoopdebug"
// 1=enable trace("x") functions in user sketch only. 
// 2=enable the library to print traces when running mySCoop.start() 
// 3=enable the library to print traces when starting SCoopEvent or derived
// 4=enable the library to print traces when starting tasks or timers 
#define  SCoopTRACE         1     

// SCOOPTIMEREPORT enable time control variables in SCoopTask and enables cycletime average calculation. accept following values:
// 4 -> 16 cycle average, 3->8 average cycles, 2 ->4 cycle average , 1 -> 2 cycles average, 
// 0 : NO TIME MEASUREMENT , NO VARIABLES yieldMicros,cycleMicros,maxCycleMicros,maxYieldMicros...
#define  SCoopTIMEREPORT    1        // default value =1 in order to prioritize performance for the user program.
                                     // overload to 4 in ARM section, if this definition was not 0, as we have more power in ARM

#define  SCoopYIELDCYCLE    1        // if set to 1, yield will automatically launch all tasks in the list 
                                     // without coming back to main loop (like mySCoop.cycle() (faster when more than 1 task)

#define SCoopInstanceNickName    mySCoop   // could be changed for "Sch" or "SC" or whatever you prefer
#define ArduinoSchedulerNickName Scheduler // for compatibility with Arduino DUE library

#define SCoopANDROIDMODE    1        // set to 1 if we want to have the possibility to use startLoop() 
                                     // set to 2 if we also want possibility to kill the task
									 
#define  SCoopOVERLOADYIELD 1        // set to 1 to provides a yield() global function which will overload standard arduino yield()

#if (ARDUINO < 103)
#warning "V1.2 TESTED ONLY ON 1.0.3 with PanSTamp, Arduino UNO, Teensy++2.0, Teensy2.0 and Teensy3.0" 
#endif

#if (ARDUINO >= 100)
#include <Arduino.h>
#else
#include <WProgram.h>                // not a valid approach for ARM
#endif

#if defined (__AVR__)
#define SCoop_AVR 1                  // inform the library that the code is made for AVR

#define SCDelay_t           int32_t  // type for all the virtual timer used in scoop library (period of timer, sleep function..) 
#define SCoopTimerCount_t   int32_t  // define the type of the counter used in SCoopTimer. can be changed to int32_t instead
                                     // these 2 variables could changed to int16_t without any issue !

#define SCoopDefaultQuantum   400    // recomended before switching to next task. this provide a 5% overhead time used by scheduler, for 3 tasks+loop
#define SCoopDefaultStackSize 150    // to be experimented by user. seems enough for a task with couple of variable and a call to serial.print
#define AndroidSchedulerDefaultStack SCoopDefaultStackSize

#define micros_t     int16_t         // used for low level time handling. MUST not be changed to int32 
#define ptrInt       uint16_t        // used to typecast pointers to integer 
typedef uint8_t      SCoopStack_t;   // type definition for stack array of bytes

#elif defined(__MK20DX128__)  || defined (__SAM3X8E__)     // below code enables compiling on ARM / could be replace by #elif defined(__arm__)
#define SCoop_ARM 1                  // inform the lbrary that the code is made for ARM // not used yet

#define SCDelay_t           int32_t  // type for all the virtual timer used in scoop library (period of timer, sleep function..)
#define SCoopTimerCount_t   int32_t  // define the type of the counter used in SCoopTimer. can be changed to int32_t instead

#define SCoopDefaultQuantum   200;   // recomended before switching to next task. this provide a 1% overhead time used by scheduler, for 3 tasks+loop
#define SCoopDefaultStackSize 256    // must be a multiple of 8
#define AndroidSchedulerDefaultStack 1024 // a bit too much, just for backward compatibility reason

#define micros_t     int32_t         // all low level micros second computation will be done in 32 bit too. possibility to change to int16
#define ptrInt       uint32_t        // used to typecast pointers to integer
typedef uint64_t     SCoopStack_t   __attribute__ ((aligned (8)));

#else
#error "this library might not be compatible with this NON-AVR / ARM platform. Please experiment and report on Arduino.cc forum"
#endif

#define SCoopDelayMillis()  (SCDelay_t)millis()  // overloading and typecasting the standard millis()

// some macro for easy code writing, just to replace "Serial." ...
#define SCbegin(_X)    { Serial.begin(_X);while(!Serial); }
#define SCp(_X)        { Serial.print(_X); }
#define SCphex(_X)     { Serial.print(_X,HEX); }
#define SCpln(_X)      { Serial.println(_X); }
#define SCplnhex(_X)   { Serial.println(_X,HEX); }
#define SCkey()        { Serial.print(">?");while (!(Serial.available())) ; SCpln((uint8_t)Serial.read()); }
#define SCpkey1(_X)    { Serial.print("<");Serial.print(_X);SCkey(); }
#define SCpkey2(_X,_Y) { Serial.print("<");Serial.print(_X);Serial.print(":");Serial.print(_Y,HEX);SCkey(); }

#define ifSCoopTRACE(_X,_Y) if (SCoopTRACE > (_X)) { trace(_Y); }   // to make code more readable

/********* type defs  *******/

typedef void (*SCoopFunc_t)(void); // type definition for a pointer to a function

typedef volatile int8_t   vi8;     // hope everyone like it
typedef volatile int16_t  vi16;
typedef volatile int32_t  vi32;
typedef volatile uint8_t  vui8;
typedef volatile uint16_t vui16;
typedef volatile uint32_t vui32;
typedef volatile uint64_t vui64;   // yep you can also play with 64 bits variable on ARM platform without pain
typedef volatile int64_t  vi64;
typedef volatile boolean  vbool;

// definition of the various state of an object or task. this was prefered to "enum" for using 8 bits instead of 16bits on AVR ...
#define SCoopTERMINATED  0
#define SCoopCONSTRUCTED B00001    // object state, compatible with Java library
#define SCoopNEW         B00010    // context ready
#define SCoopRUNNABLE    B00100    // bit 2 means the task is runnable or running : setup() has been launched and context ready
#define SCoopRUNNING     B00101    // inside run() or loop()
#define SCoopWAITING     B00110    // inside a sleep method
#define SCoopPAUSED      B01000    // bit 3 means the task is paused
#define SCoopTRIGGER     B10000    // force object to be launched when calling launch()
#define SCoopKILLING    B100000    // force object to be killed by Scheduler (or paused if static)

#define SCoopEventType   1         // used to provide a statical type information to the object in the list (polymorph)
#define SCoopTaskType    2         // only used by mySCoop.start() in the library code , as virtual call were prefered elsewhere
#define SCoopTimerType   3         // not used so far
#define SCoopDynamicTask 4         // 

/********* Objects Prototypes *******/

class SCoopDelay;
class SCoopDelayus;
class SCoopEvent;
class SCoopTimer;
class SCoopTask;
class SCoop;


/********* GLOBAL VARIABLE *******/

extern SCoopEvent *  SCoopFirstItem;      // point on the latest registered item in the scheduler list
extern SCoopTask*    SCoopFirstTask;      // point on the latest registered task
extern uint8_t       SCoopNumberTask;     // the number of tasks registered (main loop() not counted)
extern void          sleep(SCDelay_t time); // (weak) in order to replace standard delay() for Arduino <150 not containing yield

extern SCoop SCoopInstanceNickName;       // one forced instance of the SCoop Scheduler
#if SCoopANDROIDMODE >= 1
extern SCoop& ArduinoSchedulerNickName;   // redundant declaration for compatibilit with the name of the Android/DUE "Scheduler"
#endif

#if SCoopOVERLOADYIELD == 1
extern void          yield(void);         // used to overload the Arduino yield "weak"
extern void          yield0(void);        // used to define our global yield(0)
#endif

#define SCoopClassOperatorEqual(name,type) name & operator=(const type rhs) { set(rhs); return *this; }; // magic statement tadah
 

/********* SCoopEVENT CLASS *******/

class SCoopEvent                      // represent an object in the SCoop list (task, event, msg...)
{ public: 
  
  SCoopEvent();                       // basic constructor to register the object in the list
  SCoopEvent(SCoopFunc_t func);       // possibility to pass the user function (instead of overloading run() )
    
  ~SCoopEvent();                      // destructor to remove item from the list
  
  void registerThis(uint8_t type)     // add this item to the list (top/first)
  __attribute__((noinline));          // well, too much code generated. better to call it
  
  void unregisterThis()               // remove the item from the list
  __attribute__((noinline));          // well, too much code generated. better to call it
  
  void init(SCoopFunc_t func);        // init the object (an extension of constructor actions). 
                                      // Set state to NEW if parameter not NULL
  
#if SCoopTRACE > 0                    // if we want to trace whats hapen during start&launch
  void traceThis();                   // specifically display the "this" pointer value, and the SP stack 
  void trace(char * xx);              // display "this , SP" and the xx string
#else
#define traceThis() ;                 // no code generated then
#define trace(x)    ;
#endif

  virtual void setup() { };           // can be overloaded by derived object. called by start()
    
  virtual void run()                  // should be overloaded if used in a derived object. other wise call the user function if defined.
  { if (userFunc) { userFunc(); } };  // called by launch(). 
   
  virtual void start();               // used to init the user object by launching its setup(). called by mySCoop.start() only, if object in state NEW
  virtual bool launch() ;             // launch or switch into this item or its derived if not paused. called by mySCoop.yield() only
  
  virtual void pause();               // put the object in a state where it will not be launched again until resumed
  virtual bool paused();              // return the paused status as a boolean
  virtual void resume();              // clear the flag and enable the task to run again
  
  void set()   { set(true); }         // force event to be launched by futur yield()
  bool set(bool val)                  // same but possibility to pass an expression
  { if (val) { state |= SCoopTRIGGER; }; return val;  }
  
  SCoopClassOperatorEqual(SCoopEvent,bool) // overload operator assignement to make things event simpler
                                       
  uint8_t getState()                  // for compatibility with Java Thread library ...
  { return state; };                  // basically return the object state. see definition section for potential values
  
  bool    isAlive()                   // means object is in the list and has been init() successfully
  { return ((state >= SCoopNEW)); }   // may be the object is not started yet. for compatibility with Java Thread library ...

  SCoopEvent *  pNext;                // point to the next object registered in the list
  uint8_t       itemType;             // place holder for recognizing item type, as we use polymorphism... 
  vui8          state;                // status of the object. see definition section fro potential values.
                                      
protected:

  SCoopFunc_t   userFunc;            // pointer to the user function to call

private:                             // nothing private
                                     // Total object variables = 6 bytes on AVR or 10 on ARM, per object instance
};                                   // end of class SCoopEvent. 
    

/********* FACILITATE EVENT DEFINITION *******/

// this creates a derived object inheriting from SCoopEvent, 
#define defineEventBegin(myevent) \
class myevent : public SCoopEvent \
{public: myevent () : SCoopEvent() { state = SCoopNEW; }; \
SCoopClassOperatorEqual(myevent,bool)

#define defineEventEnd(myevent) }; myevent myevent ;

#define defineEvent(myevent) defineEventBegin(myevent) void setup();void run(); defineEventEnd(myevent)
// user must define the run and the setup method in the myevent scope with:
// void myevent :: setup() { ... }
// void myevent :: run()   { ... }	

// Same but the user just has to put the bloc code { } for a single run method
#define defineEventRun(myevent) defineEvent(myevent) void myevent :: setup() { }; void myevent :: run()
// user must write the bloc code for run directly after this macro : 
// defineEventRun(myEvent1) { ... }

	
/********* SCOOPDELAY CLASS *******/           // a basic virtual timer solution

class SCoopDelay                               // sort of timerDown... used in SCoopTimer and SCoopTask and sleep
{ public:
  SCoopDelay();                                // basic constructor. set time to 0 . doesnt touch reload variable;
  
  SCoopDelay(SCDelay_t reload);                // possibility to define reload value, otherwse linker should remove the corresponding code avd variable
  
  SCDelay_t setReload(SCDelay_t reload);       // define the reload period for this object
  SCDelay_t getReload();                       // return the period variable
  void     initReload();                       // load the timer with its reload value
  void     reload();                           // add the reload time to the timer
  bool     reloaded();                         // return true (only once) each time when "reload" is spent;
  
  void     reset();                            // reset timer
  
  SCDelay_t set(SCDelay_t time)                // set the time value . return time value . timer will start counting down
  __attribute__((noinline));                   // we prefer a call to this method as it will take time anyway
  
  SCDelay_t get()                              // return the value corresponding to the remaining time. return 0 if negative
  __attribute__((noinline));
  
  SCDelay_t add(SCDelay_t time);               // add amount of time to timer, keep timer synchronized with millis.
  
  SCDelay_t sub(SCDelay_t time);
  
  bool     elapsed();                          // return true if timer has reached 0. doesnt reload -> use reloaded instead.
  
  operator SCDelay_t() { return get(); }       // SCoopDelay can be used in an interger expression
  

  SCoopClassOperatorEqual(SCoopDelay,SCDelay_t)  // another magic statement 
  
  SCoopDelay & operator=(const SCoopDelay & rhs) // overload operator assignement 
  { timeValue=rhs.timeValue; return *this; }
  
  SCoopDelay & operator+=(const SCDelay_t rhs)   // overload operator += to make things event simpler
  { add(rhs); return *this;}                   

  SCoopDelay & operator-=(const SCDelay_t rhs)   // overload operator -= to make things event simpler
  { sub(rhs); return *this;}
  
  SCDelay_t timeValue;                         // the realtime value of the timer
private:
  SCDelay_t reloadValue;                       // store the period for further reload.
                                               // might be removed by linker, if object instance doesnt use reload function or constructor
};

/********* SCOOPDELAYUS CLASS *******/           // a basic virtual timer solution

class SCoopDelayus                               // sort of timerDown... used in SCoopTimer and SCoopTask and sleep
{ public:
  SCoopDelayus();                                // basic constructor. set time to 0 . doesnt touch reload variable;
  
  SCoopDelayus(micros_t reload);                // possibility to define reload value, otherwse linker should remove the corresponding code avd variable
  
  micros_t setReload(micros_t reload);       // define the reload period for this object
  micros_t getReload();                       // return the period variable
  void     initReload();                       // load the timer with its reload value
  void     reload();                           // add the reload time to the timer
  bool     reloaded();                         // return true (only once) each time when "reload" is spent;
  
  void     reset();                            // reset timer
  
  micros_t set(micros_t time)                // set the time value . return time value . timer will start counting down
  __attribute__((noinline));                   // we prefer a call to this method as it will take time anyway
  
  micros_t get()                              // return the value corresponding to the remaining time. return 0 if negative
  __attribute__((noinline));
  
  micros_t add(micros_t time);               // add amount of time to timer, keep timer synchronized with millis.
  
  micros_t sub(micros_t time);
  
  bool     elapsed();                          // return true if timer has reached 0. doesnt reload -> use reloaded instead.
  
  operator micros_t() { return get(); }       // SCoopDelay can be used in an interger expression
  

  SCoopClassOperatorEqual(SCoopDelayus,micros_t)  // another magic statement 
  
  SCoopDelayus & operator=(const SCoopDelay & rhs) // overload operator assignement 
  { timeValue=rhs.timeValue; return *this; }
  
  SCoopDelayus & operator+=(const micros_t rhs)   // overload operator += to make things event simpler
  { add(rhs); return *this;}                   

  SCoopDelayus & operator-=(const micros_t rhs)   // overload operator -= to make things event simpler
  { sub(rhs); return *this;}
  
private:
  micros_t timeValue;                         // the realtime value of the timer
  micros_t reloadValue;                       // store the period for further reload.
                                               // might be removed by linker, if object instance doesnt use reload function or constructor
};

	
/********* SCoopTIMER CLASS *******/

class SCoopTimer : public SCoopEvent
{ public:
  SCoopTimer();                                // constructor
  SCoopTimer(SCDelay_t period);
  SCoopTimer(SCDelay_t period, SCoopFunc_t func);
  
  void  init(SCDelay_t period, SCoopFunc_t func); // user function only

  void setTimeToRun(SCDelay_t time);            // set the next launch time to happen in "time" ms
  SCDelay_t getTimeToRun();                     // return the value corresponding to the time when the timer will be launched
  
  void schedule(SCDelay_t time);                // plan the next launch (same as SetTimeToRun in fact, but force counter to -1
  void schedule(SCDelay_t time, SCoopTimerCount_t count); // same but with a limited number of occurences (count)

  virtual void start();                        // initialize timer and make it ready for launch
  virtual bool launch();                       // launch the run() if time ellapsed and not paused

  operator SCDelay_t(){ return getTimeToRun(); }
                                               // all other virtual methods are inherited from Event, included run()
private:
  void initBasic();
  SCoopDelay timer;                            // virtual timer used for identifting when Timer object should be launched
  SCoopTimerCount_t counter;                   // by defaut = -1. if >0 then represent the max number of futur occurences
                                               // ptrInt will force 16 bits for AVR (new in V1.2) and 32 for ARM
};


/******* MACRO FOR CREATING TIMER OBJECTS Easily ******/
// define an object class inheriting from SCoopTimer
// user has to define the object run() and setup() method only

#define defineTimerBegin_Period(timer,period) \
class timer : public SCoopTimer          \
{public: timer () : SCoopTimer( period ) { state = SCoopNEW; }; \
operator SCDelay_t(){ return getTimeToRun(); };

#define defineTimerBegin_(timer) defineTimerBegin_Period(timer,0)

#define defineTimerBegin_X(x,A,B,FUNC, ...)  FUNC  // trick to create macro with optional arguments
#define defineTimerBegin(...)  defineTimerBegin_X(,##__VA_ARGS__, \
        defineTimerBegin_Period(__VA_ARGS__),\
        defineTimerBegin_(__VA_ARGS__)) 

#define defineTimerEnd(timer) } ; timer timer ;
		
		
#define defineTimer_Period(timer,period) defineTimerBegin_Period(timer,period) void setup();void run(); defineTimerEnd(timer)

#define defineTimer_(timer) defineTimer_Period(timer,0)

#define defineTimer_X(x,A,B,FUNC, ...)  FUNC  // trick to create macro with optional arguments
#define defineTimer(...)  defineTimer_X(,##__VA_ARGS__, \
        defineTimer_Period(__VA_ARGS__),\
        defineTimer_(__VA_ARGS__)) 

// quick definition of a timer run() with the bloc code corresponding to the run() { ... }

#define defineTimerRun_Period(timer,period) defineTimerBegin_Period(timer,period) void run(); defineTimerEnd(timer) void timer :: run()

#define defineTimerRun_(timer) defineTimerRun_Period(timer ,0)

#define defineTimerRun_X(x,A,B,FUNC, ...)  FUNC  // trick to create macro with optional arguments
#define defineTimerRun(...)  defineTimerRun_X(,##__VA_ARGS__, \
        defineTimerRun_Period(__VA_ARGS__),\
        defineTimerRun_(__VA_ARGS__)) 

		
/********* SCoopTASK CLASS *******/

class SCoopTask : public SCoopEvent
{public:

  SCoopTask();                               // basic constructor
  SCoopTask(SCoopStack_t* stack, ptrInt size);
  SCoopTask(SCoopStack_t* stack, ptrInt size, SCoopFunc_t func);

  ~SCoopTask();                              // just call terminate(). should be used only if a stack is made with malloc()

  void init(SCoopStack_t* stack, ptrInt size, SCoopFunc_t func);  // user function
  
#if SCoopTRACE > 0
  void trace(char * xx);
#else
#define trace(x)  ;
#endif

  virtual void loop()                        // this is the call to user function. should be overloaded by a derived objects
  { if (userFunc) { userFunc(); } }
  
  virtual void setup() { };                  // called after start. should be overriden by child objects

  void yield();                              // this method is specific to the task. either return to scheduler, or switch to next task
  void yield0() { yield(0); }                // same but switch imediately without checking time
  void yield(micros_t quantum);              // same but force to check if the time passed is spent
  
  void sleep(SCDelay_t ms);                  // will replace your usual arduino "delay" function
                                             
  void sleepSync(SCDelay_t ms);              // same as Sleep but delays are not absolute but relative to previous call to SleepSync 

  void sleepUntil(vbool& var);               // just wait for an external variable to become true. variable will then be flaged to false

  bool sleepUntil(vbool& var, SCDelay_t timeOut);  // same, with timeout. return true, if the var was set true
  
  ptrInt stackLeft();                        // remaining stack space in this task
#if SCoopANDROIDMODE >= 2
    void kill();                               // only works in conjunction with SCoop::startLoop for dynamic tasks
#endif  
  uint8_t *    pStack;                       // always point back and forth to the SP register for this task
  uint8_t *    pStackAddr;                   // keep a copy of the lowest stack adress. only used by stackleft()
  micros_t     quantumMicros;                // copy of the SCoopQuantum global definition, so the user can overload the value in setup()
  micros_t     prevMicros;                   // memorize the value of the micros() counter when entering the task. Works with quantumMicros
  
#if SCoopTIMEREPORT > 0                      // verifiy if we want to measure timing
  micros_t     yieldMicros;                  // time spent in the task during 1 complete scheduler cycle (average)
  micros_t     maxYieldMicros;               // maximum average amount of time spent in the task
#endif
  
protected:                                   // members below can be overidedn in a user object, if neded
  
  virtual void start();                      // initialize stack environement for calling run/loop. can be called by user if needed
  virtual bool launch() ;                    // launch the task from where it was stop, or just launch run/loop or user function the first time
  
  SCoopDelay   timer;                        // virtual timer used by Sleep functions

private:  // only internal methods used to optimize code size or readabilty
  
  void initBasic();                          // called by constructors. common code to each constructor variant
  void init(SCoopStack_t* stack, ptrInt size)// only called by constructor
  __attribute__((noinline));                 // optimize code instead of speed, as this is called only once...
  
  void sleepMs(SCDelay_t ms, bool sync);      // intermediate function called by sleep and sleepsync to optimize code size
  bool sleepUntilBool(vbool& var, bool checkTime);// intermediate function called by sleepUntil
  
  void  inline yieldInline(micros_t quantum)// potentially switch to pNext object, if time quantum given is reached
  __attribute__((always_inline));  
    
  void yieldSpent(micros_t spent)            // give control back to scheduler in order to switch to next task or come bacok in main loop()
  __attribute__((noinline));                 // speed optimization not that critical, as we already decided to switch
  
  void yieldSwitch()                         // just do it when you want to go to it
  __attribute__((noinline));
  
  inline void startFirstLoop()               // only used to simplify code reading. most likely the compiler will inline them
  __attribute__((always_inline));            // internal use only, to split cod into eementary function, facilitate inlining
  
  virtual void run() { }                     // not really used by us. putting it in private should avoid further overloading for derived object.
  __attribute__((used));                     // user will get an error message if trying to overload this method. loop() should be used!
  
                                             // total variable size = 12 on AVR and 22 on ARM if TIMEREPORT = 0
};                                           // total variable size = 16 on AVR and 30 on ARM if TIMEREPORT >=1

/******* MACRO FOR CREATING ALLIGNED STACK Easily ******/

// define a stack as a static array , taking care of stack allignement
#define defineStack(x,y) static SCoopStack_t x [ (  y + sizeof(SCoopStack_t) -1)/ sizeof(SCoopStack_t)];

/******* MACRO FOR CREATING TASK OBJECTS Easily ******/

// define a new object class inheriting from the SCoopTask object
#define defineTaskBegin_Size( mytask , stacksize ) \
defineStack( mytask##Stack , stacksize )      \
class mytask : public SCoopTask \
{ public: mytask ():SCoopTask(& mytask##Stack [0] , stacksize ) { state = SCoopNEW; };

#define defineTaskEnd(mytask) } ; mytask mytask ;

#define defineTask_Size( task , stacksize) defineTaskBegin_Size( task, stacksize) void setup(); void loop(); defineTaskEnd(task)
#define defineTask_( task ) defineTask_Size( task , SCoopDefaultStackSize )

// this is used to handle multiple optional parameters in macro ... see stackoverflow forum !
#define defineTask_X(x,A,B,FUNC, ...)  FUNC  
#define defineTask(...)  defineTask_X(,##__VA_ARGS__, \
        defineTask_Size(__VA_ARGS__),\
        defineTask_(__VA_ARGS__)) 


// define a new object class inheriting from the SCoopTask object
// predefine the prototype for loop and expect the user to complete with a bloc statement { ... }

#define defineTaskLoop_Size( task , stacksize ) defineTask_Size( task , stacksize ) void task :: setup() { }; void task :: loop()

#define defineTaskLoop_( task ) defineTaskLoop_Size( task , SCoopDefaultStackSize )

// this is used to handle multiple optional parameters in macro ... see stackoverflow forum !
#define defineTaskLoop_X(x,A,B,FUNC, ...)  FUNC  
#define defineTaskLoop(...)  defineTaskLoop_X(,##__VA_ARGS__, \
        defineTaskLoop_Size(__VA_ARGS__),\
        defineTaskLoop_(__VA_ARGS__)) 

	
/******* MAIN SCoop CLASS ******/

class SCoop                            // used only once for instanciating "mySCoop"
{ public:
  SCoop();                             // basic constructor

  void start(micros_t cycleTime);      // same as start but will compute a task quantum based on provided user expected cycle time. 
  void start(micros_t cycleTime, micros_t mainLoop);      // same as start but will compute a task quantum based on provided time. 
  void start();                        // start all registered objects in the list
  void cycle();                        // execute a complete cycle (all tasks , all timer, all event before returning)
#if SCoopANDROIDMODE >= 1
  SCoopTask* startLoop(SCoopFunc_t task, uint32_t stackSize = AndroidSchedulerDefaultStack); // dynamic task creation ... !
#endif  
  void yield();                        // can be called from where ever in order to Force the switch to next task
  void yield0();                       // can be called from where ever in order to Force the switch to next task
  void sleep(SCDelay_t time);          // quick implementation of a delay() type of function, in case the standard Arduino delay doesnt contain yield()
  void delay(uint32_t ms);             // same code as in Arduino 1.5
  
  uint8_t*    mainEnv;                 // used to store the main Stack register of the main loop()
  SCoopEvent* Current;                 // curent task in the yield cycle
  SCoopTask * Task;                    // task pointer
  vui8        Atomic;
  micros_t    startQuantum;            // initial value for each task time quantum. use default, otherwise calculated by start(x)
  micros_t    quantumMicros;           // initial value for the main loop time quantum. use default, otherwise calculated by start(x)
  micros_t    targetCycleMicros;       // this represent the target cycle time declared in the start(xxx), or the sum of all quantum
#if SCoopYIELDCYCLE == 0
  micros_t    quantumMicrosReal;       // this variable is same as quantum micros but divided by number of tasks
#endif
#if SCoopTIMEREPORT > 0                // verifiy if we want to measure timing
  micros_t     cycleMicros;            // total cycle time (average) for N cycle 
  micros_t     maxCycleMicros;         // maximum average amount of time spent in a full cycle
#endif
                                       // total variable size : 13 to 19 bytes on ARM, 25 to 37 bytes on ARM
};

 // possibility to use this excellent trick for declaring non-yield section with macro SCoopATOMIC { .. code ... } credits to Dean Camera!!!
#ifndef yieldATOMIC
void    inline __decAtomic(const  uint8_t *__s) { --SCoopInstanceNickName.Atomic; }
uint8_t inline __incAtomic(void)                { ++SCoopInstanceNickName.Atomic; return 1; }
#define SCoopATOMIC for ( uint8_t __temp __attribute__((__cleanup__(__decAtomic))) = __incAtomic(); __temp  ; __temp = 0 )
#define yieldATOMIC SCoopATOMIC
#else
#define SCoopATOMIC yieldATOMIC
#endif

#ifndef yieldPROTECT
void    inline __SCoopUnprotect(uint8_t* *__s)  { uint8_t* staticFlag = *__s; *staticFlag = 0; };
#define SCoopPROTECT() static uint8_t __SCoopProtect = 0; \
register uint8_t* __temp __attribute__((__cleanup__(__SCoopUnprotect)))=& __SCoopProtect; \
while (__SCoopProtect) yield0(); __SCoopProtect = 1; 
#define yieldPROTECT() SCoopPROTECT()
#else
#define SCoopPROTECT() yieldPROTECT()
#endif

#ifndef yieldUNPROTECT
#define SCoopUNPROTECT() { __SCoopProtect = 0; }
#define yieldUNPROTECT() SCoopUNPROTECT()
#else
#define SCoopUNPROTECT() yieldUNPROTECT()
#endif

// encapsulate the next block code within noInterrupt() and interrupts() // credits to Dean Camera
#ifndef ASM_ATOMIC
void    inline __SCoopInterrupts(const  uint8_t *__s) { interrupts(); }
uint8_t inline __SCoopNoInterrupts(void)              { noInterrupts(); return 1; }
#define ASM_ATOMIC for ( uint8_t __temp __attribute__((__cleanup__(__SCoopInterrupts))) = __SCoopNoInterrupts(); __temp  ; __temp = 0 )
#endif

/*************** SCoopFIFO CLASS ******************/

// easy way of handling tx rx buffers for bytes, int or long or any structure < 256 bytes

class SCoopFifo
{public:
  SCoopFifo(void * fifo, const uint8_t itemSize, const uint16_t itemNumber);
  
  uint16_t count();                   // return number of samples available in the buffer

  bool put(void* var);                // store one sample in the buffer. return true if ok, false if buffer is full

  bool putChar(const uint8_t value);
  bool putInt(const uint16_t value);
  bool putLong(const uint32_t value);

  bool get(void* var);               // provide the older item available in the buffer. return true if ok, false if the buffer is empty

  uint8_t  getChar();                // return the next value in the fifo, as an integer depending on the itemsize. it will wait until available!!!
  uint16_t getInt();                 // return the next value in the fifo, as an integer depending on the itemsize. it will wait until available!!!
  uint32_t getLong();                // return the next value in the fifo, as an integer depending on the itemsize. it will wait until available!!!
  
  uint16_t flush();                  // empty the fifo (disable and ENABLE interrupts)
  uint16_t flushNonAtomic();         // same without touching interrupt flags
  
  operator uint16_t() { return count(); }

private:

  void getYield(void* var);          // return an item and potentially wait until it is available. calls yield() in the meantime

  uint8_t* volatile ptrIn;
  uint8_t* volatile ptrOut;
  uint8_t  itemSize;
  uint8_t* ptrMin;
  uint8_t* ptrMax;
  };

/*************** MACRO TO CREATE FIFO BUFFER and INSTANCIATE OBJECT  ******************/

#define defineFifo( name , type , number ) \
type name##type##number [ number ]; \
SCoopFifo name ( name##type##number , sizeof( type ), number );

#endif


