/*
 * OsSync.h
 * ru - CP1251
 *
 *  Created on: 3 февр. 2017 г.
 *      Author: alexrayne <alexraynepe196@gmail.com>
  ------------------------------------------------------------------------
    Copyright (c) alexrayne

   All rights reserved.
   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions are met:
   - Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.
   - Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in the
     documentation and/or other materials provided with the distribution.
   - Neither the name of ARM nor the names of its contributors may be used
     to endorse or promote products derived from this software without
     specific prior written permission.
   *
   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE
   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   POSSIBILITY OF SUCH DAMAGE. *
 * ------------------------------------------------------------------------
 *   Здесь положу порты-обертки примитивов синхронизации задач ОСи.
 */

#ifndef OSSYNC_H_
#define OSSYNC_H_

#include <stdint.h>
#include <stdbool.h>

#include "compiler-port.h"
#include <FreeRTOS.h>
#include <queue.h>
#include <task.h>



//* freeRTOS  не дает работать с прямыми структурами синхрогнизаторов, а дает их хэндлы
//typedef mutex_t mutex_p;

typedef xQueueHandle mutex_h;
typedef BaseType_t   OSError;

#define TO_INFINITE portMAX_DELAY
#define OSTicksMS(ms) ( ms / portTICK_PERIOD_MS)

#define EnterCS(x)  taskENTER_CRITICAL()
#define ExitCS(x)   taskEXIT_CRITICAL()
#define EnterCS_ISR(x)  taskENTER_CRITICAL_FROM_ISR()
#define ExitCS_ISR(x)   taskEXIT_CRITICAL_FROM_ISR(x)



#ifdef __cplusplus
// this guards atomic code block execution
class CSGuard {
    public:
        CSGuard(){EnterCS();};
        ~CSGuard(){ExitCS();};
};
#endif

#ifdef __cplusplus
 extern "C" {
#endif /* __cplusplus */


INLINE
bool MutexLock(mutex_h m, unsigned to_ticks){
  if (m != 0)
    return xQueueTakeMutexRecursive(m, to_ticks) == pdPASS;
  else
    return true;
}

INLINE
bool MutexUnlock(mutex_h m){
  if (m)
    return xQueueGiveMutexRecursive(m) == pdPASS;
  else
    return true;
}

INLINE
mutex_h MutexCreate(void){
    return xQueueCreateMutex(0);
}

INLINE
void MutexFree(mutex_h m){
  if (m)
    vQueueDelete(m);
}



//**************   semaphore    *****************************
//* uOS kernel bindings. used by intfaces/mem.h
typedef mutex_h mutex_t;

INLINE
bool mutex_lock(mutex_t* m){
    return MutexLock(*m, TO_INFINITE);
}

INLINE
bool mutex_unlock(mutex_t* m){
    return MutexUnlock(*m);
}

#define memsetw(d,s,z) memset(d,s,z)


//**************   semaphore    *****************************
#include  <semphr.h>
typedef SemaphoreHandle_t semaphore_t;

INLINE
semaphore_t SemBinCreate(){
  return xSemaphoreCreateBinary();
}

INLINE
semaphore_t SemCreate(unsigned max, unsigned start){
  return xSemaphoreCreateCounting(max, start);
}

INLINE
void SemFree(semaphore_t s){
  vSemaphoreDelete(s);
}

INLINE
bool SemTake(semaphore_t s){
  return (xSemaphoreTake( s, portMAX_DELAY ) == pdTRUE);
}

INLINE
bool SemGive(semaphore_t s){
  return (xSemaphoreGive( s ) == pdTRUE);
}

INLINE
bool SemTryTake(semaphore_t s, TickType_t to){
  return (xSemaphoreTake( s, to ) == pdTRUE);
}

INLINE
bool SemTake_isr(semaphore_t s){
  static BaseType_t affects; //static make code a bit simpler
  return (xSemaphoreTakeFromISR( s, &affects ) == pdTRUE);
}

INLINE
bool SemGive_isr(semaphore_t s){
  static BaseType_t affects; //static make code a bit simpler
  return (xSemaphoreGiveFromISR( s, &affects ) == pdTRUE);
}



//*********************   events / signals    *****************************
#include <event_groups.h>
typedef EventGroupHandle_t  signal_h;



INLINE
void sleep_ms(const unsigned ms){
  vTaskDelay(ms / portTICK_PERIOD_MS);
}

INLINE
void sleep(const unsigned ticks){
  vTaskDelay(ticks);
}

INLINE
TaskHandle_t task_current(void){
  return xTaskGetCurrentTaskHandle();
}

INLINE
TaskHandle_t task_idle(void){
    return xTaskGetIdleTaskHandle();
}

INLINE
void task_yield(void){
  taskYIELD();
}

INLINE
void task_delete(){
    vTaskDelete(task_current());
}

INLINE
const char* task_name(TaskHandle_t t){
    return pcTaskGetTaskName(t);
}

#ifdef __cplusplus
 }

#ifndef SYNC_STRICT
#define SYNC_STRICT 0
#endif
#if SYNC_STRICT
#include <cassert>
#define sync_assert(x)  assert(x)
#else
#define sync_assert(x)
#endif

class Mutex {
    public:
        mutex_h m;
        Mutex():m(NULL){};
        ~Mutex(){
            if (m)
                destroy();
        }
        bool init(){
            sync_assert(this != NULL);
            m = MutexCreate();
            return (m != NULL);
        };
        void destroy(){
            sync_assert(this != NULL);
            MutexFree(m);
            m = NULL;
        };

        bool lock() {
            sync_assert(this != NULL);
            return MutexLock(m, portMAX_DELAY);
        };
        bool lock(unsigned TOticks) {
            sync_assert(this != NULL);
            return MutexLock(m, TOticks);
        };
        void    unlock(){
            sync_assert(this != NULL);
            MutexUnlock(m);
        };
};

class Semaphore{
public:
    semaphore_t s;
    Semaphore():s(NULL){};
    ~Semaphore(){
      if (s)
        destroy();
    };

    bool init(unsigned max, unsigned starts = 0){
      s = SemCreate(max, starts);
      return (s != NULL);
    };

    void destroy() { if(s) {SemFree(s); s = NULL;} };

    bool take(){return SemTake(s);};
    bool take(TickType_t to){return SemTryTake(s, to);};
    bool give(){return SemGive(s);};
    bool take_isr(){return SemTake_isr(s);};
    bool give_isr(){return SemGive_isr(s);};

};

class BinSemaphore: public Semaphore
{
public:
    bool init(){
      s = SemBinCreate();
      return (s != NULL);
    };
};

class Signal {
    public:
        typedef signal_h    handle_t;
        typedef EventBits_t signal_bits;
        handle_t    s;

        Signal():s(NULL){};
        ~Signal(){
            if(s)
                destroy();
        };

        bool init(){
            s = xEventGroupCreate();
            return (s != NULL);
        };
        void destroy(){
            if (s) {
                vEventGroupDelete(s);
                s = NULL;
            }
        };

        signal_bits value(){
            if(s)
                return xEventGroupGetBits(s);
            else
                return 0;
        };

        signal_bits set(const signal_bits x){
            if(s)
                return xEventGroupSetBits(s, x);
            else
                return 0;
        };
        signal_bits give(const signal_bits x){return set(x);};

        signal_bits set_isr(const signal_bits x){
            if(s)
                return xEventGroupSetBitsFromISR(s, x, NULL);
            else
                return 0;
        };
        signal_bits give_isr(const signal_bits x){return set_isr(x);};

        signal_bits clear(const signal_bits x){
            if(s)
                return xEventGroupClearBits(s, x);
            else
                return 0;
        };

        signal_bits clear_isr(const signal_bits x){
            if(s)
                return xEventGroupClearBitsFromISR(s, x);
            else
                return 0;
        };

        static const unsigned toInfinite = portMAX_DELAY;
        //* wait for any of x is set
        signal_bits wait(const signal_bits x, unsigned TOticks = toInfinite){
            if(s)
                return xEventGroupWaitBits(s, x, pdFALSE, pdFALSE, TOticks);
            else
                return noinit_wait(TOticks);
        };

        //* wait for all x is set
        signal_bits wait_all(const signal_bits x, unsigned TOticks = toInfinite){
            if(s)
                return xEventGroupWaitBits(s, x, pdFALSE, pdTRUE, TOticks);
            else
                return noinit_wait(TOticks);
        };

        //* wait for any of x is set, and clear ones
        signal_bits take(const signal_bits x, unsigned TOticks = toInfinite){
            if(s)
                return xEventGroupWaitBits(s, x, pdTRUE, pdFALSE, TOticks);
            else
                return noinit_wait(TOticks);
        };

        //* wait for any of x is set
        signal_bits take_all(const signal_bits x, unsigned TOticks = toInfinite){
            if(s)
                return xEventGroupWaitBits(s, x, pdTRUE, pdTRUE, TOticks);
            else
                return noinit_wait(TOticks);
        };

        //* wait for any of x is set
        signal_bits signal_take_all(const signal_bits post, const signal_bits waitfor, unsigned TOticks = toInfinite){
            if(s)
                return xEventGroupSync(s, post, waitfor, TOticks);
            else
                return noinit_wait(TOticks);
        };
    protected:
        static
        signal_bits noinit_wait(unsigned TOticks = toInfinite) {
            vTaskDelay(TOticks);
            return 0;
        }
};


void SuspendTask(TaskHandle_t& t);

// this is not a mutex lock, but just an control point. it checks that
//      access ti point is suitable as mutex
class ExclusiveCheck {
    public:
        int             level;
        TaskHandle_t    t;

        ExclusiveCheck():level(0), t(NULL){};
        ~ExclusiveCheck(){
            if (level!= 0)
                destroy();
        }
        bool init(){
            sync_assert(this != NULL);
            level = 0;
            t = NULL;
            return true;
        };
        void destroy(){
            sync_assert(this != NULL);
            //if (level == 0);
        };

        bool lock() {
            sync_assert(this != NULL);
            bool granted = true;
            unsigned savei = EnterCS_ISR();
            if (level <= 0){
                level = 0;
                t = task_current();
            }
            else if (t == task_current()){
                level++;
            }
            else{
                granted = false;
                on_violation();
            }
            ExitCS_ISR(savei);
            return granted;
        };
        bool lock(unsigned TOticks) {
            return lock();
        };
        void    unlock(){
            sync_assert(this != NULL);
            unsigned savei = EnterCS_ISR();
            level--;
            if (level <= 0)
                t = NULL;
            ExitCS_ISR(savei);
        };

        void on_violation();
            // TODO: here is need some action reports violation;
};

#endif /* __cplusplus */


#endif /* OSSYNC_H_ */
