/*
 * Copyright (C) 2018 dimercur
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "tasks.h"
#include <stdexcept>

// Déclaration des priorités des taches
#define PRIORITY_TSERVER 50 //Important but only once per connection so high priority it doesn't interfer with other tasks
#define PRIORITY_TOPENCOMROBOT 45 //Important but only once per connection so high priority it doesn't matter
#define PRIORITY_TMOVE 20 //Lower priority because periodicity is high it will interfer with other tasks
#define PRIORITY_TSENDTOMON 25 //Second highest priority of the periofic tasks
#define PRIORITY_TRECEIVEFROMMON 30 //Highest priority of the periodic tasks
#define PRIORITY_TSTARTROBOTWITHOUTWATCHDOG 40 //Important but only once per connection so high priority it doesn't interfer with other tasks 
#define PRIORITY_TSTARTROBOTWITHWATCHDOG 41 //Same as start without WD
#define PRIORITY_TCAMERA 21
#define PRIORITY_DETECTLOSTSUPROB 26 //Important so high priority compared to other tasks
#define PRIORITY_TVISION 26 

/*
 * Some remarks:
 * 1- This program is mostly a template. It shows you how to create tasks, semaphore
 *   message queues, mutex ... and how to use them
 * 
 * 2- semDumber is, as name say, useless. Its goal is only to show you how to use semaphore
 * 
 * 3- Data flow is probably not optimal
 * 
 * 4- Take into account that ComRobot::Write will block your task when serial buffer is full,
 *   time for internal buffer to flush
 * 
 * 5- Same behavior existe for ComMonitor::Write !
 * 
 * 6- When you want to write something in terminal, use cout and terminate with endl and flush
 * 
 * 7- Good luck !
 */

/**
 * @brief Initialisation des structures de l'application (tâches, mutex, 
 * semaphore, etc.)
 */
void Tasks::Init() {
    int status;
    int err;

    /**************************************************************************************/
    /* 	Mutex creation                                                                    */
    /**************************************************************************************/
    if (err = rt_mutex_create(&mutex_monitor, NULL)) {
        cerr << "Error mutex create: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }
    if (err = rt_mutex_create(&mutex_robot, NULL)) {
        cerr << "Error mutex create: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }
    if (err = rt_mutex_create(&mutex_robotStarted, NULL)) {
        cerr << "Error mutex create: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }
    if (err = rt_mutex_create(&mutex_move, NULL)) {
        cerr << "Error mutex create: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }
    if (err = rt_mutex_create(&mutex_killReceiveFromMon, NULL)) {
        cerr << "Error mutex create: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }
    if (err = rt_mutex_create(&mutex_killBattery, NULL)) {
        cerr << "Error mutex create: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }
    if (err = rt_mutex_create(&mutex_killVision, NULL)) {
        cerr << "Error mutex create: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }
    if (err = rt_mutex_create(&mutex_searchArena, NULL)) {
        cerr << "Error mutex create: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }
    if (err = rt_mutex_create(&mutex_drawArena, NULL)) {
        cerr << "Error mutex create: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }
    if (err = rt_mutex_create(&mutex_getPosition, NULL)) {
        cerr << "Error mutex create: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }
    if (err = rt_mutex_create(&mutex_killSendMon, NULL)) {
        cerr << "Error mutex create: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }    

    if (err = rt_mutex_create(&mutex_killDetectLostSupRob, NULL)) {
        cerr << "Error mutex create: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    } 
    
    if (err = rt_mutex_create(&mutex_acquireImage, NULL)) {
        cerr << "Error mutex create: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }     
  
    /*if (err = rt_mutex_create(&mutex_robot_on, NULL)) {
        cerr << "Error mutex create: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }*/
   

    cout << "Mutexes created successfully" << endl << flush;

    /**************************************************************************************/
    /* 	Semaphors creation       							  */
    /**************************************************************************************/
    if (err = rt_sem_create(&sem_barrier, NULL, 0, S_FIFO)) {
        cerr << "Error semaphore create: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }
    if (err = rt_sem_create(&sem_openComRobot, NULL, 0, S_FIFO)) {
        cerr << "Error semaphore create: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }
    if (err = rt_sem_create(&sem_serverOk, NULL, 0, S_FIFO)) {
        cerr << "Error semaphore create: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }
    if (err = rt_sem_create(&sem_startRobotWithoutWatchdog, NULL, 0, S_FIFO)) {
        cerr << "Error semaphore create: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }
    if (err = rt_sem_create(&sem_startRobotWithWatchdog, NULL, 0, S_FIFO)) {
        cerr << "Error semaphore create: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }
        
     if (err = rt_sem_create(&sem_detectLostSupRob, NULL, 0, S_FIFO)) {
        cerr << "Error semaphore create: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }

    if (err = rt_sem_create(&sem_askBattery, NULL, 0, S_FIFO)) {
        cerr << "Error semaphore create: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }
    
    if (err = rt_sem_create(&sem_startVision, NULL, 0, S_FIFO)) {
        cerr << "Error semaphore create: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }
    
    cout << "Semaphores created successfully" << endl << flush;

    /**************************************************************************************/
    /* Tasks creation                                                                     */
    /**************************************************************************************/
    if (err = rt_task_create(&th_server, "th_server", 0, PRIORITY_TSERVER, 0)) {
        cerr << "Error task create server: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }
    if (err = rt_task_create(&th_sendToMon, "th_sendToMon", 0, PRIORITY_TSENDTOMON, 0)) {
        cerr << "Error task create sendtoMon: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }
    if (err = rt_task_create(&th_receiveFromMon, "th_receiveFromMon", 0, PRIORITY_TRECEIVEFROMMON, 0)) {
        cerr << "Error task create receiveFromMon: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }
    if (err = rt_task_create(&th_openComRobot, "th_openComRobot", 0, PRIORITY_TOPENCOMROBOT, 0)) {
        cerr << "Error task create openComRobot: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }
    if (err = rt_task_create(&th_startRobotWithoutWatchdog, "th_startRobotWithoutWatchdog", 0, PRIORITY_TSTARTROBOTWITHOUTWATCHDOG, 0)) {
        cerr << "Error task create startRobotWithoutWatchdog: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }
    if (err = rt_task_create(&th_startRobotWithWatchdog, "th_startRobotWithWatchdog", 0, PRIORITY_TSTARTROBOTWITHWATCHDOG, 0)) {
        cerr << "Error task create startRobotWithWatchdog: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }
    if (err = rt_task_create(&th_move, "th_move", 0, PRIORITY_TMOVE, 0)) {
        cerr << "Error task create move: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }
    
    if (err = rt_task_create(&th_detectLostSupRob, "th_detectLostSupRob", 0, PRIORITY_DETECTLOSTSUPROB, 0)) {
        cerr << "Error task create detectLostSupRob: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }
    if (err = rt_task_create(&th_askBattery, "th_askBattery", 0, PRIORITY_TMOVE, 0)) {
        cerr << "Error task create: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }
   if (err = rt_task_create(&th_vision, "th_vision", 0, PRIORITY_TVISION, 0)) {
        cerr << "Error task create: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }
    cout << "Tasks created successfully" << endl << flush;

    /**************************************************************************************/
    /* Message queues creation                                                            */
    /**************************************************************************************/
    if ((err = rt_queue_create(&q_messageToMon, "q_messageToMon", sizeof (Message*)*50, Q_UNLIMITED, Q_FIFO)) < 0) {
        cerr << "Error msg queue create: " << strerror(-err) << endl << flush;
        exit(EXIT_FAILURE);
    }
    cout << "Queues created successfully" << endl << flush;

}

/**
 * @brief Démarrage des tâches
 */
void Tasks::Run() {

        rt_task_set_priority(NULL, T_LOPRIO);
        int err;

        if (err = rt_task_start(&th_server, (void(*)(void*)) & Tasks::ServerTask, this)) {
            cerr << "Error task start server: " << strerror(-err) << endl << flush;
            exit(EXIT_FAILURE);
        }
        if (err = rt_task_start(&th_sendToMon, (void(*)(void*)) & Tasks::SendToMonTask, this)) {
            cerr << "Error task start sendToMon: " << strerror(-err) << endl << flush;
            exit(EXIT_FAILURE);
        }
        if (err = rt_task_start(&th_receiveFromMon, (void(*)(void*)) & Tasks::ReceiveFromMonTask, this)) {
            cerr << "Error task start receiveFromMon: " << strerror(-err) << endl << flush;
            exit(EXIT_FAILURE);
        }
        if (err = rt_task_start(&th_openComRobot, (void(*)(void*)) & Tasks::OpenComRobot, this)) {
            cerr << "Error task start openComRobot: " << strerror(-err) << endl << flush;
            exit(EXIT_FAILURE);
        }
        if (err = rt_task_start(&th_startRobotWithoutWatchdog, (void(*)(void*)) & Tasks::StartRobotTaskWithoutWatchdog, this)) {
            cerr << "Error task start startRobotWithoutWatchdog: " << strerror(-err) << endl << flush;
            exit(EXIT_FAILURE);
        }
        if (err = rt_task_start(&th_startRobotWithWatchdog, (void(*)(void*)) & Tasks::StartRobotTaskWithWatchdog, this)) {
            cerr << "Error task start startRobotWithWatchdog: " << strerror(-err) << endl << flush;
            exit(EXIT_FAILURE);
        }
        if (err = rt_task_start(&th_move, (void(*)(void*)) & Tasks::MoveTask, this)) {
            cerr << "Error task start move: " << strerror(-err) << endl << flush;
            exit(EXIT_FAILURE);
        }

        if (err = rt_task_start(&th_detectLostSupRob, (void(*)(void*)) & Tasks::DetectLostSupRob, this)) {
            cerr << "Error task start detectLostSupRob: " << strerror(-err) << endl << flush;
            exit(EXIT_FAILURE);
        }    

        if (err = rt_task_start(&th_askBattery, (void(*)(void*)) & Tasks::AskBattery, this)) {
            cerr << "Error task start: " << strerror(-err) << endl << flush;
            exit(EXIT_FAILURE);
        }
        if (err = rt_task_start(&th_vision, (void(*)(void*)) & Tasks::Vision, this)) {
            cerr << "Error task start: " << strerror(-err) << endl << flush;
            exit(EXIT_FAILURE);
        }
        cout << "Tasks launched" << endl << flush;
}

/**
 * @brief Arrêt des tâches
 */
void Tasks::Stop() {
    monitor.Close();
    robot.Close();
}

/**
 */
void Tasks::Join() {
    cout << "Tasks synchronized" << endl << flush;
    rt_sem_broadcast(&sem_barrier);
    
    pause();
}

/**
 * @brief Thread handling server communication with the monitor.
 */
void Tasks::ServerTask(void *arg) {
    int status;
    
    cout << "Start " << __PRETTY_FUNCTION__ << endl << flush;
    
    while(1){ //Will be used to restart process 
        
        // Synchronization barrier (waiting that all tasks are started)
        rt_sem_p(&sem_barrier, TM_INFINITE);

        /**************************************************************************************/
        /* The task server starts here                                                        */
        /**************************************************************************************/
        rt_mutex_acquire(&mutex_monitor, TM_INFINITE);
        status = monitor.Open(SERVER_PORT);
        rt_mutex_release(&mutex_monitor);

        cout << "Open server on port " << (SERVER_PORT) << " (" << status << ")" << endl;

        if (status < 0) throw std::runtime_error {
            "Unable to start server on port " + std::to_string(SERVER_PORT)
        };
        monitor.AcceptClient(); // Wait the monitor client
        cout << "Rock'n'Roll baby, client accepted!" << endl << flush;
        rt_sem_broadcast(&sem_serverOk);
    }
}

/**
 * @brief Thread sending data to monitor.
 */
void Tasks::SendToMonTask(void* arg) {
    Message *msg;
    bool kill_sendToMonOk=0;
    
    cout << "Start " << __PRETTY_FUNCTION__ << endl << flush;
    
    while(1){
        // Synchronization barrier (waiting that all tasks are starting)
        rt_sem_p(&sem_barrier, TM_INFINITE);
    

        /**************************************************************************************/
        /* The task sendToMon starts here                                                     */
        /**************************************************************************************/
        rt_sem_p(&sem_serverOk, TM_INFINITE);
        
        //Initialize the loop condition
        kill_sendToMonOk=0;
        rt_mutex_acquire(&mutex_killSendMon, TM_INFINITE);
            killSendMon=0; // The message is deleted with the Write
        rt_mutex_release(&mutex_killSendMon);

        while (!kill_sendToMonOk) {
            cout << "wait msg to send" << endl << flush;
            msg = ReadInQueue(&q_messageToMon);
            cout << "Send msg to mon: " << msg->ToString() << endl << flush;
            rt_mutex_acquire(&mutex_monitor, TM_INFINITE);
            monitor.Write(msg); // The message is deleted with the Write
            rt_mutex_release(&mutex_monitor);

            rt_mutex_acquire(&mutex_killSendMon, TM_INFINITE);
            kill_sendToMonOk=killSendMon; // The message is deleted with the Write
            rt_mutex_release(&mutex_killSendMon);        

        }
    }
}

/**
 * @brief Thread receiving data from monitor.
 */
void Tasks::ReceiveFromMonTask(void *arg) {
    Message *msgRcv;
    bool killReceiveFromMonOk=0;
    int status;
    
    cout << "Start " << __PRETTY_FUNCTION__ << endl << flush;
        
    // Synchronization barrier (waiting that all tasks are starting)
    rt_sem_p(&sem_barrier, TM_INFINITE);
    
    while(1){ //Will be used to restart process

        //Reinitialize control boolean
        killReceiveFromMonOk = 0;
        rt_mutex_acquire(&mutex_killReceiveFromMon, TM_INFINITE);
            killReceiveFromMon = 0;
        rt_mutex_release(&mutex_killReceiveFromMon);

        /**************************************************************************************/
        /* The task receiveFromMon starts here                                                */
        /**************************************************************************************/
        rt_sem_p(&sem_serverOk, TM_INFINITE);
        cout << "Received message from monitor activated" << endl << flush;

        while (!killReceiveFromMonOk) {
            msgRcv = monitor.Read();
            cout << "Rcv <= " << msgRcv->ToString() << endl << flush;

            if (msgRcv->CompareID(MESSAGE_MONITOR_LOST)) {
                cout << "Connection to monitor lost" << endl;
                
                //Kill this task by exiting the main loop
                rt_mutex_acquire(&mutex_killReceiveFromMon, TM_INFINITE);
                    killReceiveFromMon=1;
                rt_mutex_release(&mutex_killReceiveFromMon);
                
                //Kill the vision taks by exiting the main loop
                rt_mutex_acquire(&mutex_killVision, TM_INFINITE);
                    killVision=1;
                rt_mutex_release(&mutex_killVision);
                
                //Kill the SendToMon taks by exiting the main loop
                rt_mutex_acquire(&mutex_killSendMon, TM_INFINITE);
                    killSendMon=1;
                rt_mutex_release(&mutex_killSendMon); 
                //Write fake message in queue to unblock Read function
                WriteInQueue(&q_messageToMon, new Message(MESSAGE_EMPTY));
                
                //Kill the different start robot tasks (with or without WD) by exiting their main loop
                rt_mutex_acquire(&mutex_killBattery, TM_INFINITE);
                    killBattery=1;
                rt_mutex_release(&mutex_killBattery);

                //Kill the DetectLostSupRon taks by exiting the main loop
                rt_mutex_acquire(&mutex_killDetectLostSupRob, TM_INFINITE);
                    killDetectLostSupRob=1;
                rt_mutex_release(&mutex_killDetectLostSupRob);

                //Update variable acquireImage to start or stop acquiring in Vision task
                rt_mutex_acquire(&mutex_acquireImage, TM_INFINITE);
                    acquireImage=0;
                rt_mutex_release(&mutex_acquireImage);
                
                //Block the move task
                rt_mutex_acquire(&mutex_robotStarted, TM_INFINITE);
                    robotStarted=0;
                rt_mutex_release(&mutex_robotStarted);

                //Wait every task to die
                sleep(1);
  
                rt_mutex_acquire(&mutex_robot, TM_INFINITE);
                    status=robot.Close();
                rt_mutex_release(&mutex_robot);
                
                if(status<0){
                    cout << "Close Robot Fail" << endl << flush;
                }
                else{
                    cout << "Close Robot Success" << endl << flush;
                }

                rt_mutex_acquire(&mutex_monitor, TM_INFINITE);
                    monitor.Close();
                rt_mutex_release(&mutex_monitor);
                
                //Release restarted tasks
                rt_sem_broadcast(&sem_barrier);             


            } else if (msgRcv->CompareID(MESSAGE_CAM_OPEN)) {
                cout << "Command Open Camera Received" << endl << flush;
                //start task Vision
                rt_sem_v(&sem_startVision);

            } else if (msgRcv->CompareID(MESSAGE_CAM_CLOSE)) {
                cout << "Command Close Camera Received" << endl << flush;
                //Trigger killVision
                rt_mutex_acquire(&mutex_killVision, TM_INFINITE);
                    killVision=1;
                rt_mutex_release(&mutex_killVision);

            } else if (msgRcv->CompareID(MESSAGE_ROBOT_COM_OPEN)) {
                cout << "Command Open Communication with Robot Received" << endl << flush;
                rt_sem_v(&sem_openComRobot);

            } else if (msgRcv->CompareID(MESSAGE_ROBOT_COM_CLOSE)) { //Actually we never get this message
                cout << "Command Close Communication with Robot Received" << endl << flush;
              
                rt_mutex_acquire(&mutex_robot, TM_INFINITE);
                    status=robot.Close();
                rt_mutex_release(&mutex_robot);
                

            } else if (msgRcv->CompareID(MESSAGE_ROBOT_START_WITHOUT_WD)) {
                cout << "Command Start Robot without Watchdog Received" << endl << flush;
                rt_sem_v(&sem_startRobotWithoutWatchdog);

            } else if (msgRcv->CompareID(MESSAGE_ROBOT_START_WITH_WD)) {
                cout << "Command Start Robot with Watchdog Received" << endl << flush;
                rt_sem_v(&sem_startRobotWithWatchdog);

            } else if (msgRcv->CompareID(MESSAGE_CAM_ASK_ARENA)) {
                cout << "Command Search Arena Received" << endl << flush;
                rt_mutex_acquire(&mutex_searchArena, TM_INFINITE);
                    searchArena=1;
                rt_mutex_release(&mutex_searchArena);

            } else if (msgRcv->CompareID(MESSAGE_CAM_POSITION_COMPUTE_START)) {
                cout << "Command Get Robot Position Received" << endl << flush;
                rt_mutex_acquire(&mutex_getPosition, TM_INFINITE);
                    getPosition=1;
                rt_mutex_release(&mutex_getPosition);            

            } else if (msgRcv->CompareID(MESSAGE_CAM_POSITION_COMPUTE_STOP)) {
                cout << "Command Stop Getting Robot Position Received" << endl << flush;
                rt_mutex_acquire(&mutex_getPosition, TM_INFINITE);
                    getPosition=0;
                rt_mutex_release(&mutex_getPosition); 

            } else if (msgRcv->CompareID(MESSAGE_CAM_ARENA_CONFIRM)) {
                cout << "Command Confirm Arena Received" << endl << flush;
                rt_mutex_acquire(&mutex_drawArena, TM_INFINITE);
                    drawArena=1; //Trigger the drawing of the arena in Vision task
                rt_mutex_release(&mutex_drawArena);

                rt_mutex_acquire(&mutex_searchArena, TM_INFINITE);
                    searchArena=0; //Stop searching arena in Vision task
                rt_mutex_release(&mutex_searchArena);

                rt_mutex_acquire(&mutex_acquireImage, TM_INFINITE);
                    acquireImage=1; //Start acquiring again because we had to stop during the search of the arena in Vision Task
                rt_mutex_release(&mutex_acquireImage);            

            } else if (msgRcv->CompareID(MESSAGE_CAM_ARENA_INFIRM)) {
                cout << "Command Infirm Arena Received" << endl << flush;
                rt_mutex_acquire(&mutex_searchArena, TM_INFINITE);
                    searchArena=0; //We stop searching the arena in Vision Task
                rt_mutex_release(&mutex_searchArena);

                rt_mutex_acquire(&mutex_acquireImage, TM_INFINITE);
                    acquireImage=1; //Start acquiring again because we had to stop during the search of the arena in Vision Task
                rt_mutex_release(&mutex_acquireImage);

            } else if (msgRcv->CompareID(MESSAGE_ROBOT_GO_FORWARD) ||
                    msgRcv->CompareID(MESSAGE_ROBOT_GO_BACKWARD) ||
                    msgRcv->CompareID(MESSAGE_ROBOT_GO_LEFT) ||
                    msgRcv->CompareID(MESSAGE_ROBOT_GO_RIGHT) ||
                    msgRcv->CompareID(MESSAGE_ROBOT_STOP)) {

                rt_mutex_acquire(&mutex_move, TM_INFINITE);
                move = msgRcv->GetID();
                rt_mutex_release(&mutex_move);
            }
            delete(msgRcv); // must be deleted manually, no consumer

            //Update loop condition
            rt_mutex_acquire(&mutex_killReceiveFromMon, TM_INFINITE);
                killReceiveFromMonOk = killReceiveFromMon;
            rt_mutex_release(&mutex_killReceiveFromMon);
        }
    }
}




/**
 * @brief Thread opening communication with the robot.
 */
void Tasks::OpenComRobot(void *arg) { //PAS DE SOUCIS AU REDEMARAGE
    int status;
    int err;
    //bool killOpenComRobotOk = 0;

    cout << "Start " << __PRETTY_FUNCTION__ << endl << flush;
    // Synchronization barrier (waiting that all tasks are starting)
    rt_sem_p(&sem_barrier, TM_INFINITE);

        /**************************************************************************************/
        /* The task openComRobot starts here                                                  */
        /**************************************************************************************/
        while (1) { //Is used to restart process
            rt_sem_p(&sem_openComRobot, TM_INFINITE);    
            cout << "Open serial com (";
            rt_mutex_acquire(&mutex_robot, TM_INFINITE);
            status = robot.Open();
            rt_mutex_release(&mutex_robot);
            cout << status;
            cout << ")" << endl << flush;

            Message * msgSend;
            if (status < 0) {
                msgSend = new Message(MESSAGE_ANSWER_NACK);
            } else {           
                msgSend = new Message(MESSAGE_ANSWER_ACK);
            }
            WriteInQueue(&q_messageToMon, msgSend); // msgSend will be deleted by sendToMon
            
            //Trigger Detection of Communication Loss with Robot
            rt_sem_v(&sem_detectLostSupRob);

        }
}






/**
 * @brief Thread starting the communication with the robot.
 *//**
 * @brief Thread starting the communication with the robot.
 */
void Tasks::StartRobotTaskWithoutWatchdog(void *arg) {

    int killBatteryOk=0;
    Message * msgSend;
    /**************************************************************************************/
    /* The task startRobot starts here                                                    */
    /**************************************************************************************/
    while(1){
        cout << "Start " << __PRETTY_FUNCTION__ << endl << flush;
        // Synchronization barrier (waiting that all tasks are starting)
        rt_sem_p(&sem_barrier, TM_INFINITE);
        //Boolean to get the battery
        rt_mutex_acquire(&mutex_killBattery, TM_INFINITE);
        killBatteryOk=0;
        rt_mutex_release(&mutex_killBattery);
        rt_sem_p(&sem_startRobotWithoutWatchdog, TM_INFINITE);
        cout << "Start robot without watchdog (";
        rt_mutex_acquire(&mutex_robot, TM_INFINITE);
        msgSend = robot.Write(robot.StartWithoutWD());
        rt_mutex_release(&mutex_robot);
        cout << msgSend->GetID();
        cout << ")" << endl;  
        cout << "Movement answer: " << msgSend->ToString() << endl << flush;
        WriteInQueue(&q_messageToMon, msgSend);  // msgSend will be deleted by sendToMon

        if (msgSend->GetID() == MESSAGE_ANSWER_ACK) {
            rt_mutex_acquire(&mutex_robotStarted, TM_INFINITE);
            robotStarted = 1;
            rt_mutex_release(&mutex_robotStarted);
            rt_task_set_periodic(NULL, TM_NOW, 500000000);
            while (killBatteryOk==0) {
                rt_task_wait_period(NULL);
                rt_sem_v(&sem_askBattery);
                rt_mutex_acquire(&mutex_killBattery, TM_INFINITE);
                killBatteryOk=killBattery;
                rt_mutex_release(&mutex_killBattery);
            }
        }
    }
}


/**
 * @brief Thread starting the communication with the robot.
 *//**
 * @brief Thread starting the communication with the robot.
 */
void Tasks::StartRobotTaskWithWatchdog(void *arg) {
    
    int killBatteryOk;
    int cpt=1;
    Message * msgSend;
    
    while(1){
    
        cout << "Start " << __PRETTY_FUNCTION__ << endl << flush;
        // Synchronization barrier (waiting that all tasks are starting)
        rt_sem_p(&sem_barrier, TM_INFINITE);
        /**************************************************************************************/
        /* The task startRobot starts here                                                    */
        /**************************************************************************************/


        //Boolean to get the battery
        killBatteryOk = 0;
        rt_mutex_acquire(&mutex_killBattery, TM_INFINITE);
        killBattery=0;
        rt_mutex_release(&mutex_killBattery);

        rt_sem_p(&sem_startRobotWithWatchdog, TM_INFINITE);
        cout << "Start robot with watchdog (";

        rt_mutex_acquire(&mutex_robot, TM_INFINITE);
        msgSend = robot.Write(robot.StartWithWD());
        rt_mutex_release(&mutex_robot);

        cout << msgSend->GetID();
        cout << ")" << endl;  
        cout << "Movement answer: " << msgSend->ToString() << endl << flush;
        WriteInQueue(&q_messageToMon, msgSend);  // msgSend will be deleted by sendToMon

        if (msgSend->GetID() == MESSAGE_ANSWER_ACK) {
            rt_mutex_acquire(&mutex_robotStarted, TM_INFINITE);
            robotStarted = 1;
            rt_mutex_release(&mutex_robotStarted);

            rt_task_set_periodic(NULL, TM_NOW, 500000000);

            while (killBatteryOk==0) {
                cpt++;
                if(cpt==2){
                    rt_mutex_acquire(&mutex_robot, TM_INFINITE);
                        robot.Write(robot.ReloadWD());
                    rt_mutex_release(&mutex_robot);
                    cpt=0;
                }
                rt_task_wait_period(NULL);
                rt_sem_v(&sem_askBattery);
                rt_mutex_acquire(&mutex_killBattery, TM_INFINITE);
                killBatteryOk=killBattery;
                rt_mutex_release(&mutex_killBattery);
            }
        }
    }
}



/**
 * @brief Thread handling control of the robot.
 */
void Tasks::MoveTask(void *arg) {
    int rs;
    int cpMove;
    
    cout << "Start " << __PRETTY_FUNCTION__ << endl << flush;
    // Synchronization barrier (waiting that all tasks are starting)
    rt_sem_p(&sem_barrier, TM_INFINITE);
    
    /**************************************************************************************/
    /* The task starts here                                                               */
    /**************************************************************************************/
    rt_task_set_periodic(NULL, TM_NOW, 100000000);

    while (1) {
        rt_task_wait_period(NULL);
        cout << "Periodic movement update";
        rt_mutex_acquire(&mutex_robotStarted, TM_INFINITE);
        rs = robotStarted; //Value is updated in ReceiveFromMon when needed
        rt_mutex_release(&mutex_robotStarted);
        if (rs == 1) {
            rt_mutex_acquire(&mutex_move, TM_INFINITE);
            cpMove = move;
            rt_mutex_release(&mutex_move);
            
            cout << " move: " << cpMove;
            
            rt_mutex_acquire(&mutex_robot, TM_INFINITE);
            robot.Write(new Message((MessageID)cpMove));
            rt_mutex_release(&mutex_robot);
        }
        cout << endl << flush;
    }
}


void Tasks::DetectLostSupRob(void *arg){
    //counter to detect loss of communication
    int cpt=0;
    bool kill_detectLostSupRobOk=0;
    Message* msgSend;
    
    //Period = 1s
    rt_task_set_periodic(NULL, TM_NOW, 1000000000);

    while(1){//Used to restart process
    
        //Wait the Communication with the Robot to be Set
        rt_sem_p(&sem_detectLostSupRob, TM_INFINITE);
        cout << "Start DetectLostSupRob" << endl << flush;
        
        //Initialize counter to detect loss
        cpt = 0;
        
        //Initialize the variable for the loop condition
        kill_detectLostSupRobOk = 0;        
        rt_mutex_acquire(&mutex_killDetectLostSupRob, TM_INFINITE);
            killDetectLostSupRob = 0;
        rt_mutex_release(&mutex_killDetectLostSupRob);


        while(!kill_detectLostSupRobOk){
            rt_task_wait_period(NULL);

            //Test Communication with Robot
            rt_mutex_acquire(&mutex_robot, TM_INFINITE);
                msgSend = robot.Write(robot.Ping());
            rt_mutex_release(&mutex_robot);
            cout << "J'écris un message" << endl << flush;
            

            if(msgSend->GetID() == MESSAGE_ANSWER_COM_ERROR || msgSend->GetID() == MESSAGE_ANSWER_ROBOT_TIMEOUT){

                cout << "Didn't Get Any Answer from Robot" << endl << flush;
                cpt++;

                if(cpt==3){ //If 3 errors in the row
                    
                    //acknowledge loss communication with robot I THINK YOU MADE A MISTAKE IN THE CONCEPTION DOCUMENT THIS MESSAGE DOESN'T WORK
                    //WriteInQueue(&q_messageToMon, new Message(MESSAGE_MONITOR_LOST));
                    
                    cout << "Restart Communication with Robot" << endl << flush;
                    //Trigger Kill of DetectLostSupRob
                    rt_mutex_acquire(&mutex_killDetectLostSupRob, TM_INFINITE);
                        killDetectLostSupRob=1;
                    rt_mutex_release(&mutex_killDetectLostSupRob);

                    //Trigger Kill of the Battery acquisition and  therefore Robot with or without WD
                    rt_mutex_acquire(&mutex_killBattery, TM_INFINITE);
                        killBattery=1;
                    rt_mutex_release(&mutex_killBattery);
                    
                    rt_mutex_acquire(&mutex_robotStarted, TM_INFINITE);
                        robotStarted=0;
                    rt_mutex_release(&mutex_robotStarted);
                    
                    rt_sem_v(&sem_openComRobot);    


                }
            }

            else{
                cout << "I got an Answer from Robot" << endl << flush;
            }

            //Update loop variable
            rt_mutex_acquire(&mutex_killDetectLostSupRob, TM_INFINITE);
                kill_detectLostSupRobOk=killDetectLostSupRob;
            rt_mutex_release(&mutex_killDetectLostSupRob);        
        }
    }
}

        
void Tasks::AskBattery(void *arg){
    Message* p_mess_answer_battery;
    cout << "Start " << __PRETTY_FUNCTION__ << endl << flush;
    // Synchronization barrier (waiting that all tasks are starting)
    rt_sem_p(&sem_barrier, TM_INFINITE);
    
    /**************************************************************************************/
    /* The task starts here                                                               */
    /**************************************************************************************/
    while(1){
        rt_sem_p(&sem_askBattery, TM_INFINITE);
        rt_mutex_acquire(&mutex_robot, TM_INFINITE);
        p_mess_answer_battery = robot.Write(robot.GetBattery());
        rt_mutex_release(&mutex_robot);
        if(p_mess_answer_battery->GetID()==MESSAGE_ROBOT_BATTERY_LEVEL){
            WriteInQueue(&q_messageToMon, p_mess_answer_battery);
            cout << endl << flush;
        }
    }
}


//This task works on a difficult principle that fully make it controllable through 
// monitor, accessing booleans variables to set getPosition or searchArena



void Tasks::Vision(void *arg){
    Camera camera;
    Arena arena;
    //Img* img;
    list<Position> positionList;
    list<Position>::iterator it;
    string strPos;
    //MessagePosition* msgPos;
    Message* msgPosMsg;
    MessageImg msgImg;
    Message* msgImgMsg;
    msgImg.SetID(MESSAGE_CAM_IMAGE);
    
    // Synchronization barrier (waiting that all tasks are starting)
    rt_sem_p(&sem_barrier, TM_INFINITE);
    
    while(1){
        cout << "Start " << __PRETTY_FUNCTION__ << endl << flush;
        //Wait to get the order to start Vision managed in receiveFromMon
        rt_sem_p(&sem_startVision, TM_INFINITE);

        int killVisionOk=0;
        int acquireImageOk=0;
        int searchArenaOk=0;
        int drawArenaOk=0;
        int getPositionOk=0;
        //msgPos->SetID(MESSAGE_CAM_POSITION);
        rt_mutex_acquire(&mutex_killVision, TM_INFINITE);
        killVision=0;
        rt_mutex_release(&mutex_killVision);
        rt_task_set_periodic(NULL, TM_NOW, 100000000);
        camera.Open();  
        while(killVisionOk==0){
            rt_task_wait_period(NULL);
            while(acquireImageOk==1){
                rt_task_wait_period(NULL);
                cout << "open cam" << endl;
                Img img=camera.Grab();
                cout << "after camera grab" << endl << endl << endl;
                msgImg.SetImage(&img);
                cout << "after set image in msgImg" << endl << endl << endl;
                if(searchArenaOk==1){
                    rt_mutex_acquire(&mutex_acquireImage, TM_INFINITE);
                    acquireImage=0;
                    rt_mutex_release(&mutex_acquireImage);
                    arena=img.SearchArena();
                    img.DrawArena(arena);
                }
                if(drawArenaOk==1){
                    img.DrawArena(arena);
                }
                if(getPositionOk==1){
                    cout << "in GetPos=1" << endl << endl << endl;
                    //On démarre la recherche du robot dans la zone définie par l'arène
                    positionList=img.SearchRobot(arena);
                    //Définitition et assignation de l'itérateur de parcrous de la liste positionList
                    it=positionList.begin();
                    //Définition d'un messagePosition qui va contenir l'information (x,y)
                    //msgPos->SetPosition(*it);
                    MessagePosition msgPos(MESSAGE_CAM_POSITION,*it);
                    //Transformation en message classique
                    msgPosMsg=msgPos.Copy();
                    //Envoi
                    cout << "envoi au mon" << endl;
                    WriteInQueue(&q_messageToMon,&msgPos);
                    //Dessis du robot sur l'image
                    img.DrawRobot(*it);
                }

                //Définition d'un messageImg contenant l'image avec le robot et l'arène dessinés (ou pas)
                msgImg.SetImage(&img);
                //Transformation en message classique
                msgImgMsg=msgImg.Copy();
                //Envoi
                WriteInQueue(&q_messageToMon,&msgImg);
            }
            rt_mutex_acquire(&mutex_acquireImage, TM_INFINITE);
                acquireImageOk=acquireImage;
            rt_mutex_release(&mutex_acquireImage);
            rt_mutex_acquire(&mutex_getPosition, TM_INFINITE);
                getPositionOk=getPosition;
            rt_mutex_release(&mutex_getPosition);
            rt_mutex_acquire(&mutex_searchArena, TM_INFINITE);
                searchArenaOk=searchArena;
            rt_mutex_release(&mutex_searchArena);
            rt_mutex_acquire(&mutex_drawArena, TM_INFINITE);
                drawArenaOk=drawArena;
            rt_mutex_release(&mutex_drawArena);
        }
        rt_mutex_acquire(&mutex_killVision, TM_INFINITE);
        killVisionOk = killVision;
        rt_mutex_release(&mutex_killVision);
    }       
}



/*
 * Write a message in a given queue
 * @param queue Queue identifier
 * @param msg Message to be stored
 */
void Tasks::WriteInQueue(RT_QUEUE *queue, Message *msg) {
    int err;
    if ((err = rt_queue_write(queue, (const void *) &msg, sizeof ((const void *) &msg), Q_NORMAL)) < 0) {
        cerr << "Write in queue failed: " << strerror(-err) << endl << flush;
        throw std::runtime_error{"Error in write in queue"};
    }
}

/**
 * Read a message from a given queue, block if empty
 * @param queue Queue identifier
 * @return Message read
 */
Message *Tasks::ReadInQueue(RT_QUEUE *queue) {
    int err;
    Message *msg;

    if ((err = rt_queue_read(queue, &msg, sizeof ((void*) &msg), TM_INFINITE)) < 0) {
        cout << "Read in queue failed: " << strerror(-err) << endl << flush;
        throw std::runtime_error{"Error in read in queue"};
    }/** else {
        cout << "@msg :" << msg << endl << flush;
    } /**/

    return msg;
}

