/*
 * Copyright (C) 1998-2015 Paul Davis <paul@linuxaudiosystems.com>
 * Copyright (C) 2009-2011 Carl Hetherington <carl@carlh.net>
 * Copyright (C) 2015 Robin Gareus <robin@gareus.org>
 *
 * 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 2 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, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#include <iostream>
#include <cstdio>
#include <fcntl.h>
#include <errno.h>

#include "pbd/xml++.h"
#include "pbd/error.h"
#include "pbd/failed_constructor.h"
#include "pbd/convert.h"
#include "pbd/strsplit.h"

#include "midi++/types.h"
#include "midi++/port.h"
#include "midi++/channel.h"

using namespace MIDI;
using namespace std;
using namespace PBD;

string Port::state_node_name = "MIDI-port";

Port::Port (string const & name, Flags flags)
	: _flags (flags)
	, _centrally_parsed (true)
{
	init (name, flags);
}

Port::Port (const XMLNode& node)
	: _centrally_parsed (true)
{
	Descriptor desc (node);

	init (desc.tag, desc.flags);

	/* derived class must call ::set_state() */
}

void
Port::init (string const & name, Flags flags)
{
	_ok = false;  /* derived class must set to true if constructor
			 succeeds.
		      */

	_parser = 0;

	_tagname = name;
	_flags = flags;

	_parser = new Parser ();

	for (int i = 0; i < 16; i++) {
		_channel[i] = new Channel (i, *this);
		_channel[i]->connect_signals ();
	}
}

Port::~Port ()
{
	for (int i = 0; i < 16; i++) {
		delete _channel[i];
	}

	delete _parser;
}

/** Send a clock tick message.
 * \return true on success.
 */
bool
Port::clock (timestamp_t timestamp)
{
	static byte clockmsg = 0xf8;

	if (sends_output()) {
		return midimsg (&clockmsg, 1, timestamp);
	}

	return false;
}

std::ostream & MIDI::operator << ( std::ostream & os, const MIDI::Port & port )
{
	using namespace std;
	os << "MIDI::Port { ";
	os << "name: " << port.name();
	os << "; ";
	os << "ok: " << port.ok();
	os << "; ";
	os << " }";
	return os;
}

Port::Descriptor::Descriptor (const XMLNode& node)
{
	const XMLProperty *prop;
	bool have_tag = false;
	bool have_mode = false;

	if ((prop = node.property ("tag")) != 0) {
		tag = prop->value();
		have_tag = true;
	}

	if ((prop = node.property ("mode")) != 0) {

		if (strings_equal_ignore_case (prop->value(), "output") || strings_equal_ignore_case (prop->value(), "out")) {
			flags = IsOutput;
		} else if (strings_equal_ignore_case (prop->value(), "input") || strings_equal_ignore_case (prop->value(), "in")) {
			flags = IsInput;
		}

		have_mode = true;
	}

	if (!have_tag || !have_mode) {
		throw failed_constructor();
	}
}

XMLNode&
Port::get_state () const
{
	XMLNode* root = new XMLNode (state_node_name);
	root->set_property ("tag", _tagname);

	if (_flags == IsInput) {
		root->set_property ("mode", "input");
	} else {
		root->set_property ("mode", "output");
	}

#if 0
	byte device_inquiry[6];

	device_inquiry[0] = 0xf0;
	device_inquiry[0] = 0x7e;
	device_inquiry[0] = 0x7f;
	device_inquiry[0] = 0x06;
	device_inquiry[0] = 0x02;
	device_inquiry[0] = 0xf7;

	write (device_inquiry, sizeof (device_inquiry), 0);
#endif

	return *root;
}

void
Port::set_state (const XMLNode& node)
{
	const XMLProperty* prop;

	if ((prop = node.property ("tag")) == 0 || prop->value() != _tagname) {
		return;
	}
}

bool
Port::centrally_parsed() const
{
	return _centrally_parsed;
}
