//-----------------------------------------------------------------------------
//
//	ControllerReplication.cpp
//
//	Implementation of the Z-Wave COMMAND_CLASS_CONTROLLER_REPLICATION
//
//	Copyright (c) 2010 Mal Lansell <openzwave@lansell.org>
//
//	SOFTWARE NOTICE AND LICENSE
//
//	This file is part of OpenZWave.
//
//	OpenZWave 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 3 of the License,
//	or (at your option) any later version.
//
//	OpenZWave 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 OpenZWave.  If not, see <http://www.gnu.org/licenses/>.
//
//-----------------------------------------------------------------------------

#include "command_classes/CommandClasses.h"
#include "command_classes/ControllerReplication.h"
#include "Defs.h"
#include "Msg.h"
#include "Driver.h"
#include "Node.h"
#include "Utils.h"

#include "value_classes/ValueByte.h"
#include "value_classes/ValueList.h"
#include "value_classes/ValueButton.h"

namespace OpenZWave
{
	namespace Internal
	{
		namespace CC
		{

			enum ControllerReplicationCmd
			{
				ControllerReplicationCmd_TransferGroup = 0x31,
				ControllerReplicationCmd_TransferGroupName = 0x32,
				ControllerReplicationCmd_TransferScene = 0x33,
				ControllerReplicationCmd_TransferSceneName = 0x34
			};

			static char const* c_controllerReplicationFunctionNames[] =
			{ "Groups", "Group Names", "Scenes", "Scene Names", };

//-----------------------------------------------------------------------------
// <ControllerReplication::HandleMsg>
// Handle a message from the Z-Wave network
//-----------------------------------------------------------------------------
			ControllerReplication::ControllerReplication(uint32 const _homeId, uint8 const _nodeId) :
					CommandClass(_homeId, _nodeId), m_busy(false), m_targetNodeId(0), m_funcId(0), m_nodeId(-1), m_groupCount(-1), m_groupIdx(-1)
			{
			}

//-----------------------------------------------------------------------------
// <ControllerReplication::HandleMsg>
// Handle a message from the Z-Wave network
//-----------------------------------------------------------------------------
			bool ControllerReplication::HandleMsg(uint8 const* _data, uint32 const _length, uint32 const _instance	// = 1
					)
			{
				// When creating replication messages, use FUNC_ID_ZW_SEND_REPLICATION_DATA instead of FUNC_ID_ZW_SEND_DATA
				// e.g. Msg* msg = new Msg( "TransferGroup", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_REPLICATION_DATA, true, false );

				switch (_data[0])
				{
					case ControllerReplicationCmd_TransferGroup:
					{
						break;
					}
					case ControllerReplicationCmd_TransferGroupName:
					{
						break;
					}
					case ControllerReplicationCmd_TransferScene:
					{
						break;
					}
					case ControllerReplicationCmd_TransferSceneName:
					{
						break;
					}
				}

				Msg* msg = new Msg("ControllerReplicationCmd_Complete", GetNodeId(), REQUEST, FUNC_ID_ZW_REPLICATION_COMMAND_COMPLETE, false, false);
				GetDriver()->SendMsg(msg, Driver::MsgQueue_Command);
				return true;
			}

//-----------------------------------------------------------------------------
// <ControllerReplication::SetValue>
// Set a value on the Z-Wave device
//-----------------------------------------------------------------------------
			bool ControllerReplication::SetValue(Internal::VC::Value const& _value)
			{
				bool res = false;
				uint8 instance = _value.GetID().GetInstance();

				switch (_value.GetID().GetIndex())
				{
					case ValueID_Index_ControllerReplication::NodeId:
					{
						if (Internal::VC::ValueByte* value = static_cast<Internal::VC::ValueByte*>(GetValue(instance, (uint16) ValueID_Index_ControllerReplication::NodeId)))
						{
							value->OnValueRefreshed((static_cast<Internal::VC::ValueByte const*>(&_value))->GetValue());
							value->Release();
							res = true;
						}
						break;
					}
					case ValueID_Index_ControllerReplication::Function:
					{
						if (Internal::VC::ValueList* value = static_cast<Internal::VC::ValueList*>(GetValue(instance, ValueID_Index_ControllerReplication::Function)))
						{
							Internal::VC::ValueList::Item const *item = (static_cast<Internal::VC::ValueList const*>(&_value))->GetItem();
							value->OnValueRefreshed(item->m_value);
							value->Release();
							res = true;
						}
						break;
					}
					case ValueID_Index_ControllerReplication::Replicate:
					{
						if (Internal::VC::ValueButton* button = static_cast<Internal::VC::ValueButton*>(GetValue(instance, ValueID_Index_ControllerReplication::Replicate)))
						{
							if (button->IsPressed())
							{
								res = StartReplication(instance);
							}
							button->Release();
						}
						break;
					}
				}
				return res;
			}

//-----------------------------------------------------------------------------
// <ControllerReplication::StartReplication>
// Set up the group and scene data to be sent to the other controller
//-----------------------------------------------------------------------------
			bool ControllerReplication::StartReplication(uint8 const _instance)
			{
				if (m_busy)
				{
					return false;
				}

				if (Internal::VC::ValueByte* value = static_cast<Internal::VC::ValueByte*>(GetValue(_instance, ValueID_Index_ControllerReplication::NodeId)))
				{
					m_targetNodeId = value->GetValue();
					value->Release();
				}
				else
				{
					return false;
				}

				if (Internal::VC::ValueList* value = static_cast<Internal::VC::ValueList*>(GetValue(_instance, ValueID_Index_ControllerReplication::Function)))
				{
					Internal::VC::ValueList::Item const *item = value->GetItem();
					if (item)
						m_funcId = item->m_value;
					value->Release();
				}
				else
				{
					return false;
				}

				// Store the Z-Wave command we should use when replication has completed.
				m_nodeId = -1;
				m_groupCount = -1;
				m_groupIdx = -1;
				m_busy = true;

				// Set up the groups and scenes to be sent
				SendNextData();
				return true;
			}

//-----------------------------------------------------------------------------
// <ControllerReplication::SendNextData>
// Send the next block of replication data
//-----------------------------------------------------------------------------
			void ControllerReplication::SendNextData()
			{
				uint16 i = 255;

				if (!m_busy)
				{
					return;
				}

				while (1)
				{
					if (m_groupIdx != -1)
					{
						m_groupIdx++;
						if (m_groupIdx <= m_groupCount)
						{
							break;
						}
					}
					i = m_nodeId == -1 ? 0 : m_nodeId + 1;
					LockGuard LG(GetDriver()->m_nodeMutex);
					while (i < 256)
					{
						if (GetDriver()->m_nodes[i])
						{
							m_groupCount = GetDriver()->m_nodes[i]->GetNumGroups();
							if (m_groupCount != 0)
							{
								m_groupName = GetDriver()->m_nodes[i]->GetGroupLabel(m_groupIdx);
								m_groupIdx = m_groupName.length() > 0 ? 0 : 1;
								break;
							}
						}
						i++;
					}
					m_nodeId = i;
					break;
				}
				if (i < 255)
				{
					Msg* msg = new Msg((m_groupName.length() > 0 ? "ControllerReplicationCmd_TransferGroupName" : "ControllerReplicationCmd_TransferGroup"), m_targetNodeId, REQUEST, FUNC_ID_ZW_REPLICATION_SEND_DATA, true, true, FUNC_ID_APPLICATION_COMMAND_HANDLER, GetCommandClassId());
					msg->Append(m_targetNodeId);
					if (m_groupName.length() > 0)
					{
						msg->Append((uint8) (m_groupName.length() + 4));
						msg->Append(GetCommandClassId());
						msg->Append(ControllerReplicationCmd_TransferGroupName);
						msg->Append(0);
						msg->Append(m_groupIdx);
						for (uint8 j = 0; j < m_groupName.length(); j++)
						{
							msg->Append(m_groupName[j]);
						}
						m_groupName = "";
					}
					else
					{
						msg->Append(5);
						msg->Append(GetCommandClassId());
						msg->Append(ControllerReplicationCmd_TransferGroup);
						msg->Append(0);
						msg->Append(m_groupIdx);
						msg->Append(m_nodeId);
					}
					msg->Append( TRANSMIT_OPTION_ACK);
					GetDriver()->SendMsg(msg, Driver::MsgQueue_Command);
				}
				else
				{
					GetDriver()->AddNodeStop(m_funcId);
					m_busy = false;
				}
			}

//-----------------------------------------------------------------------------
// <ControllerReplication::CreateVars>
// Create the values managed by this command class
//-----------------------------------------------------------------------------
			void ControllerReplication::CreateVars(uint8 const _instance)
			{
				if (Node* node = GetNodeUnsafe())
				{
					node->CreateValueByte(ValueID::ValueGenre_System, GetCommandClassId(), _instance, ValueID_Index_ControllerReplication::NodeId, "Node", "", false, false, 0, 0);
					vector<Internal::VC::ValueList::Item> items;

					Internal::VC::ValueList::Item item;
					for (uint8 i = 0; i < 4; ++i)
					{
						item.m_label = c_controllerReplicationFunctionNames[i];
						item.m_value = ControllerReplicationCmd_TransferGroup + i;
						items.push_back(item);
					}

					node->CreateValueList(ValueID::ValueGenre_System, GetCommandClassId(), _instance, ValueID_Index_ControllerReplication::Function, "Functions", "", false, false, 1, items, 0, 0);
					node->CreateValueButton(ValueID::ValueGenre_System, GetCommandClassId(), _instance, ValueID_Index_ControllerReplication::Replicate, "Replicate", 0);
				}
			}
		} // namespace CC
	} // namespace Internal
} // namespace OpenZWave
