/*!
 * sequence.cpp
 *
 * Copyright (c) 2016-2018, NADAL Jean-Baptiste. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA
 * 
 * @Author:  NADAL Jean-Baptiste
 * @Date: 11/04/2018
 *
 */

/*------------------------------- INCLUDES ----------------------------------*/

#include <json-c/json.h>

#include "devices/devices-manager.h"

#include "domo.h"

#include "sequence.h"

/*!  ----------------------------------------------------------------------------
 *  @fn Sequence
 *
 *  @brief Constructor of the Sequence Object.
 */
Sequence::Sequence(void) : m_is_in_progress(false),
                           m_id(0),
                           m_current_action(0),
                           m_current_duration(0)
{
}

/*!  ----------------------------------------------------------------------------
 *  @fn ~Sequence
 *
 *  @brief Constructor of the Sequence Object.
 */
Sequence::~Sequence(void)
{
}

/*!  ----------------------------------------------------------------------------
 *  @fn from_json
 *
 *  @brief Load Sequence Object.
 */
int Sequence::from_json(struct json_object *a_node)
{
    struct json_object *the_value_node;

    // id
    if (json_object_object_get_ex(a_node, k_entry_id, &the_value_node))
    {
        m_id = json_object_get_int(the_value_node);
    }

    // name
    if (json_object_object_get_ex(a_node, k_entry_name, &the_value_node))
    {
        m_name = json_object_get_string(the_value_node);
    }

    // timer
    if (json_object_object_get_ex(a_node, k_entry_timer, &the_value_node))
    {
        m_timer.from_json(the_value_node);
    }

    // actions
    if (json_object_object_get_ex(a_node, k_entry_actions, &the_value_node) && (json_object_get_type(the_value_node) == json_type_array))
    {
        int the_len;
        struct json_object *the_action_node;

        the_len = json_object_array_length(the_value_node);
        for (int i = 0; i < the_len; i++)
        {
            Action the_action;
            the_action_node = json_object_array_get_idx(the_value_node, i);
            the_action.from_json(the_action_node);

            m_actions.push_back(the_action);
        }
    }

    return 0;
}

/*!  ----------------------------------------------------------------------------
 *  @fn to_json
 *
 *  @brief Export Sequence Object as JSON Object.
 */
struct json_object *Sequence::to_json(void)
{
    std::vector<Action>::iterator the_action_it;
    struct json_object *the_root_node, *the_action_array_node;

    the_root_node = json_object_new_object();

    // id
    json_object_object_add(the_root_node, k_entry_id, json_object_new_int(m_id));

    // name
    json_object_object_add(the_root_node, k_entry_name, json_object_new_string(m_name.c_str()));

    // timer
    json_object_object_add(the_root_node, k_entry_timer, m_timer.to_json());

    // actions
    the_action_array_node = json_object_new_array();
    json_object_object_add(the_root_node, k_entry_actions, the_action_array_node);

    for (the_action_it = m_actions.begin(); the_action_it != m_actions.end(); the_action_it++)
    {
        json_object_array_add(the_action_array_node, (*the_action_it).to_json());
    }

    return the_root_node;
}

/*!  ----------------------------------------------------------------------------
 *  @fn get_id
 *
 *  @brief return the id of the Sequence.
 */
uint16_t Sequence::get_id(void) const
{
    return m_id;
}

/*!  ----------------------------------------------------------------------------
 *  @fn get_name
 *
 *  @brief return the name of the Sequence.
 */
const std::string &Sequence::get_name(void) const
{
    return m_name;
}

/*!  ----------------------------------------------------------------------------
 *  @fn get_timer
 *
 *  @brief return the timer of the Sequence.
 */
const Timer &Sequence::get_timer(void) const
{
    return m_timer;
}

/*!  ----------------------------------------------------------------------------
 *  @fn get_action
 *
 *  @brief return a reference on the current action.
 */
const Action &Sequence::get_action(void) const
{
    return m_actions[m_current_action];
}


/*!  ----------------------------------------------------------------------------
 *  @fn is_active
 *
 *  @brief return true if the sequence has an active timer. false otherwise.
 */
bool Sequence::is_active(void)
{
    return m_timer.is_active();
}

/*!  ----------------------------------------------------------------------------
 *  @fn is_in_progress
 *
 *  @brief return true if the sequence has been started..
 */
bool Sequence::is_in_progress(void)
{
    return m_is_in_progress;
}

/*!  ----------------------------------------------------------------------------
 *  @fn get_duration
 *
 *  @brief return current duration of the sequence.
 */
uint16_t Sequence::get_duration(void) const
{
    return m_current_duration + get_action().get_duration();
}

/*!  ----------------------------------------------------------------------------
 *  @fn is_in_progress
 *
 *  @brief start the sequence.
 */
void Sequence::start(DevicesManager *a_device_manager)
{
    m_is_in_progress = true;
    m_current_action = 0;
    m_current_duration = 0;
    
    a_device_manager->set_state(m_actions[m_current_action].get_capability(), m_actions[m_current_action].get_device_id(), true);
}

/*!  ----------------------------------------------------------------------------
 *  @fn stop
 *
 *  @brief stop the sequence.
 */
void Sequence::stop(void)
{
    m_is_in_progress = false;
}

/*!  ----------------------------------------------------------------------------
 *  @fn switch_to_next_action
 *
 *  @brief switch to the next action of the sequence.
 */
bool Sequence::switch_to_next_action(DevicesManager *a_device_manager)
{
    printf("switch_to_next_action current: %d, size: %ld\n", m_current_action, m_actions.size());
    if ((m_current_action + 1) >= m_actions.size())
    {
        printf("sequence is done.\n");
        return false;
    }    

    m_current_duration += m_actions[m_current_action].get_duration();
    // TODO STOP DEVICE MANAGER 
    m_current_action++;
    // TODO START DEVICE MANAGER 
    return true;
}
