/*
 * Copyright 2008 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com
 * Copyright 2008 Mika Lindqvist, monni1995_at_gmail.com
 * All rights reserved. Distributed under the terms of the MIT License.
 */
 
#include <bluetooth/DeviceClass.h>
#include <bluetooth/DiscoveryAgent.h>
#include <bluetooth/DiscoveryListener.h>
#include <bluetooth/bdaddrUtils.h>
#include <bluetooth/LocalDevice.h>
#include <bluetooth/RemoteDevice.h>
 
#include <bluetooth/HCI/btHCI_command.h>
#include <bluetooth/HCI/btHCI_event.h>
 
#include <bluetooth/debug.h>
#include <bluetooth/bluetooth_error.h>
 
#include <Catalog.h>
#include <CommandManager.h>
#include <Locale.h>
#include <bluetoothserver_p.h>
 
#include "KitSupport.h"
 
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "RemoteDevice"
 
 
namespace Bluetooth {
 
// TODO: Check headers for valid/reserved ranges
static const uint16 invalidConnectionHandle = 0xF000;
 
bool
RemoteDevice::IsTrustedDevice(void)
{
	CALLED();
	return true;
}
 
 
BString
RemoteDevice::GetFriendlyName(bool alwaysAsk)
{
	CALLED();
	if (!alwaysAsk) {
		// Check if the name is already retrieved
		// TODO: Check if It is known from a KnownDevicesList
		return BString(B_TRANSLATE("Not implemented"));
	}
 
	if (fDiscovererLocalDevice == NULL)
		return BString(B_TRANSLATE("#NoOwnerError#Not Valid name"));
 
	if (fMessenger == NULL)
		return BString(B_TRANSLATE("#ServerNotReady#Not Valid name"));
 
	void* remoteNameCommand = NULL;
	size_t size;
 
	// Issue inquiry command
	BMessage request(BT_MSG_HANDLE_SIMPLE_REQUEST);
	BMessage reply;
 
	request.AddInt32("hci_id", fDiscovererLocalDevice->ID());
 
	// Fill the request
	remoteNameCommand = buildRemoteNameRequest(fBdaddr, fPageRepetitionMode,
		fClockOffset, &size);
 
	request.AddData("raw command", B_ANY_TYPE, remoteNameCommand, size);
 
	request.AddInt16("eventExpected",  HCI_EVENT_CMD_STATUS);
	request.AddInt16("opcodeExpected",
		PACK_OPCODE(OGF_LINK_CONTROL, OCF_REMOTE_NAME_REQUEST));
 
	request.AddInt16("eventExpected",  HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE);
 
 
	if (fMessenger->SendMessage(&request, &reply) == B_OK) {
		BString name;
		int8 status;
 
		if ((reply.FindInt8("status", &status) == B_OK) && (status == BT_OK)) {
 
			if ((reply.FindString("friendlyname", &name) == B_OK )) {
				return name;
			} else {
				return BString(""); // should not happen
			}
 
		} else {
			// seems we got a negative event
			return BString(B_TRANSLATE("#CommandFailed#Not Valid name"));
		}
	}
 
	return BString(B_TRANSLATE("#NotCompletedRequest#Not Valid name"));
}
 
 
BString
RemoteDevice::GetFriendlyName()
{
	CALLED();
	return GetFriendlyName(true);
}
 
 
bdaddr_t
RemoteDevice::GetBluetoothAddress()
{
	CALLED();
	return fBdaddr;
}
 
 
bool
RemoteDevice::Equals(RemoteDevice* obj)
{
	CALLED();
	return bdaddrUtils::Compare(fBdaddr, obj->GetBluetoothAddress());
}
 
 
//  static RemoteDevice* GetRemoteDevice(Connection conn);
 
 
bool
RemoteDevice::Authenticate()
{
	CALLED();
	int8 btStatus = BT_ERROR;
 
	if (fMessenger == NULL || fDiscovererLocalDevice == NULL)
		return false;
 
	BluetoothCommand<typed_command(hci_cp_create_conn)>
		createConnection(OGF_LINK_CONTROL, OCF_CREATE_CONN);
 
	bdaddrUtils::Copy(createConnection->bdaddr, fBdaddr);
	createConnection->pscan_rep_mode = fPageRepetitionMode;
	createConnection->pscan_mode = fScanMode; // Reserved in spec 2.1
	createConnection->clock_offset = fClockOffset | 0x8000; // substract!
 
	uint32 roleSwitch;
	fDiscovererLocalDevice->GetProperty("role_switch_capable", &roleSwitch);
	createConnection->role_switch = (uint8)roleSwitch;
 
	uint32 packetType;
	fDiscovererLocalDevice->GetProperty("packet_type", &packetType);
	createConnection->pkt_type = (uint16)packetType;
 
	BMessage request(BT_MSG_HANDLE_SIMPLE_REQUEST);
	BMessage reply;
 
	request.AddInt32("hci_id", fDiscovererLocalDevice->ID());
	request.AddData("raw command", B_ANY_TYPE,
		createConnection.Data(), createConnection.Size());
 
	// First we get the status about the starting of the connection
	request.AddInt16("eventExpected",  HCI_EVENT_CMD_STATUS);
	request.AddInt16("opcodeExpected", PACK_OPCODE(OGF_LINK_CONTROL,
		OCF_CREATE_CONN));
 
	// if authentication needed, we will send any of these commands
	// to accept or deny the LINK KEY [a]
	request.AddInt16("eventExpected",  HCI_EVENT_CMD_COMPLETE);
	request.AddInt16("opcodeExpected", PACK_OPCODE(OGF_LINK_CONTROL,
		OCF_LINK_KEY_REPLY));
 
	request.AddInt16("eventExpected",  HCI_EVENT_CMD_COMPLETE);
	request.AddInt16("opcodeExpected", PACK_OPCODE(OGF_LINK_CONTROL,
		OCF_LINK_KEY_NEG_REPLY));
 
	// in negative case, a pincode will be replied [b]
	// this request will be handled by sepatated by the pincode window
	// request.AddInt16("eventExpected",  HCI_EVENT_CMD_COMPLETE);
	// request.AddInt16("opcodeExpected", PACK_OPCODE(OGF_LINK_CONTROL,
	//	OCF_PIN_CODE_REPLY));
 
	// [a] this is expected of authentication required
	request.AddInt16("eventExpected", HCI_EVENT_LINK_KEY_REQ);
	// [b] If we deny the key an authentication will be requested
	// but this request will be handled by sepatated by the pincode
	// window
	// request.AddInt16("eventExpected", HCI_EVENT_PIN_CODE_REQ);
 
	// this almost involves already the happy end
	request.AddInt16("eventExpected",  HCI_EVENT_LINK_KEY_NOTIFY);
 
	request.AddInt16("eventExpected", HCI_EVENT_CONN_COMPLETE);
 
	if (fMessenger->SendMessage(&request, &reply) == B_OK)
		reply.FindInt8("status", &btStatus);
 
	if (btStatus == BT_OK) {
		reply.FindInt16("handle", (int16*)&fHandle);
		return true;
	} else
		return false;
}
 
 
status_t
RemoteDevice::Disconnect(int8 reason)
{
	CALLED();
	if (fHandle != invalidConnectionHandle) {
 
		int8 btStatus = BT_ERROR;
 
		if (fMessenger == NULL || fDiscovererLocalDevice == NULL)
			return false;
 
		BluetoothCommand<typed_command(struct hci_disconnect)>
			disconnect(OGF_LINK_CONTROL, OCF_DISCONNECT);
 
		disconnect->reason = reason;
		disconnect->handle = fHandle;
 
		BMessage request(BT_MSG_HANDLE_SIMPLE_REQUEST);
		BMessage reply;
 
 
		request.AddInt32("hci_id", fDiscovererLocalDevice->ID());
		request.AddData("raw command", B_ANY_TYPE,
			disconnect.Data(), disconnect.Size());
 
		request.AddInt16("eventExpected",  HCI_EVENT_CMD_STATUS);
		request.AddInt16("opcodeExpected", PACK_OPCODE(OGF_LINK_CONTROL,
			OCF_DISCONNECT));
 
		request.AddInt16("eventExpected",  HCI_EVENT_DISCONNECTION_COMPLETE);
 
		if (fMessenger->SendMessage(&request, &reply) == B_OK)
			reply.FindInt8("status", &btStatus);
 
		if (btStatus == BT_OK)
			fHandle = invalidConnectionHandle;
 
		return btStatus;
 
	}
 
	return B_ERROR;
}
 
 
//  bool Authorize(Connection conn);
//  bool Encrypt(Connection conn, bool on);
 
 
bool
RemoteDevice::IsAuthenticated()
{
	CALLED();
	return true;
}
 
 
//  bool IsAuthorized(Connection conn);
 
 
bool
RemoteDevice::IsEncrypted()
{
	CALLED();
	return true;
}
 
 
LocalDevice*
RemoteDevice::GetLocalDeviceOwner()
{
	CALLED();
	return fDiscovererLocalDevice;
}
 
 
/* Private */
void
RemoteDevice::SetLocalDeviceOwner(LocalDevice* ld)
{
	CALLED();
	fDiscovererLocalDevice = ld;
}
 
 
/* Constructor */
RemoteDevice::RemoteDevice(const bdaddr_t address, uint8 record[3])
	:
	BluetoothDevice(),
	fDiscovererLocalDevice(NULL),
	fHandle(invalidConnectionHandle)
{
	CALLED();
	fBdaddr = address;
	fDeviceClass.SetRecord(record);
	fMessenger = _RetrieveBluetoothMessenger();
}
 
 
RemoteDevice::RemoteDevice(const BString& address)
	:
	BluetoothDevice(),
	fDiscovererLocalDevice(NULL),
	fHandle(invalidConnectionHandle)
{
	CALLED();
	fBdaddr = bdaddrUtils::FromString((const char*)address.String());
	fMessenger = _RetrieveBluetoothMessenger();
}
 
 
RemoteDevice::~RemoteDevice()
{
	CALLED();
	delete fMessenger;
}
 
 
BString
RemoteDevice::GetProperty(const char* property) /* Throwing */
{
	return NULL;
}
 
 
status_t
RemoteDevice::GetProperty(const char* property, uint32* value) /* Throwing */
{
	CALLED();
	return B_ERROR;
}
 
 
DeviceClass
RemoteDevice::GetDeviceClass()
{
	CALLED();
	return fDeviceClass;
}
 
}

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fPageRepetitionMode, fScanPeriodMode, fScanMode, fClockOffset.