<commit_before>#include "portaudio.h"

#include "ARTypes.h"
#include "ARServer.h"
#include "ARClient.h"
#include "IEncoder.h"
#include "WavPackEncoder.h"
#include "IDecoder.h"
#include "WavPackDecoder.h"

#include <boost/lexical_cast.hpp>

#include <iostream>
#include <string>
#include <stdexcept>

using namespace std;

const string& VER = "0.1";

void BeginServerMode();
void BeginClientMode();

int main (int argc, char* argv[])
{
	cout << "AudioReflector " << VER << endl;
	Pa_Initialize();

	cout << "Server or Client mode? [s/c]: ";
	string serverOrClient;
	getline(cin, serverOrClient);

	if (serverOrClient == "s") {
		BeginServerMode();

	} else {
		BeginClientMode();
	}

	return 0;
}

void BeginServerMode()
{
	using namespace audioreflector;

	string line;

	cout << "UDP Subscription Port [8889]: ";
	ar_ushort subscPort;
	getline(cin, line);
	if (line == "") {
		subscPort = 8889;
	} else {
		subscPort = boost::lexical_cast<ar_ushort>(line);
	}

	cout << "Sample Rate [44100]: ";
	int sampleRate;
	getline(cin, line);
	if (line == "") {
		sampleRate = 44100;
	} else {
		sampleRate = boost::lexical_cast<int>(line);
	}

	cout << "Encoder (n:none, wl:lossless) [wl]: ";
	IEncoderPtr encoder;
	getline(cin, line);
	if (line == "" || line == "wl") {
		encoder.reset(new WavPackEncoder(sampleRate));
	} else {

	}

	ARServer* server = new ARServer(subscPort, sampleRate, encoder);
	server->start();

	string input;
	cin >> input;
}

void BeginClientMode()
{
	using namespace audioreflector;

	string line;

	cout << "Reflector Host: ";
	string reflectorHost;
	getline(cin, reflectorHost);

	cout << "UDP Subscription Port [8889]: ";
	ushort subscPort;
	getline(cin, line);
	if (line == "") {
		subscPort = 8889;
	} else {
		subscPort = boost::lexical_cast<ushort>(line);
	}

	cout << "Sample Rate [44100]: ";
	int sampleRate;
	getline(cin, line);
	if (line == "") {
		sampleRate = 44100;
	} else {
		sampleRate = boost::lexical_cast<int>(line);
	}

	cout << "Encoding (n:none, wl:lossless) [wl]: ";
	IDecoderPtr decoder;
	getline(cin, line);
	if (line == "" || line == "wl") {
		decoder.reset(new WavPackDecoder());
	} else {

	}

	try
	{
		ARClient* client = new ARClient(reflectorHost, subscPort, sampleRate,
				decoder);
		client->start();

		string input;
		cin >> input;

		client->stop();
	}
	catch (const std::runtime_error& e)
	{
		cout << "Error: " << e.what() << endl;
	}
}
<commit_msg>Fixed an ambiguity/compile issue on windows with ushort<commit_after>#include "portaudio.h"

#include "ARTypes.h"
#include "ARServer.h"
#include "ARClient.h"
#include "IEncoder.h"
#include "WavPackEncoder.h"
#include "IDecoder.h"
#include "WavPackDecoder.h"

#include <boost/lexical_cast.hpp>

#include <iostream>
#include <string>
#include <stdexcept>

using namespace std;

const string& VER = "0.1";

void BeginServerMode();
void BeginClientMode();

int main (int argc, char* argv[])
{
	cout << "AudioReflector " << VER << endl;
	Pa_Initialize();

	cout << "Server or Client mode? [s/c]: ";
	string serverOrClient;
	getline(cin, serverOrClient);

	if (serverOrClient == "s") {
		BeginServerMode();

	} else {
		BeginClientMode();
	}

	return 0;
}

void BeginServerMode()
{
	using namespace audioreflector;

	string line;

	cout << "UDP Subscription Port [8889]: ";
	ar_ushort subscPort;
	getline(cin, line);
	if (line == "") {
		subscPort = 8889;
	} else {
		subscPort = boost::lexical_cast<ar_ushort>(line);
	}

	cout << "Sample Rate [44100]: ";
	int sampleRate;
	getline(cin, line);
	if (line == "") {
		sampleRate = 44100;
	} else {
		sampleRate = boost::lexical_cast<int>(line);
	}

	cout << "Encoder (n:none, wl:lossless) [wl]: ";
	IEncoderPtr encoder;
	getline(cin, line);
	if (line == "" || line == "wl") {
		encoder.reset(new WavPackEncoder(sampleRate));
	} else {

	}

	ARServer* server = new ARServer(subscPort, sampleRate, encoder);
	server->start();

	string input;
	cin >> input;
}

void BeginClientMode()
{
	using namespace audioreflector;

	string line;

	cout << "Reflector Host: ";
	string reflectorHost;
	getline(cin, reflectorHost);

	cout << "UDP Subscription Port [8889]: ";
	audioreflector::ar_ushort subscPort;
	getline(cin, line);
	if (line == "") {
		subscPort = 8889;
	} else {
		subscPort = boost::lexical_cast<audioreflector::ar_ushort>(line);
	}

	cout << "Sample Rate [44100]: ";
	int sampleRate;
	getline(cin, line);
	if (line == "") {
		sampleRate = 44100;
	} else {
		sampleRate = boost::lexical_cast<int>(line);
	}

	cout << "Encoding (n:none, wl:lossless) [wl]: ";
	IDecoderPtr decoder;
	getline(cin, line);
	if (line == "" || line == "wl") {
		decoder.reset(new WavPackDecoder());
	} else {

	}

	try
	{
		ARClient* client = new ARClient(reflectorHost, subscPort, sampleRate,
				decoder);
		client->start();

		string input;
		cin >> input;

		client->stop();
	}
	catch (const std::runtime_error& e)
	{
		cout << "Error: " << e.what() << endl;
	}
}
<|endoftext|>


<commit_before>/*
 * RelayNode.cpp
 * Homie Node for a Relay with optional status indicator LED
 *
 * Version: 1.2
 * Author: Lübbe Onken (http://github.com/luebbe)
 */

#include "RelayNode.hpp"

RelayNode::RelayNode(const char *name, const int8_t relayPin, const int8_t ledPin, const bool reverseSignal)
    : HomieNode(name, "RelayNode", "actor")
{
  _name = name;
  _id = 0;
  _relayPin = relayPin;
  _ledPin = ledPin;
  if (reverseSignal)
  {
    _relayOnValue = LOW;
    _relayOffValue = HIGH;
  }
  else
  {
    _relayOnValue = HIGH;
    _relayOffValue = LOW;
  }
  advertiseProps();
}

RelayNode::RelayNode(const char *name, const uint8_t id, TGetRelayState OnGetRelayState, TSetRelayState OnSetRelayState)
    : HomieNode(name, "RelayNode", "actor")
{
  _name = name;
  _id = id;
  _onGetRelayState = OnGetRelayState;
  _onSetRelayState = OnSetRelayState;
  advertiseProps();
}

void RelayNode::advertiseProps()
{
  advertise("on")
      .setDatatype("boolean")
      .settable();

  advertise("timeout")
      .setDatatype("integer")
      .settable();
}

bool RelayNode::handleOnOff(const String &value)
{
  if (value == "true" || value == "false" || value == "toggle")
  {
    if (value == "toggle")
    {
      bool current = getRelay();
      setRelay(!current);
    }
    else
      setRelay(value == "true");
    return true;
  }
  else
  {
    return false;
  }
}

#define IS_INTEGER(s) (s == String(s.toInt()))

bool RelayNode::handleTimeout(const String &value)
{
  if (IS_INTEGER(value))
  {
    long timeout = value.toInt();
    if (timeout > 0)
    {
      setRelay(true, timeout);
      return true;
    }
  }
  return false;
}

bool RelayNode::handleInput(const HomieRange &range, const String &property, const String &value)
{
  Homie.getLogger() << "Message: " << property << " " << value << endl;

  if (property.equals("on"))
  {
    return handleOnOff(value);
  }
  else if (property.equals("timeout"))
  {
    return handleTimeout(value);
  }
  else
  {
    return false;
  }
}

void RelayNode::onReadyToOperate()
{
  setRelay(false);
};

void RelayNode::printCaption()
{
  Homie.getLogger() << cCaption << _name.c_str() << ":" << endl;
}

void RelayNode::sendState()
{
  printCaption();
  bool on = getRelay();
  Homie.getLogger() << cIndent << "is " << (on ? "on" : "off") << endl;
  if (Homie.isConnected())
  {
    setProperty("on").send(on ? "true" : "false");
    setProperty("timeout").send(String(long(_timeout)));
  }
}

void RelayNode::setLed(bool on)
{
  if (_ledPin > DEFAULTPIN)
  {
    digitalWrite(_ledPin, on ? LOW : HIGH); // LOW = LED on
  }
}

bool RelayNode::getRelay()
{
  if (_onGetRelayState != NULL)
  {
    return _onGetRelayState(_id);
  }
  else if (_relayPin > DEFAULTPIN)
  {
    return digitalRead(_relayPin) == _relayOnValue;
  }
  else
  {
    return false;
  }
}

void RelayNode::setRelay(bool on, long timeoutSecs)
{
  if (_onSetRelayState != NULL)
  {
    _onSetRelayState(_id, on);
    setTimeout(on, timeoutSecs);
    sendState();
  }
  else if (_relayPin > DEFAULTPIN)
  {
    digitalWrite(_relayPin, on ? _relayOnValue : _relayOffValue);
    setTimeout(on, timeoutSecs);
    sendState();
  }
  // Set Led according to relay
  setLed(on);
}

void RelayNode::setTimeout(bool on, long timeoutSecs)
{
  if (on && timeoutSecs > 0)
  {
    _ticker.attach(1.0f, std::bind(&RelayNode::tick, this));
    _timeout = timeoutSecs;
  }
  else
  {
    _ticker.detach();
  }
}

void RelayNode::tick()
{
  if (_timeout > 0)
  {
    _timeout--;
  }
  else
  {
    setRelay(false);
    _ticker.detach();
  }
  if (Homie.isConnected())
  {
    setProperty("timeout").send(String(long(_timeout)));
  }
}

void RelayNode::toggleRelay()
{
  setRelay(!getRelay());
}

void RelayNode::setup()
{
  printCaption();

  std::string info;

  if ((_onSetRelayState != NULL) && (_onGetRelayState != NULL))
  {
    Homie.getLogger() << cIndent << "Callback" << endl;
  }
  else if (_relayPin > DEFAULTPIN)
  {
    Homie.getLogger() << cIndent << "Relay Pin: " << _relayPin << endl
                      << cIndent << "Led Pin  : " << _ledPin << endl;
  }
  else
  {
    Homie.getLogger() << cIndent << "No Relay Pin or callback!" << endl;
  }

  if (_ledPin > DEFAULTPIN)
  {
    pinMode(_ledPin, OUTPUT);
  }

  if (_relayPin > DEFAULTPIN)
  {
    pinMode(_relayPin, OUTPUT);
  }
}
<commit_msg>RelayNode: Display ID on logger<commit_after>/*
 * RelayNode.cpp
 * Homie Node for a Relay with optional status indicator LED
 *
 * Version: 1.2
 * Author: Lübbe Onken (http://github.com/luebbe)
 */

#include "RelayNode.hpp"

RelayNode::RelayNode(const char *name, const int8_t relayPin, const int8_t ledPin, const bool reverseSignal)
    : HomieNode(name, "RelayNode", "actor")
{
  _name = name;
  _id = 0;
  _relayPin = relayPin;
  _ledPin = ledPin;
  if (reverseSignal)
  {
    _relayOnValue = LOW;
    _relayOffValue = HIGH;
  }
  else
  {
    _relayOnValue = HIGH;
    _relayOffValue = LOW;
  }
  advertiseProps();
}

RelayNode::RelayNode(const char *name, const uint8_t id, TGetRelayState OnGetRelayState, TSetRelayState OnSetRelayState)
    : HomieNode(name, "RelayNode", "actor")
{
  _name = name;
  _id = id;
  _onGetRelayState = OnGetRelayState;
  _onSetRelayState = OnSetRelayState;
  advertiseProps();
}

void RelayNode::advertiseProps()
{
  advertise("on")
      .setDatatype("boolean")
      .settable();

  advertise("timeout")
      .setDatatype("integer")
      .settable();
}

bool RelayNode::handleOnOff(const String &value)
{
  if (value == "true" || value == "false" || value == "toggle")
  {
    if (value == "toggle")
    {
      bool current = getRelay();
      setRelay(!current);
    }
    else
      setRelay(value == "true");
    return true;
  }
  else
  {
    return false;
  }
}

#define IS_INTEGER(s) (s == String(s.toInt()))

bool RelayNode::handleTimeout(const String &value)
{
  if (IS_INTEGER(value))
  {
    long timeout = value.toInt();
    if (timeout > 0)
    {
      setRelay(true, timeout);
      return true;
    }
  }
  return false;
}

bool RelayNode::handleInput(const HomieRange &range, const String &property, const String &value)
{
  Homie.getLogger() << "Message: " << property << " " << value << endl;

  if (property.equals("on"))
  {
    return handleOnOff(value);
  }
  else if (property.equals("timeout"))
  {
    return handleTimeout(value);
  }
  else
  {
    return false;
  }
}

void RelayNode::onReadyToOperate()
{
  setRelay(false);
};

void RelayNode::printCaption()
{
  Homie.getLogger() << cCaption << _name.c_str() << ":" << endl;
}

void RelayNode::sendState()
{
  printCaption();
  bool on = getRelay();
  Homie.getLogger() << cIndent << "is " << (on ? "on" : "off") << endl;
  if (Homie.isConnected())
  {
    setProperty("on").send(on ? "true" : "false");
    setProperty("timeout").send(String(long(_timeout)));
  }
}

void RelayNode::setLed(bool on)
{
  if (_ledPin > DEFAULTPIN)
  {
    digitalWrite(_ledPin, on ? LOW : HIGH); // LOW = LED on
  }
}

bool RelayNode::getRelay()
{
  if (_onGetRelayState != NULL)
  {
    return _onGetRelayState(_id);
  }
  else if (_relayPin > DEFAULTPIN)
  {
    return digitalRead(_relayPin) == _relayOnValue;
  }
  else
  {
    return false;
  }
}

void RelayNode::setRelay(bool on, long timeoutSecs)
{
  if (_onSetRelayState != NULL)
  {
    _onSetRelayState(_id, on);
    setTimeout(on, timeoutSecs);
    sendState();
  }
  else if (_relayPin > DEFAULTPIN)
  {
    digitalWrite(_relayPin, on ? _relayOnValue : _relayOffValue);
    setTimeout(on, timeoutSecs);
    sendState();
  }
  // Set Led according to relay
  setLed(on);
}

void RelayNode::setTimeout(bool on, long timeoutSecs)
{
  if (on && timeoutSecs > 0)
  {
    _ticker.attach(1.0f, std::bind(&RelayNode::tick, this));
    _timeout = timeoutSecs;
  }
  else
  {
    _ticker.detach();
  }
}

void RelayNode::tick()
{
  if (_timeout > 0)
  {
    _timeout--;
  }
  else
  {
    setRelay(false);
    _ticker.detach();
  }
  if (Homie.isConnected())
  {
    setProperty("timeout").send(String(long(_timeout)));
  }
}

void RelayNode::toggleRelay()
{
  setRelay(!getRelay());
}

void RelayNode::setup()
{
  printCaption();

  std::string info;

  if ((_onSetRelayState != NULL) && (_onGetRelayState != NULL))
  {
    Homie.getLogger() << cIndent << "Callback id: " << _id << endl;
  }
  else if (_relayPin > DEFAULTPIN)
  {
    Homie.getLogger() << cIndent << "Relay Pin: " << _relayPin << endl
                      << cIndent << "Led Pin  : " << _ledPin << endl;
  }
  else
  {
    Homie.getLogger() << cIndent << "No Relay Pin or callback!" << endl;
  }

  if (_ledPin > DEFAULTPIN)
  {
    pinMode(_ledPin, OUTPUT);
  }

  if (_relayPin > DEFAULTPIN)
  {
    pinMode(_relayPin, OUTPUT);
  }
}
<|endoftext|>


<commit_before>#include "SynthGrid.h"
#include "SDL.h"
#include "ISynth.h"
#include "Renderer.h"
#include "ModularSynth.h"
#include "SynthModule.h"
#include "ModuleSelector.h"
#include "Color.h"
#include <cstdlib>
#include <cstdio>
#include <cmath>

SynthGrid::SynthGrid(EditorState& editorState, ISynth& synth)
	:Editor(editorState, true), mSynth(synth), mMode(IDLE), mSelectedModule(-1)
{
	mModuleSelector = new ModuleSelector(editorState);
}

void SynthGrid::drawWire(Renderer& renderer, int x1, int y1, int x2, int y2, const Color& color1, const Color& color2) const
{
	const int spacing = 8;
	int dx = x1 - x2, dy = y1 - y2;
	int d = sqrt(dx * dx + dy * dy) / spacing;

	if (d == 0)
		return;

	int px = x1, py = y1;

	for (int i = 1 ; i <= d ; ++i)
	{
		int x = (x2 - x1) * i / d + x1;
		int y = (y2 - y1) * i / d + y1 + sin((float)i/d * M_PI) * 16;

		renderer.renderLine(px, py, x, y + 1, Color(0,0,0));
		renderer.renderLine(px, py, x, y, i % 2 ? color1 : color2);

		px = x;
		py = y;
	}
}

void SynthGrid::startMove(int module)
{
	// Don't start moving from empty slot

	if (getModularSynth().getModule(module) == NULL)
		return;

	mMode = MOVING_MODULE;
	mFromModule = module;
}

void SynthGrid::endMove(int targetModule)
{
	if (mMode != MOVING_MODULE)
		return;

	mMode = IDLE;

	getModularSynth().swapModules(mFromModule, targetModule);
}

void SynthGrid::startConnect(int fromModule, int toModule, int fromOutput, int toInput)
{
	mMode = CONNECTING_MODULE;
	mFromModule = fromModule;
	mToModule = toModule;
	mFromOutput = fromOutput;
	mToInput = toInput;
}

void SynthGrid::endConnect(int module, int connector)
{
	if (mMode != CONNECTING_MODULE)
		return;

	mMode = IDLE;

	if (mFromModule != -1)
	{
		mToModule = module;
		mToInput = connector;
	}
	else
	{
		mFromModule = module;
		mFromOutput = connector;
	}
}

SDL_Rect SynthGrid::getConnectorArea(int moduleIndex, int type, int connectorIndex, const SDL_Rect& parent) const
{
	const SynthModule* module = getModularSynth().getModule(moduleIndex);
	const int connectorSize = 8;

	SDL_Rect area = {0,0,connectorSize,connectorSize};

	if (module)
	{
		SDL_Rect moduleArea = getModuleArea(moduleIndex, parent);
		int c;

		if (type == 0)
			c = module->getNumInputs();
		else
			c = module->getNumOutputs();

		area.x = moduleArea.x + moduleArea.w / 2 + (connectorIndex - c / 2) * moduleArea.w / c - connectorSize / 2;
		area.y = moduleArea.y;

		if (type == 1)
			area.y += moduleArea.h - area.h - 2;
		else
			area.y += 2;
	}

	return area;
}

SDL_Rect SynthGrid::getModuleArea(int index, const SDL_Rect& parent) const
{
	SDL_Rect area = {0, 0, parent.w / gridWidth, parent.h / gridHeight};

	area.x = parent.x + area.w * (index % gridWidth);
	area.y = parent.y + area.h * (index / gridHeight);

	return area;
}

bool SynthGrid::pickModule(int x, int y, const SDL_Rect& area, int& moduleOut, bool includeEmpty)
{
	const ModularSynth& modularSynth = getModularSynth();

	for (int index = 0 ; index < ModularSynth::maxModules ; ++index)
	{
		const SynthModule *module = modularSynth.getModule(index);

		if (includeEmpty || module != NULL)
		{
			SDL_Rect moduleArea = getModuleArea(index, area);
			SDL_Point point = {x,y};

			if (pointInRect(point, moduleArea))
			{
				moduleOut = index;
				return true;
			}
		}
	}

	return false;
}

bool SynthGrid::pickConnector(int x, int y, const SDL_Rect& area, int& moduleOut, int& connectorType, int& connector)
{
	if (!pickModule(x, y, area, moduleOut))
		return false;

	const ModularSynth& modularSynth = getModularSynth();
	const SynthModule *module = modularSynth.getModule(moduleOut);
	SDL_Point point = {x,y};

	for (connectorType = 0 ; connectorType <= 1 ; connectorType++)
	{
		int c;

		if (connectorType == 0)
			c = module->getNumInputs();
		else
			c = module->getNumOutputs();

		for (int i = 0 ; i < c ; ++i)
		{
			if (pointInRect(point, getConnectorArea(moduleOut, connectorType, i, area)))
			{
				connector = i;
				return true;
			}
		}
	}

	return false;
}

void SynthGrid::onDraw(Renderer& renderer, const SDL_Rect& area)
{
	setDirty(false);

	if (mModal != NULL)
	{
		//mModal->onDraw(renderer, area);
		return;
	}

	renderer.clearRect(area, Color(0,0,0));

	const ModularSynth& modularSynth = getModularSynth();

	for (int index = 0 ; index < ModularSynth::maxModules ; ++index)
	{
		const SynthModule *module = modularSynth.getModule(index);

		if (module != NULL)
		{
			SDL_Rect moduleArea = getModuleArea(index, area);
			renderer.drawRect(moduleArea, mSelectedModule == index ? Color(255,0,0) : Color(255,255,255));

			SDL_Rect textArea = {moduleArea.x + 2, moduleArea.y + moduleArea.h / 2 - 4, 100, 100};

			renderer.renderText(textArea, Color(255,255,255), module->getName());

			for (int connectorType = 0 ; connectorType <= 1 ; connectorType++)
			{
				int c;

				if (connectorType == 0)
					c = module->getNumInputs();
				else
					c = module->getNumOutputs();

				for (int i = 0 ; i < c ; ++i)
				{
					renderer.drawRect(getConnectorArea(index, connectorType, i, area), Color(255,255,0));
				}
			}
		}
	}

	for (int i = 0 ; i < modularSynth.getNumConnections() ; ++i)
	{
		const SynthConnection& connection = modularSynth.getConnection(i);
		SDL_Rect fromModuleArea = getConnectorArea(connection.fromModule, 1, connection.fromOutput, area);
		SDL_Rect toModuleArea = getConnectorArea(connection.toModule, 0, connection.toInput, area);
		drawWire(renderer, fromModuleArea.x + fromModuleArea.w / 2, fromModuleArea.y + fromModuleArea.h / 2, toModuleArea.x + toModuleArea.w / 2, toModuleArea.y + toModuleArea.h / 2, Color(255,0,0), Color(0,255,0));
	}

	if (mMode == CONNECTING_MODULE)
	{
		SDL_Rect moduleArea;
		if (mFromModule != -1)
		{
			moduleArea = getConnectorArea(mFromModule, 1, mFromOutput, area);
		}
		else
		{
			moduleArea = getConnectorArea(mToModule, 0, mToInput, area);
		}

		drawWire(renderer, mMouseX, mMouseY, moduleArea.x + moduleArea.w / 2, moduleArea.y + moduleArea.h / 2, Color(255, 0,0), Color(0,255,0));
	}
}

bool SynthGrid::onEvent(SDL_Event& event)
{
	if (mModal != NULL)
	{
		return mModal->onEvent(event);
	}

	if (event.type == SDL_MOUSEBUTTONDOWN)
	{
		// Editor base class should probably do this when clicked
		setFocus(this);

		mMouseX = event.button.x / SCALE;
		mMouseY = event.button.y / SCALE;

		int moduleOut, connectorType, connector;

		switch (event.button.button)
		{
			case SDL_BUTTON_LEFT:

				// Left button starts module connection mode or module move mode
				// depending on if the user clicks the connector or just the module
				// area.

				if (pickConnector(event.button.x / SCALE, event.button.y / SCALE, mThisArea, moduleOut, connectorType, connector))
				{
					ModularSynth& modularSynth = getModularSynth();

					if (mMode == CONNECTING_MODULE)
					{
						if ((connectorType == 0 && mToModule == -1) || (connectorType == 1 && mToModule != -1))
						{
							endConnect(moduleOut, connector);
							modularSynth.connectModules(mFromModule, mToModule, mFromOutput, mToInput);
							mSelectedModule = -1;
						}
					}
					else if (mMode == IDLE || mMode == MOVING_MODULE)
					{
						// Abort moving if clicking on connectors

						if (connectorType == 0)
						{
							startConnect(-1, moduleOut, -1, connector);
							modularSynth.detachConnection(moduleOut, 0, connector);
							mSelectedModule = moduleOut;
						}
						else
						{
							startConnect(moduleOut, -1, connector, -1);
							modularSynth.detachConnection(moduleOut, 1, connector);
							mSelectedModule = moduleOut;
						}
					}
				}
				else if (pickModule(event.button.x / SCALE, event.button.y / SCALE, mThisArea, moduleOut, true))
				{
					if (mMode == IDLE)
					{
						const ModularSynth& modularSynth = getModularSynth();
						if (modularSynth.getModule(moduleOut) != NULL)
						{
							startMove(moduleOut);
						}
						else
						{
							if (event.button.clicks == 2)
							{
								showNewModuleDialog();
							}
						}

						mSelectedModule = moduleOut;
					}
					else if (mMode == MOVING_MODULE)
					{
						endMove(moduleOut);
						mSelectedModule = -1;
					}
				}

				break;

			case SDL_BUTTON_RIGHT:
				if (mMode == CONNECTING_MODULE || mMode == MOVING_MODULE)
				{
					// Abort connecting/moving
					mMode = IDLE;
				}
				break;
		}

		return true;
	}
	else if (event.type == SDL_KEYDOWN)
	{
		switch (event.key.keysym.sym)
		{
			case SDLK_DELETE:
				ModularSynth& modularSynth = getModularSynth();
				if (modularSynth.getModule(mSelectedModule) != NULL)
				{
					modularSynth.removeModule(mSelectedModule);
					mMode = IDLE;
				}

				break;
		}
	}
	else if (event.type == SDL_MOUSEMOTION)
	{
		mMouseX = event.motion.x / SCALE;
		mMouseY = event.motion.y / SCALE;

		if (mMode == CONNECTING_MODULE)
			setDirty(true);
	}

	return false;
}

void SynthGrid::showNewModuleDialog()
{
	mModuleSelector->populate(getModularSynth());
	setModal(mModuleSelector);
	setFocus(mModuleSelector);
}

void SynthGrid::onFileSelectorEvent(const Editor& moduleSelector, bool accept)
{
	if (accept)
	{
		getModularSynth().addModule(mSelectedModule, static_cast<const ModuleSelector&>(moduleSelector).getSelectedModuleId());
	}

	setModal(NULL);
}

ModularSynth& SynthGrid::getModularSynth()
{
	return static_cast<ModularSynth&>(mSynth.getOscillator(0));
}

const ModularSynth& SynthGrid::getModularSynth() const
{
	return static_cast<const ModularSynth&>(mSynth.getOscillator(0));
}
<commit_msg>Making a new connection will edit an existing connection if possible<commit_after>#include "SynthGrid.h"
#include "SDL.h"
#include "ISynth.h"
#include "Renderer.h"
#include "ModularSynth.h"
#include "SynthModule.h"
#include "ModuleSelector.h"
#include "Color.h"
#include <cstdlib>
#include <cstdio>
#include <cmath>

SynthGrid::SynthGrid(EditorState& editorState, ISynth& synth)
	:Editor(editorState, true), mSynth(synth), mMode(IDLE), mSelectedModule(-1)
{
	mModuleSelector = new ModuleSelector(editorState);
}

void SynthGrid::drawWire(Renderer& renderer, int x1, int y1, int x2, int y2, const Color& color1, const Color& color2) const
{
	const int spacing = 8;
	int dx = x1 - x2, dy = y1 - y2;
	int d = sqrt(dx * dx + dy * dy) / spacing;

	if (d == 0)
		return;

	int px = x1, py = y1;

	for (int i = 1 ; i <= d ; ++i)
	{
		int x = (x2 - x1) * i / d + x1;
		int y = (y2 - y1) * i / d + y1 + sin((float)i/d * M_PI) * 16;

		renderer.renderLine(px, py, x, y + 1, Color(0,0,0));
		renderer.renderLine(px, py, x, y, i % 2 ? color1 : color2);

		px = x;
		py = y;
	}
}

void SynthGrid::startMove(int module)
{
	// Don't start moving from empty slot

	if (getModularSynth().getModule(module) == NULL)
		return;

	mMode = MOVING_MODULE;
	mFromModule = module;
}

void SynthGrid::endMove(int targetModule)
{
	if (mMode != MOVING_MODULE)
		return;

	mMode = IDLE;

	getModularSynth().swapModules(mFromModule, targetModule);
}

void SynthGrid::startConnect(int fromModule, int toModule, int fromOutput, int toInput)
{
	mMode = CONNECTING_MODULE;
	mFromModule = fromModule;
	mToModule = toModule;
	mFromOutput = fromOutput;
	mToInput = toInput;

	const ModularSynth& modularSynth = getModularSynth();

	for (int i = 0 ; i < modularSynth.getNumConnections() ; ++i)
	{
		const SynthConnection& connection = modularSynth.getConnection(i);

		if (connection.fromModule == fromModule && connection.fromOutput == fromOutput)
		{
			mFromModule = -1;
			mFromOutput = -1;
			mToModule = connection.toModule;
			mToInput = connection.toInput;
			break;
		}
		else if (connection.toModule == toModule && connection.toInput == toInput)
		{
			mFromModule = connection.fromModule;
			mFromOutput = connection.fromOutput;
			mToModule = -1;
			mToInput = -1;
			break;
		}
	}
}

void SynthGrid::endConnect(int module, int connector)
{
	if (mMode != CONNECTING_MODULE)
		return;

	mMode = IDLE;

	if (mFromModule != -1)
	{
		mToModule = module;
		mToInput = connector;
	}
	else
	{
		mFromModule = module;
		mFromOutput = connector;
	}
}

SDL_Rect SynthGrid::getConnectorArea(int moduleIndex, int type, int connectorIndex, const SDL_Rect& parent) const
{
	const SynthModule* module = getModularSynth().getModule(moduleIndex);
	const int connectorSize = 8;

	SDL_Rect area = {0,0,connectorSize,connectorSize};

	if (module)
	{
		SDL_Rect moduleArea = getModuleArea(moduleIndex, parent);
		int c;

		if (type == 0)
			c = module->getNumInputs();
		else
			c = module->getNumOutputs();

		area.x = moduleArea.x + moduleArea.w / 2 + (connectorIndex - c / 2) * moduleArea.w / c - connectorSize / 2;
		area.y = moduleArea.y;

		if (type == 1)
			area.y += moduleArea.h - area.h - 2;
		else
			area.y += 2;
	}

	return area;
}

SDL_Rect SynthGrid::getModuleArea(int index, const SDL_Rect& parent) const
{
	SDL_Rect area = {0, 0, parent.w / gridWidth, parent.h / gridHeight};

	area.x = parent.x + area.w * (index % gridWidth);
	area.y = parent.y + area.h * (index / gridHeight);

	return area;
}

bool SynthGrid::pickModule(int x, int y, const SDL_Rect& area, int& moduleOut, bool includeEmpty)
{
	const ModularSynth& modularSynth = getModularSynth();

	for (int index = 0 ; index < ModularSynth::maxModules ; ++index)
	{
		const SynthModule *module = modularSynth.getModule(index);

		if (includeEmpty || module != NULL)
		{
			SDL_Rect moduleArea = getModuleArea(index, area);
			SDL_Point point = {x,y};

			if (pointInRect(point, moduleArea))
			{
				moduleOut = index;
				return true;
			}
		}
	}

	return false;
}

bool SynthGrid::pickConnector(int x, int y, const SDL_Rect& area, int& moduleOut, int& connectorType, int& connector)
{
	if (!pickModule(x, y, area, moduleOut))
		return false;

	const ModularSynth& modularSynth = getModularSynth();
	const SynthModule *module = modularSynth.getModule(moduleOut);
	SDL_Point point = {x,y};

	for (connectorType = 0 ; connectorType <= 1 ; connectorType++)
	{
		int c;

		if (connectorType == 0)
			c = module->getNumInputs();
		else
			c = module->getNumOutputs();

		for (int i = 0 ; i < c ; ++i)
		{
			if (pointInRect(point, getConnectorArea(moduleOut, connectorType, i, area)))
			{
				connector = i;
				return true;
			}
		}
	}

	return false;
}

void SynthGrid::onDraw(Renderer& renderer, const SDL_Rect& area)
{
	setDirty(false);

	if (mModal != NULL)
	{
		//mModal->onDraw(renderer, area);
		return;
	}

	renderer.clearRect(area, Color(0,0,0));

	const ModularSynth& modularSynth = getModularSynth();

	for (int index = 0 ; index < ModularSynth::maxModules ; ++index)
	{
		const SynthModule *module = modularSynth.getModule(index);

		if (module != NULL)
		{
			SDL_Rect moduleArea = getModuleArea(index, area);
			renderer.drawRect(moduleArea, mSelectedModule == index ? Color(255,0,0) : Color(255,255,255));

			SDL_Rect textArea = {moduleArea.x + 2, moduleArea.y + moduleArea.h / 2 - 4, 100, 100};

			renderer.renderText(textArea, Color(255,255,255), module->getName());

			for (int connectorType = 0 ; connectorType <= 1 ; connectorType++)
			{
				int c;

				if (connectorType == 0)
					c = module->getNumInputs();
				else
					c = module->getNumOutputs();

				for (int i = 0 ; i < c ; ++i)
				{
					renderer.drawRect(getConnectorArea(index, connectorType, i, area), Color(255,255,0));
				}
			}
		}
	}

	for (int i = 0 ; i < modularSynth.getNumConnections() ; ++i)
	{
		const SynthConnection& connection = modularSynth.getConnection(i);
		SDL_Rect fromModuleArea = getConnectorArea(connection.fromModule, 1, connection.fromOutput, area);
		SDL_Rect toModuleArea = getConnectorArea(connection.toModule, 0, connection.toInput, area);
		drawWire(renderer, fromModuleArea.x + fromModuleArea.w / 2, fromModuleArea.y + fromModuleArea.h / 2, toModuleArea.x + toModuleArea.w / 2, toModuleArea.y + toModuleArea.h / 2, Color(255,0,0), Color(0,255,0));
	}

	if (mMode == CONNECTING_MODULE)
	{
		SDL_Rect moduleArea;
		if (mFromModule != -1)
		{
			moduleArea = getConnectorArea(mFromModule, 1, mFromOutput, area);
		}
		else
		{
			moduleArea = getConnectorArea(mToModule, 0, mToInput, area);
		}

		drawWire(renderer, mMouseX, mMouseY, moduleArea.x + moduleArea.w / 2, moduleArea.y + moduleArea.h / 2, Color(255, 0,0), Color(0,255,0));
	}
}

bool SynthGrid::onEvent(SDL_Event& event)
{
	if (mModal != NULL)
	{
		return mModal->onEvent(event);
	}

	if (event.type == SDL_MOUSEBUTTONDOWN)
	{
		// Editor base class should probably do this when clicked
		setFocus(this);

		mMouseX = event.button.x / SCALE;
		mMouseY = event.button.y / SCALE;

		int moduleOut, connectorType, connector;

		switch (event.button.button)
		{
			case SDL_BUTTON_LEFT:

				// Left button starts module connection mode or module move mode
				// depending on if the user clicks the connector or just the module
				// area.

				if (pickConnector(event.button.x / SCALE, event.button.y / SCALE, mThisArea, moduleOut, connectorType, connector))
				{
					ModularSynth& modularSynth = getModularSynth();

					if (mMode == CONNECTING_MODULE)
					{
						if ((connectorType == 0 && mToModule == -1) || (connectorType == 1 && mToModule != -1))
						{
							endConnect(moduleOut, connector);
							modularSynth.connectModules(mFromModule, mToModule, mFromOutput, mToInput);
							mSelectedModule = -1;
						}
					}
					else if (mMode == IDLE || mMode == MOVING_MODULE)
					{
						// Abort moving if clicking on connectors

						if (connectorType == 0)
						{
							startConnect(-1, moduleOut, -1, connector);
							modularSynth.detachConnection(moduleOut, 0, connector);
							mSelectedModule = moduleOut;
						}
						else
						{
							startConnect(moduleOut, -1, connector, -1);
							modularSynth.detachConnection(moduleOut, 1, connector);
							mSelectedModule = moduleOut;
						}
					}
				}
				else if (pickModule(event.button.x / SCALE, event.button.y / SCALE, mThisArea, moduleOut, true))
				{
					if (mMode == IDLE)
					{
						const ModularSynth& modularSynth = getModularSynth();
						if (modularSynth.getModule(moduleOut) != NULL)
						{
							startMove(moduleOut);
						}
						else
						{
							if (event.button.clicks == 2)
							{
								showNewModuleDialog();
							}
						}

						mSelectedModule = moduleOut;
					}
					else if (mMode == MOVING_MODULE)
					{
						endMove(moduleOut);
						mSelectedModule = -1;
					}
				}

				break;

			case SDL_BUTTON_RIGHT:
				if (mMode == CONNECTING_MODULE || mMode == MOVING_MODULE)
				{
					// Abort connecting/moving
					mMode = IDLE;
				}
				break;
		}

		return true;
	}
	else if (event.type == SDL_KEYDOWN)
	{
		switch (event.key.keysym.sym)
		{
			case SDLK_DELETE:
				ModularSynth& modularSynth = getModularSynth();
				if (modularSynth.getModule(mSelectedModule) != NULL)
				{
					modularSynth.removeModule(mSelectedModule);
					mMode = IDLE;
				}

				break;
		}
	}
	else if (event.type == SDL_MOUSEMOTION)
	{
		mMouseX = event.motion.x / SCALE;
		mMouseY = event.motion.y / SCALE;

		if (mMode == CONNECTING_MODULE)
			setDirty(true);
	}

	return false;
}

void SynthGrid::showNewModuleDialog()
{
	mModuleSelector->populate(getModularSynth());
	setModal(mModuleSelector);
	setFocus(mModuleSelector);
}

void SynthGrid::onFileSelectorEvent(const Editor& moduleSelector, bool accept)
{
	if (accept)
	{
		getModularSynth().addModule(mSelectedModule, static_cast<const ModuleSelector&>(moduleSelector).getSelectedModuleId());
	}

	setModal(NULL);
}

ModularSynth& SynthGrid::getModularSynth()
{
	return static_cast<ModularSynth&>(mSynth.getOscillator(0));
}

const ModularSynth& SynthGrid::getModularSynth() const
{
	return static_cast<const ModularSynth&>(mSynth.getOscillator(0));
}
<|endoftext|>


<commit_before>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015-2018 Hugo Beauzée-Luyssen, Videolabs, VideoLAN
 *
 * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
 *
 * This program 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 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser 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.
 *****************************************************************************/

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include "Thumbnail.h"
#include "utils/File.h"
#include "utils/Filename.h"
#include "Album.h"
#include "Artist.h"
#include "Media.h"

namespace medialibrary
{

const std::string Thumbnail::Table::Name = "Thumbnail";
const std::string Thumbnail::Table::PrimaryKeyColumn = "id_thumbnail";
int64_t Thumbnail::*const Thumbnail::Table::PrimaryKey = &Thumbnail::m_id;
const std::string Thumbnail::LinkingTable::Name = "ThumbnailLinking";

const std::string Thumbnail::EmptyMrl;

Thumbnail::Thumbnail( MediaLibraryPtr ml, sqlite::Row& row )
    : m_ml( ml )
    , m_id( row.extract<decltype(m_id)>() )
    , m_mrl( row.extract<decltype(m_mrl)>() )
    , m_origin( row.extract<decltype(m_origin)>() )
    , m_sizeType( row.extract<decltype(m_sizeType)>() )
    , m_isOwned( row.extract<decltype(m_isOwned)>() )
    , m_sharedCounter( row.extract<decltype(m_sharedCounter)>() )
{
    assert( row.hasRemainingColumns() == false );
    // If the thumbnail was generated by the medialibrary, we store it as a
    // relative path, from the user provided workspace
    if ( m_isOwned == true )
    {
        auto thumbnailDirMrl = utils::file::toMrl( m_ml->thumbnailPath() );
        assert( m_mrl.find( thumbnailDirMrl ) == std::string::npos );
        // We expect the relative part of the mrl to be already encoded, however
        // the path to the thumbnail directory is stored as a file path
        m_mrl = thumbnailDirMrl + m_mrl;
    }
}

Thumbnail::Thumbnail( MediaLibraryPtr ml, std::string mrl,
                      Thumbnail::Origin origin, ThumbnailSizeType sizeType, bool isOwned )
    : m_ml( ml )
    , m_id( 0 )
    , m_mrl( std::move( mrl ) )
    , m_origin( origin )
    , m_sizeType( sizeType )
    , m_isOwned( isOwned )
    , m_sharedCounter( 0 )
{
    // Store the mrl as is, and fiddle with it upon insertion as we only care
    // about storing a relative path in db, but we want to return the mrl as it
    // was given, ie. as an absolute mrl.
    assert( ( m_mrl.empty() == true && isOwned == false ) ||
            utils::file::scheme( m_mrl ).empty() == false );
}

int64_t Thumbnail::id() const
{
    return m_id;
}

const std::string& Thumbnail::mrl() const
{
    assert( isValid() == true );
    return m_mrl;
}

bool Thumbnail::update( std::string mrl, bool isOwned )
{
    if ( m_mrl == mrl && isOwned == m_isOwned )
        return true;
    std::string storedMrl;
    if ( isOwned )
        storedMrl = toRelativeMrl( mrl );
    else
        storedMrl = mrl;
    static const std::string req = "UPDATE " + Thumbnail::Table::Name +
            " SET mrl = ?, is_generated = ? WHERE id_thumbnail = ?";
    if( sqlite::Tools::executeUpdate( m_ml->getConn(), req, storedMrl,
                                      isOwned, m_id ) == false )
        return false;
    m_mrl = std::move( mrl );
    m_isOwned = isOwned;
    return true;
}

bool Thumbnail::updateLinkRecord( int64_t entityId, EntityType type,
                                  Thumbnail::Origin origin )
{
    const std::string req = "UPDATE " + LinkingTable::Name +
        " SET thumbnail_id = ?, origin = ?"
        " WHERE entity_id = ? AND entity_type = ? AND size_type = ?";
    // This needs to be run in a transaction, as we insert the new thumbnail
    // record or update the linked thumbnail
    assert( sqlite::Transaction::transactionInProgress() == true );
    if ( sqlite::Tools::executeUpdate( m_ml->getConn(), req, m_id, origin,
                                       entityId, type, m_sizeType ) == false )
        return false;
    m_origin = origin;
    return true;
}

void Thumbnail::insertLinkRecord( int64_t entityId, EntityType type,
                                  Thumbnail::Origin origin )
{
    const std::string req = "INSERT INTO " + LinkingTable::Name +
            " (entity_id, entity_type, size_type, thumbnail_id, origin)"
            " VALUES(?, ?, ?, ?, ?)";
    sqlite::Tools::executeInsert( m_ml->getConn(), req, entityId, type,
                                  m_sizeType, m_id, origin );
    m_sharedCounter++;
}

void Thumbnail::unlinkThumbnail( int64_t entityId, EntityType type )
{
    const std::string req = "DELETE FROM " + LinkingTable::Name +
            " WHERE entity_id = ? AND entity_type = ? AND size_type = ?";
    if ( sqlite::Tools::executeDelete( m_ml->getConn(), req, entityId, type,
                                       m_sizeType ) == false )
        return;
    --m_sharedCounter;
}

bool Thumbnail::isValid() const
{
    return m_mrl.empty() == false;
}

Thumbnail::Origin Thumbnail::origin() const
{
    return m_origin;
}

bool Thumbnail::isOwned() const
{
    return m_isOwned;
}

bool Thumbnail::isShared() const
{
    assert( m_sharedCounter != 0 || m_id == 0 );
    return m_sharedCounter > 1;
}

ThumbnailSizeType Thumbnail::sizeType() const
{
    return m_sizeType;
}

bool Thumbnail::isFailureRecord() const
{
    return m_mrl.empty() == true &&
           m_origin == Origin::Media &&
            m_isOwned == false;
}

void Thumbnail::relocate()
{
    // There is no point in relocating a failure record.
    assert( isValid() == true );

    auto originalMrl = m_mrl;
    auto destPath = m_ml->thumbnailPath() +
                    std::to_string( m_id ) + "." +
                    utils::file::extension( originalMrl );
    std::string localPath;
    try
    {
        localPath = utils::file::toLocalPath( originalMrl );
    }
    catch ( const std::exception& ex )
    {
        LOG_ERROR( "Failed to relocate thumbnail ", originalMrl, ": ", ex.what() );
        return;
    }
    if ( utils::fs::copy( localPath, destPath ) == true )
    {
        auto destMrl = utils::file::toMrl( destPath );
        if ( sqlite::Tools::withRetries( 3, [this, &destMrl]() {
                return update( destMrl, true );
            }) == false )
        {
            utils::fs::remove( destPath );
        }
    }
}

std::shared_ptr<Thumbnail>
Thumbnail::updateOrReplace( MediaLibraryPtr ml,
                            std::shared_ptr<Thumbnail> oldThumbnail,
                            std::shared_ptr<Thumbnail> newThumbnail,
                            int64_t entityId, EntityType entityType )
{
    std::shared_ptr<Thumbnail> res;
    std::unique_ptr<sqlite::Transaction> t;
    if ( sqlite::Transaction::transactionInProgress() == false )
        t = ml->getConn()->newTransaction();
    /**
     * We are trying to assign the values from newThumbnail to oldThumbnail.
     * Multiple cases exist:
     * - oldThumbnail is a nullptr
     * - newThumbnail is not inserted, and is only a temporary object to provide
     *   the values that the user wishes to assign
     * - newThumbnail already exists
     *
     * If oldThumbnail is a nullptr, simply insert newThumbnail in DB if needed
     * and insert a linking record.
     *
     * If newThumbnail already exists, that's easy, we update the linking record
     * if any, or insert a new one.
     *
     * If oldThumbnail was already inserted, we might be able to update the
     * Thumbnail record itself, but only if the thumbnail isn't shared, as changing
     * it would change the thumbnail for other entities. So if the thumbnail isn't
     * shared, we update it, otherwise we update the linking record, which means
     * we may have to insert the newThumbnail if it hasn't been already
     */
    if ( oldThumbnail == nullptr )
    {
        if ( newThumbnail->id() == 0 )
        {
            if ( newThumbnail->insert() == 0 )
                return nullptr;
        }
        newThumbnail->insertLinkRecord( entityId, entityType, newThumbnail->origin() );
        res = std::move( newThumbnail );
    }
    else if ( newThumbnail->id() != 0 )
    {
        newThumbnail->updateLinkRecord( entityId, entityType, newThumbnail->origin() );
        --oldThumbnail->m_sharedCounter;
        ++newThumbnail->m_sharedCounter;
        res = std::move( newThumbnail );
    }
    else
    {
        if ( oldThumbnail->id() != 0 && oldThumbnail->isShared() == false )
        {
            if ( oldThumbnail->update( newThumbnail->mrl(),
                                       newThumbnail->isOwned() ) == false )
                return nullptr;
            if ( oldThumbnail->origin() != newThumbnail->origin() )
            {
                if ( oldThumbnail->updateLinkRecord( entityId, entityType,
                                                     newThumbnail->origin() ) == false )
                    return nullptr;
            }
            res = oldThumbnail;
        }
        else
        {
            assert( oldThumbnail != nullptr );
            if ( newThumbnail->insert() == 0 )
                return nullptr;
            if ( newThumbnail->updateLinkRecord( entityId, entityType,
                                                 newThumbnail->origin() ) == false )
                return nullptr;
            --oldThumbnail->m_sharedCounter;
            ++newThumbnail->m_sharedCounter;
            res = newThumbnail;
        }
    }

    if ( t != nullptr )
        t->commit();
    return res;
}

void Thumbnail::createTable( sqlite::Connection* dbConnection )
{
    const std::string reqs[] = {
        #include "database/tables/Thumbnail_v18.sql"
    };
    for ( const auto& req : reqs )
        sqlite::Tools::executeRequest( dbConnection, req );
}

void Thumbnail::createTriggers( sqlite::Connection* dbConnection )
{
    const std::string reqs[] = {
        #include "database/tables/Thumbnail_triggers_v18.sql"
    };
    for ( const auto& req : reqs )
        sqlite::Tools::executeRequest( dbConnection, req );
}

std::shared_ptr<Thumbnail> Thumbnail::create( MediaLibraryPtr ml, std::string mrl,
                                              Thumbnail::Origin origin,
                                              ThumbnailSizeType sizeType, bool isOwned )
{
    static const std::string req = "INSERT INTO " + Thumbnail::Table::Name +
            "(mrl, is_generated) VALUES(?,?)";
    auto self = std::make_shared<Thumbnail>( ml, mrl, origin, sizeType, isOwned );
    assert( self->isValid() == true || self->isFailureRecord() == true );
    if ( DatabaseHelpers<Thumbnail>::insert( ml, self, req,
                                             sqlite::NullableString{ mrl },
                                             isOwned ) == false )
        return nullptr;
    return self;
}

std::shared_ptr<Thumbnail> Thumbnail::fetch( MediaLibraryPtr ml, EntityType type,
                                             int64_t entityId, ThumbnailSizeType sizeType )
{
    std::string req = "SELECT t.id_thumbnail, t.mrl, ent.origin, ent.size_type,"
            "t.is_generated, t.shared_counter "
            "FROM " + Table::Name + " t "
            "INNER JOIN " + LinkingTable::Name + " ent "
                "ON t.id_thumbnail = ent.thumbnail_id "
            "WHERE ent.entity_id = ? AND ent.entity_type = ? AND ent.size_type = ?";
    return DatabaseHelpers<Thumbnail>::fetch( ml, req, entityId, type, sizeType );
}

int64_t Thumbnail::insert()
{
    assert( m_id == 0 );
    static const std::string req = "INSERT INTO " + Thumbnail::Table::Name +
            "(mrl, is_generated) VALUES(?,?)";
    auto pKey = sqlite::Tools::executeInsert( m_ml->getConn(), req,
                            m_isOwned == true ? toRelativeMrl( m_mrl ) : m_mrl,
                            m_isOwned );
    if ( pKey == 0 )
        return 0;
    m_id = pKey;
    return m_id;
}

bool Thumbnail::deleteFailureRecords(MediaLibraryPtr ml)
{
    static const std::string req = "DELETE FROM " + Table::Name +
                                   " WHERE mrl IS NULL";
    return sqlite::Tools::executeDelete( ml->getConn(), req );
}

std::string Thumbnail::path( MediaLibraryPtr ml, int64_t thumbnailId )
{
    return ml->thumbnailPath() + std::to_string( thumbnailId ) + ".jpg";
}

std::string Thumbnail::toRelativeMrl( const std::string& absoluteMrl )
{
    // We can still
    if ( absoluteMrl.empty() == true )
    {
        assert( isFailureRecord() == true );
        return absoluteMrl;
    }
    // Ensure the thumbnail mrl is an absolute mrl and contained in the
    // thumbnail directory.
    assert( utils::file::schemeIs( "file://", absoluteMrl ) == true );
    auto thumbnailDirMrl = utils::file::toMrl( m_ml->thumbnailPath() );
    assert( absoluteMrl.find( thumbnailDirMrl ) == 0 );
    return utils::file::removePath( absoluteMrl, thumbnailDirMrl );
}

}
<commit_msg>Thumbnail: Avoid updating an outdated thumbnail<commit_after>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015-2018 Hugo Beauzée-Luyssen, Videolabs, VideoLAN
 *
 * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
 *
 * This program 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 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser 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.
 *****************************************************************************/

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include "Thumbnail.h"
#include "utils/File.h"
#include "utils/Filename.h"
#include "Album.h"
#include "Artist.h"
#include "Media.h"

namespace medialibrary
{

const std::string Thumbnail::Table::Name = "Thumbnail";
const std::string Thumbnail::Table::PrimaryKeyColumn = "id_thumbnail";
int64_t Thumbnail::*const Thumbnail::Table::PrimaryKey = &Thumbnail::m_id;
const std::string Thumbnail::LinkingTable::Name = "ThumbnailLinking";

const std::string Thumbnail::EmptyMrl;

Thumbnail::Thumbnail( MediaLibraryPtr ml, sqlite::Row& row )
    : m_ml( ml )
    , m_id( row.extract<decltype(m_id)>() )
    , m_mrl( row.extract<decltype(m_mrl)>() )
    , m_origin( row.extract<decltype(m_origin)>() )
    , m_sizeType( row.extract<decltype(m_sizeType)>() )
    , m_isOwned( row.extract<decltype(m_isOwned)>() )
    , m_sharedCounter( row.extract<decltype(m_sharedCounter)>() )
{
    assert( row.hasRemainingColumns() == false );
    // If the thumbnail was generated by the medialibrary, we store it as a
    // relative path, from the user provided workspace
    if ( m_isOwned == true )
    {
        auto thumbnailDirMrl = utils::file::toMrl( m_ml->thumbnailPath() );
        assert( m_mrl.find( thumbnailDirMrl ) == std::string::npos );
        // We expect the relative part of the mrl to be already encoded, however
        // the path to the thumbnail directory is stored as a file path
        m_mrl = thumbnailDirMrl + m_mrl;
    }
}

Thumbnail::Thumbnail( MediaLibraryPtr ml, std::string mrl,
                      Thumbnail::Origin origin, ThumbnailSizeType sizeType, bool isOwned )
    : m_ml( ml )
    , m_id( 0 )
    , m_mrl( std::move( mrl ) )
    , m_origin( origin )
    , m_sizeType( sizeType )
    , m_isOwned( isOwned )
    , m_sharedCounter( 0 )
{
    // Store the mrl as is, and fiddle with it upon insertion as we only care
    // about storing a relative path in db, but we want to return the mrl as it
    // was given, ie. as an absolute mrl.
    assert( ( m_mrl.empty() == true && isOwned == false ) ||
            utils::file::scheme( m_mrl ).empty() == false );
}

int64_t Thumbnail::id() const
{
    return m_id;
}

const std::string& Thumbnail::mrl() const
{
    assert( isValid() == true );
    return m_mrl;
}

bool Thumbnail::update( std::string mrl, bool isOwned )
{
    if ( m_mrl == mrl && isOwned == m_isOwned )
        return true;
    std::string storedMrl;
    if ( isOwned )
        storedMrl = toRelativeMrl( mrl );
    else
        storedMrl = mrl;
    // Also include the current generated state to the request, in case this update
    // request came while the thumbnailer was also generating a thumbnail
    static const std::string req = "UPDATE " + Thumbnail::Table::Name +
            " SET mrl = ?, is_generated = ? "
            "WHERE id_thumbnail = ? AND is_generated = ?";
    if( sqlite::Tools::executeUpdate( m_ml->getConn(), req, storedMrl,
                                      isOwned, m_id, m_isOwned ) == false )
        return false;
    m_mrl = std::move( mrl );
    m_isOwned = isOwned;
    return true;
}

bool Thumbnail::updateLinkRecord( int64_t entityId, EntityType type,
                                  Thumbnail::Origin origin )
{
    const std::string req = "UPDATE " + LinkingTable::Name +
        " SET thumbnail_id = ?, origin = ?"
        " WHERE entity_id = ? AND entity_type = ? AND size_type = ?";
    // This needs to be run in a transaction, as we insert the new thumbnail
    // record or update the linked thumbnail
    assert( sqlite::Transaction::transactionInProgress() == true );
    if ( sqlite::Tools::executeUpdate( m_ml->getConn(), req, m_id, origin,
                                       entityId, type, m_sizeType ) == false )
        return false;
    m_origin = origin;
    return true;
}

void Thumbnail::insertLinkRecord( int64_t entityId, EntityType type,
                                  Thumbnail::Origin origin )
{
    const std::string req = "INSERT INTO " + LinkingTable::Name +
            " (entity_id, entity_type, size_type, thumbnail_id, origin)"
            " VALUES(?, ?, ?, ?, ?)";
    sqlite::Tools::executeInsert( m_ml->getConn(), req, entityId, type,
                                  m_sizeType, m_id, origin );
    m_sharedCounter++;
}

void Thumbnail::unlinkThumbnail( int64_t entityId, EntityType type )
{
    const std::string req = "DELETE FROM " + LinkingTable::Name +
            " WHERE entity_id = ? AND entity_type = ? AND size_type = ?";
    if ( sqlite::Tools::executeDelete( m_ml->getConn(), req, entityId, type,
                                       m_sizeType ) == false )
        return;
    --m_sharedCounter;
}

bool Thumbnail::isValid() const
{
    return m_mrl.empty() == false;
}

Thumbnail::Origin Thumbnail::origin() const
{
    return m_origin;
}

bool Thumbnail::isOwned() const
{
    return m_isOwned;
}

bool Thumbnail::isShared() const
{
    assert( m_sharedCounter != 0 || m_id == 0 );
    return m_sharedCounter > 1;
}

ThumbnailSizeType Thumbnail::sizeType() const
{
    return m_sizeType;
}

bool Thumbnail::isFailureRecord() const
{
    return m_mrl.empty() == true &&
           m_origin == Origin::Media &&
            m_isOwned == false;
}

void Thumbnail::relocate()
{
    // There is no point in relocating a failure record.
    assert( isValid() == true );

    auto originalMrl = m_mrl;
    auto destPath = m_ml->thumbnailPath() +
                    std::to_string( m_id ) + "." +
                    utils::file::extension( originalMrl );
    std::string localPath;
    try
    {
        localPath = utils::file::toLocalPath( originalMrl );
    }
    catch ( const std::exception& ex )
    {
        LOG_ERROR( "Failed to relocate thumbnail ", originalMrl, ": ", ex.what() );
        return;
    }
    if ( utils::fs::copy( localPath, destPath ) == true )
    {
        auto destMrl = utils::file::toMrl( destPath );
        if ( sqlite::Tools::withRetries( 3, [this, &destMrl]() {
                return update( destMrl, true );
            }) == false )
        {
            utils::fs::remove( destPath );
        }
    }
}

std::shared_ptr<Thumbnail>
Thumbnail::updateOrReplace( MediaLibraryPtr ml,
                            std::shared_ptr<Thumbnail> oldThumbnail,
                            std::shared_ptr<Thumbnail> newThumbnail,
                            int64_t entityId, EntityType entityType )
{
    std::shared_ptr<Thumbnail> res;
    std::unique_ptr<sqlite::Transaction> t;
    if ( sqlite::Transaction::transactionInProgress() == false )
        t = ml->getConn()->newTransaction();
    /**
     * We are trying to assign the values from newThumbnail to oldThumbnail.
     * Multiple cases exist:
     * - oldThumbnail is a nullptr
     * - newThumbnail is not inserted, and is only a temporary object to provide
     *   the values that the user wishes to assign
     * - newThumbnail already exists
     *
     * If oldThumbnail is a nullptr, simply insert newThumbnail in DB if needed
     * and insert a linking record.
     *
     * If newThumbnail already exists, that's easy, we update the linking record
     * if any, or insert a new one.
     *
     * If oldThumbnail was already inserted, we might be able to update the
     * Thumbnail record itself, but only if the thumbnail isn't shared, as changing
     * it would change the thumbnail for other entities. So if the thumbnail isn't
     * shared, we update it, otherwise we update the linking record, which means
     * we may have to insert the newThumbnail if it hasn't been already
     */
    if ( oldThumbnail == nullptr )
    {
        if ( newThumbnail->id() == 0 )
        {
            if ( newThumbnail->insert() == 0 )
                return nullptr;
        }
        newThumbnail->insertLinkRecord( entityId, entityType, newThumbnail->origin() );
        res = std::move( newThumbnail );
    }
    else if ( newThumbnail->id() != 0 )
    {
        newThumbnail->updateLinkRecord( entityId, entityType, newThumbnail->origin() );
        --oldThumbnail->m_sharedCounter;
        ++newThumbnail->m_sharedCounter;
        res = std::move( newThumbnail );
    }
    else
    {
        if ( oldThumbnail->id() != 0 && oldThumbnail->isShared() == false )
        {
            if ( oldThumbnail->update( newThumbnail->mrl(),
                                       newThumbnail->isOwned() ) == false )
                return nullptr;
            if ( oldThumbnail->origin() != newThumbnail->origin() )
            {
                if ( oldThumbnail->updateLinkRecord( entityId, entityType,
                                                     newThumbnail->origin() ) == false )
                    return nullptr;
            }
            res = oldThumbnail;
        }
        else
        {
            assert( oldThumbnail != nullptr );
            if ( newThumbnail->insert() == 0 )
                return nullptr;
            if ( newThumbnail->updateLinkRecord( entityId, entityType,
                                                 newThumbnail->origin() ) == false )
                return nullptr;
            --oldThumbnail->m_sharedCounter;
            ++newThumbnail->m_sharedCounter;
            res = newThumbnail;
        }
    }

    if ( t != nullptr )
        t->commit();
    return res;
}

void Thumbnail::createTable( sqlite::Connection* dbConnection )
{
    const std::string reqs[] = {
        #include "database/tables/Thumbnail_v18.sql"
    };
    for ( const auto& req : reqs )
        sqlite::Tools::executeRequest( dbConnection, req );
}

void Thumbnail::createTriggers( sqlite::Connection* dbConnection )
{
    const std::string reqs[] = {
        #include "database/tables/Thumbnail_triggers_v18.sql"
    };
    for ( const auto& req : reqs )
        sqlite::Tools::executeRequest( dbConnection, req );
}

std::shared_ptr<Thumbnail> Thumbnail::create( MediaLibraryPtr ml, std::string mrl,
                                              Thumbnail::Origin origin,
                                              ThumbnailSizeType sizeType, bool isOwned )
{
    static const std::string req = "INSERT INTO " + Thumbnail::Table::Name +
            "(mrl, is_generated) VALUES(?,?)";
    auto self = std::make_shared<Thumbnail>( ml, mrl, origin, sizeType, isOwned );
    assert( self->isValid() == true || self->isFailureRecord() == true );
    if ( DatabaseHelpers<Thumbnail>::insert( ml, self, req,
                                             sqlite::NullableString{ mrl },
                                             isOwned ) == false )
        return nullptr;
    return self;
}

std::shared_ptr<Thumbnail> Thumbnail::fetch( MediaLibraryPtr ml, EntityType type,
                                             int64_t entityId, ThumbnailSizeType sizeType )
{
    std::string req = "SELECT t.id_thumbnail, t.mrl, ent.origin, ent.size_type,"
            "t.is_generated, t.shared_counter "
            "FROM " + Table::Name + " t "
            "INNER JOIN " + LinkingTable::Name + " ent "
                "ON t.id_thumbnail = ent.thumbnail_id "
            "WHERE ent.entity_id = ? AND ent.entity_type = ? AND ent.size_type = ?";
    return DatabaseHelpers<Thumbnail>::fetch( ml, req, entityId, type, sizeType );
}

int64_t Thumbnail::insert()
{
    assert( m_id == 0 );
    static const std::string req = "INSERT INTO " + Thumbnail::Table::Name +
            "(mrl, is_generated) VALUES(?,?)";
    auto pKey = sqlite::Tools::executeInsert( m_ml->getConn(), req,
                            m_isOwned == true ? toRelativeMrl( m_mrl ) : m_mrl,
                            m_isOwned );
    if ( pKey == 0 )
        return 0;
    m_id = pKey;
    return m_id;
}

bool Thumbnail::deleteFailureRecords(MediaLibraryPtr ml)
{
    static const std::string req = "DELETE FROM " + Table::Name +
                                   " WHERE mrl IS NULL";
    return sqlite::Tools::executeDelete( ml->getConn(), req );
}

std::string Thumbnail::path( MediaLibraryPtr ml, int64_t thumbnailId )
{
    return ml->thumbnailPath() + std::to_string( thumbnailId ) + ".jpg";
}

std::string Thumbnail::toRelativeMrl( const std::string& absoluteMrl )
{
    // We can still
    if ( absoluteMrl.empty() == true )
    {
        assert( isFailureRecord() == true );
        return absoluteMrl;
    }
    // Ensure the thumbnail mrl is an absolute mrl and contained in the
    // thumbnail directory.
    assert( utils::file::schemeIs( "file://", absoluteMrl ) == true );
    auto thumbnailDirMrl = utils::file::toMrl( m_ml->thumbnailPath() );
    assert( absoluteMrl.find( thumbnailDirMrl ) == 0 );
    return utils::file::removePath( absoluteMrl, thumbnailDirMrl );
}

}
<|endoftext|>


<commit_before>
#include "U8Gettext.h"
#include <Arduino.h>

#define ENSURE(condition) do { if (!(condition)) {Serial.println("ENSURE failed at: "#condition); while(1); } } while(0)
#define SIZE_OF_ARRAY(array) (sizeof((array))/sizeof((array)[0]))
#define PTR_OFFSET_BYTES(ptr, nBytes) ((void*)(((char *)(ptr)) + (nBytes)))
#define PTR_OFFSET_BETWEEN(ptrBegin, ptrEnd) ((char*)(ptrEnd) - (char*)(ptrBegin))

extern const size_t gU8GettextLanguagesLength;
extern const U8GettextLanguage gU8GettextLanguages[];

static const char *
sU8GettextLanguage = "en_US";

static const U8GettextLanguage *
sLanguageInstance = NULL;

const char *U8GettextSetLanguage(const char *language)
{
  const char * oldLanguage = sU8GettextLanguage;

  sU8GettextLanguage = language;
  return oldLanguage;
}

const char *U8GettextGetLanguage()
{
  return sU8GettextLanguage;
}

const char *U8Gettext(const char *str)
{
  // TODO Implementation
  if (!sLanguageInstance) {
    return str;
  }
}
<commit_msg>Implemented a slowest way to find the translation (Just for working)<commit_after>
#include "U8Gettext.h"
#include <Arduino.h>

#define ENSURE(condition) do { if (!(condition)) {Serial.println("ENSURE failed at: "#condition); while(1); } } while(0)
#define SIZE_OF_ARRAY(array) (sizeof((array))/sizeof((array)[0]))
#define PTR_OFFSET_BYTES(ptr, nBytes) ((void*)(((char *)(ptr)) + (nBytes)))
#define PTR_OFFSET_BETWEEN(ptrBegin, ptrEnd) ((char*)(ptrEnd) - (char*)(ptrBegin))

extern const size_t gU8GettextLanguagesLength;
extern const U8GettextLanguage gU8GettextLanguages[];

static const char *
sU8GettextLanguage = "en_US";

static const U8GettextLanguage *
sLanguageInstance = NULL;

const char *U8GettextSetLanguage(const char *language)
{
  const char * oldLanguage = sU8GettextLanguage;

  sU8GettextLanguage = language;
  return oldLanguage;
}

const char *U8GettextGetLanguage()
{
  return sU8GettextLanguage;
}

const char *U8Gettext(const char *str)
{
  uint16_t i = 0;
  const U8GettextTranslation *translation = NULL;

  if (!sLanguageInstance) {
    return str;
  }

  translation = sLanguageInstance->translations;
  for(i = 0; i < sLanguageInstance->translationCount; ++i, ++translation) {
    // Slowest way to find translation
    if(0 == strcmp(str, translation->msgId)) {
      return translation->msgStr;
    }
  }

  // Can't find any translations!
  return str;
}
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2015 - The CM Authors <legal@clickmatcher.com>
 *   All Rights Reserved.
 *
 * This file is CONFIDENTIAL -- Distribution or duplication of this material or
 * the information contained herein is strictly forbidden unless prior written
 * permission is obtained.
 */
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include "fnord-base/io/filerepository.h"
#include "fnord-base/io/fileutil.h"
#include "fnord-base/application.h"
#include "fnord-base/logging.h"
#include "fnord-base/random.h"
#include "fnord-base/thread/eventloop.h"
#include "fnord-base/thread/threadpool.h"
#include "fnord-base/thread/FixedSizeThreadPool.h"
#include "fnord-base/wallclock.h"
#include "fnord-base/VFS.h"
#include "fnord-rpc/ServerGroup.h"
#include "fnord-rpc/RPC.h"
#include "fnord-rpc/RPCClient.h"
#include "fnord-base/cli/flagparser.h"
#include "fnord-json/json.h"
#include "fnord-json/jsonrpc.h"
#include "fnord-http/httprouter.h"
#include "fnord-http/httpserver.h"
#include "fnord-http/VFSFileServlet.h"
#include "fnord-feeds/FeedService.h"
#include "fnord-feeds/RemoteFeedFactory.h"
#include "fnord-feeds/RemoteFeedReader.h"
#include "fnord-dproc/LocalScheduler.h"
#include "fnord-dproc/DispatchService.h"
#include "fnord-base/stats/statsdagent.h"
#include "fnord-sstable/SSTableServlet.h"
#include "fnord-logtable/LogTableServlet.h"
#include "fnord-logtable/TableRepository.h"
#include "fnord-logtable/TableJanitor.h"
#include "fnord-logtable/TableReplication.h"
#include "fnord-afx/ArtifactReplication.h"
#include "fnord-logtable/NumericBoundsSummary.h"
#include "fnord-mdb/MDB.h"
#include "fnord-mdb/MDBUtil.h"
#include "common.h"
#include "schemas.h"
#include "CustomerNamespace.h"
#include "analytics/AnalyticsServlet.h"
#include "analytics/FeedExportApp.h"
#include "analytics/CTRByPositionQuery.h"
#include "analytics/CTRByPageQuery.h"
#include "analytics/CTRByResultItemCategoryQuery.h"
#include "analytics/TopSearchQueriesQuery.h"
#include "analytics/DiscoveryDashboardQuery.h"
#include "analytics/DiscoveryCatalogStatsQuery.h"
#include "analytics/DiscoveryCategoryStatsQuery.h"
#include "analytics/DiscoverySearchStatsQuery.h"
#include "analytics/ECommerceKPIQuery.h"
#include "analytics/AnalyticsQueryEngine.h"
#include "analytics/AnalyticsQueryEngine.h"
#include "analytics/ShopStatsServlet.h"

using namespace fnord;

fnord::thread::EventLoop ev;

int main(int argc, const char** argv) {
  fnord::Application::init();
  fnord::Application::logToStderr();

  fnord::cli::FlagParser flags;

  flags.defineFlag(
      "http_port",
      fnord::cli::FlagParser::T_INTEGER,
      false,
      NULL,
      "8000",
      "Start the public http server on this port",
      "<port>");

  flags.defineFlag(
      "datadir",
      cli::FlagParser::T_STRING,
      true,
      NULL,
      NULL,
      "datadir path",
      "<path>");

  flags.defineFlag(
      "shopstats_table",
      cli::FlagParser::T_STRING,
      true,
      NULL,
      NULL,
      "path",
      "<file>");

  flags.defineFlag(
      "loglevel",
      fnord::cli::FlagParser::T_STRING,
      false,
      NULL,
      "INFO",
      "loglevel",
      "<level>");

  flags.parseArgv(argc, argv);

  Logger::get()->setMinimumLogLevel(
      strToLogLevel(flags.getString("loglevel")));

  /* thread pools */
  fnord::thread::ThreadPool tpool;
  fnord::thread::FixedSizeThreadPool wpool(8);
  wpool.start();

  /* http */
  fnord::http::HTTPRouter http_router;
  fnord::http::HTTPServer http_server(&http_router, &ev);
  http_server.listen(flags.getInt("http_port"));
  http::HTTPConnectionPool http(&ev);

  /* tsdb */
  tsdb::TSDBClient tsdb("http://nue03.prod.fnrd.net:7003/tsdb", &http);

  /* dproc */
  dproc::DispatchService dproc;
  auto local_scheduler = mkRef(new dproc::LocalScheduler());

  /* feed export */
  dproc.registerApp(new FeedExportApp(&tsdb), local_scheduler.get());

  /* stop stats */
  //auto shopstats = cm::ShopStatsTable::open(flags.getString("shopstats_table"));
  //cm::ShopStatsServlet shopstats_servlet(shopstats);
  //http_router.addRouteByPrefixMatch("/shopstats", &shopstats_servlet, &tpool);

  /* analytics core */
  auto dir = flags.getString("datadir");
  cm::AnalyticsQueryEngine analytics(32, dir, &tsdb);
  cm::AnalyticsServlet analytics_servlet(&analytics);
  http_router.addRouteByPrefixMatch("/analytics", &analytics_servlet, &tpool);

  analytics.registerQueryFactory("ctr_by_position", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::CTRByPositionQuery(scan, segments);
  });

  analytics.registerQueryFactory("ctr_by_page", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::CTRByPageQuery(scan, segments);
  });

  analytics.registerQueryFactory("ctr_by_result_item_category", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::CTRByResultItemCategoryQuery(scan, segments);
  });

  analytics.registerQueryFactory("discovery_dashboard", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::DiscoveryDashboardQuery(
        scan,
        segments,
        query.start_time,
        query.end_time);
  });

  analytics.registerQueryFactory("discovery_search_stats", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::DiscoverySearchStatsQuery(
        scan,
        segments,
        query.start_time,
        query.end_time,
        params);
  });

  analytics.registerQueryFactory("discovery_catalog_stats", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::DiscoveryCatalogStatsQuery(
        scan,
        segments,
        query.start_time,
        query.end_time,
        params);
  });

  analytics.registerQueryFactory("discovery_category0_stats", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::DiscoveryCategoryStatsQuery(
        scan,
        segments,
        query.start_time,
        query.end_time,
        "search_queries.category1",
        "search_queries.category1",
        params);
  });

  analytics.registerQueryFactory("discovery_category1_stats", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::DiscoveryCategoryStatsQuery(
        scan,
        segments,
        query.start_time,
        query.end_time,
        "search_queries.category1",
        "search_queries.category2",
        params);
  });

  analytics.registerQueryFactory("discovery_category2_stats", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::DiscoveryCategoryStatsQuery(
        scan,
        segments,
        query.start_time,
        query.end_time,
        "search_queries.category2",
        "search_queries.category3",
        params);
  });

  analytics.registerQueryFactory("discovery_category3_stats", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::DiscoveryCategoryStatsQuery(
        scan,
        segments,
        query.start_time,
        query.end_time,
        "search_queries.category3",
        "search_queries.category3",
        params);
  });

  analytics.registerQueryFactory("top_search_queries", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::TopSearchQueriesQuery(scan, segments, params);
  });

  analytics.registerQueryFactory("ecommerce_dashboard", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::ECommerceKPIQuery(
        scan,
        segments,
        query.start_time,
        query.end_time,
        params);
  });

  ev.run();

  fnord::logInfo("cm.analytics.backend", "Exiting...");
  exit(0);
}

<commit_msg>analyticsservlet...<commit_after>/**
 * Copyright (c) 2015 - The CM Authors <legal@clickmatcher.com>
 *   All Rights Reserved.
 *
 * This file is CONFIDENTIAL -- Distribution or duplication of this material or
 * the information contained herein is strictly forbidden unless prior written
 * permission is obtained.
 */
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include "fnord-base/io/filerepository.h"
#include "fnord-base/io/fileutil.h"
#include "fnord-base/application.h"
#include "fnord-base/logging.h"
#include "fnord-base/random.h"
#include "fnord-base/thread/eventloop.h"
#include "fnord-base/thread/threadpool.h"
#include "fnord-base/thread/FixedSizeThreadPool.h"
#include "fnord-base/wallclock.h"
#include "fnord-base/VFS.h"
#include "fnord-rpc/ServerGroup.h"
#include "fnord-rpc/RPC.h"
#include "fnord-rpc/RPCClient.h"
#include "fnord-base/cli/flagparser.h"
#include "fnord-json/json.h"
#include "fnord-json/jsonrpc.h"
#include "fnord-http/httprouter.h"
#include "fnord-http/httpserver.h"
#include "fnord-http/VFSFileServlet.h"
#include "fnord-feeds/FeedService.h"
#include "fnord-feeds/RemoteFeedFactory.h"
#include "fnord-feeds/RemoteFeedReader.h"
#include "fnord-dproc/LocalScheduler.h"
#include "fnord-dproc/DispatchService.h"
#include "fnord-base/stats/statsdagent.h"
#include "fnord-sstable/SSTableServlet.h"
#include "fnord-logtable/LogTableServlet.h"
#include "fnord-logtable/TableRepository.h"
#include "fnord-logtable/TableJanitor.h"
#include "fnord-logtable/TableReplication.h"
#include "fnord-afx/ArtifactReplication.h"
#include "fnord-logtable/NumericBoundsSummary.h"
#include "fnord-mdb/MDB.h"
#include "fnord-mdb/MDBUtil.h"
#include "common.h"
#include "schemas.h"
#include "CustomerNamespace.h"
#include "analytics/AnalyticsServlet.h"
#include "analytics/FeedExportApp.h"
#include "analytics/CTRByPositionQuery.h"
#include "analytics/CTRByPageQuery.h"
#include "analytics/CTRByResultItemCategoryQuery.h"
#include "analytics/TopSearchQueriesQuery.h"
#include "analytics/DiscoveryDashboardQuery.h"
#include "analytics/DiscoveryCatalogStatsQuery.h"
#include "analytics/DiscoveryCategoryStatsQuery.h"
#include "analytics/DiscoverySearchStatsQuery.h"
#include "analytics/ECommerceKPIQuery.h"
#include "analytics/AnalyticsQueryEngine.h"
#include "analytics/AnalyticsQueryEngine.h"
#include "analytics/ShopStatsServlet.h"

using namespace fnord;

fnord::thread::EventLoop ev;

int main(int argc, const char** argv) {
  fnord::Application::init();
  fnord::Application::logToStderr();

  fnord::cli::FlagParser flags;

  flags.defineFlag(
      "http_port",
      fnord::cli::FlagParser::T_INTEGER,
      false,
      NULL,
      "8000",
      "Start the public http server on this port",
      "<port>");

  flags.defineFlag(
      "datadir",
      cli::FlagParser::T_STRING,
      true,
      NULL,
      NULL,
      "datadir path",
      "<path>");

  flags.defineFlag(
      "shopstats_table",
      cli::FlagParser::T_STRING,
      true,
      NULL,
      NULL,
      "path",
      "<file>");

  flags.defineFlag(
      "loglevel",
      fnord::cli::FlagParser::T_STRING,
      false,
      NULL,
      "INFO",
      "loglevel",
      "<level>");

  flags.parseArgv(argc, argv);

  Logger::get()->setMinimumLogLevel(
      strToLogLevel(flags.getString("loglevel")));

  /* thread pools */
  fnord::thread::ThreadPool tpool;
  fnord::thread::FixedSizeThreadPool wpool(8);
  wpool.start();

  /* http */
  fnord::http::HTTPRouter http_router;
  fnord::http::HTTPServer http_server(&http_router, &ev);
  http_server.listen(flags.getInt("http_port"));
  http::HTTPConnectionPool http(&ev);

  /* tsdb */
  tsdb::TSDBClient tsdb("http://nue03.prod.fnrd.net:7003/tsdb", &http);

  /* dproc */
  dproc::DispatchService dproc;
  auto local_scheduler = mkRef(new dproc::LocalScheduler());

  /* feed export */
  dproc.registerApp(new FeedExportApp(&tsdb), local_scheduler.get());

  /* stop stats */
  //auto shopstats = cm::ShopStatsTable::open(flags.getString("shopstats_table"));
  //cm::ShopStatsServlet shopstats_servlet(shopstats);
  //http_router.addRouteByPrefixMatch("/shopstats", &shopstats_servlet, &tpool);

  /* analytics core */
  auto dir = flags.getString("datadir");
  cm::AnalyticsQueryEngine analytics(32, dir, &tsdb);
  cm::AnalyticsServlet analytics_servlet(&analytics, &dproc);
  http_router.addRouteByPrefixMatch("/analytics", &analytics_servlet, &tpool);

  analytics.registerQueryFactory("ctr_by_position", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::CTRByPositionQuery(scan, segments);
  });

  analytics.registerQueryFactory("ctr_by_page", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::CTRByPageQuery(scan, segments);
  });

  analytics.registerQueryFactory("ctr_by_result_item_category", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::CTRByResultItemCategoryQuery(scan, segments);
  });

  analytics.registerQueryFactory("discovery_dashboard", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::DiscoveryDashboardQuery(
        scan,
        segments,
        query.start_time,
        query.end_time);
  });

  analytics.registerQueryFactory("discovery_search_stats", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::DiscoverySearchStatsQuery(
        scan,
        segments,
        query.start_time,
        query.end_time,
        params);
  });

  analytics.registerQueryFactory("discovery_catalog_stats", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::DiscoveryCatalogStatsQuery(
        scan,
        segments,
        query.start_time,
        query.end_time,
        params);
  });

  analytics.registerQueryFactory("discovery_category0_stats", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::DiscoveryCategoryStatsQuery(
        scan,
        segments,
        query.start_time,
        query.end_time,
        "search_queries.category1",
        "search_queries.category1",
        params);
  });

  analytics.registerQueryFactory("discovery_category1_stats", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::DiscoveryCategoryStatsQuery(
        scan,
        segments,
        query.start_time,
        query.end_time,
        "search_queries.category1",
        "search_queries.category2",
        params);
  });

  analytics.registerQueryFactory("discovery_category2_stats", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::DiscoveryCategoryStatsQuery(
        scan,
        segments,
        query.start_time,
        query.end_time,
        "search_queries.category2",
        "search_queries.category3",
        params);
  });

  analytics.registerQueryFactory("discovery_category3_stats", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::DiscoveryCategoryStatsQuery(
        scan,
        segments,
        query.start_time,
        query.end_time,
        "search_queries.category3",
        "search_queries.category3",
        params);
  });

  analytics.registerQueryFactory("top_search_queries", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::TopSearchQueriesQuery(scan, segments, params);
  });

  analytics.registerQueryFactory("ecommerce_dashboard", [] (
      const cm::AnalyticsQuery& query,
      const cm::AnalyticsQuery::SubQueryParams params,
      const Vector<RefPtr<cm::TrafficSegment>>& segments,
      cm::AnalyticsTableScan* scan) {
    return new cm::ECommerceKPIQuery(
        scan,
        segments,
        query.start_time,
        query.end_time,
        params);
  });

  ev.run();

  fnord::logInfo("cm.analytics.backend", "Exiting...");
  exit(0);
}

<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017 deipi.com LLC and contributors. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include <string>             // for string
#include <stdlib.h>           // for getenv

#include "static_str.hh"

template <int N, int rem, char... a>
struct explode : explode<N + 1, rem / 10, ('0' + rem % 10), a...> { };

template <int N, char... a>
struct explode<N, 0, a...> {
  constexpr static const char value[N + 1]{a..., 0};
};

template <unsigned num>
struct to_string : explode<0, num> { };

template <>
struct to_string<0> : explode<1, 0, '0'> { };

enum class Coloring : uint8_t {
  TrueColor,
  Palette,
  Standard256,
  Standard16,
  None,
};

// Ansi colors:
template <uint8_t red, uint8_t green, uint8_t blue, bool bold = false>
class ansi_color {
  static constexpr auto esc = static_str::literal("\033[");

  static constexpr auto trueColor() {
    return (
      esc +
      to_string<bold>::value +
      ";38;2;" +
      to_string<red>::value + ";" +
      to_string<green>::value + ";" +
      to_string<blue>::value +
      "m"
    );
  }

  static constexpr auto standard256() {
    constexpr uint8_t color = (red == green && green == blue) ? (
      red <= 8 ? 16 :
      red >= 238 ? 231 :
      (232 + static_cast<int>(((red - 8) * 23.0f / 230.0f) + 0.5f))
    ) : (
      16 +
      (static_cast<int>(red / 255.0f * 5.0f + 0.5f) * 36) +
      (static_cast<int>(green / 255.0f * 5.0f + 0.5f) * 6) +
      (static_cast<int>(blue / 255.0f * 5.0f + 0.5f))
    );
    return (
      esc +
      to_string<bold>::value +
      ";38;5;" +
      to_string<color>::value +
      "m"
    );
  }

  static constexpr auto standard16() {
    constexpr auto _min = red < green ? red : green;
    constexpr auto min = _min < blue ? _min : blue;
    constexpr auto _max = red > green ? red : green;
    constexpr auto max = _max > blue ? _max : blue;
    constexpr uint8_t color = (red == green && green == blue) ? (
      red > 192 ? 15 :
      red > 128 ? 7 :
      red > 32 ? 8 :
      0
    ) : (
      (max <= 32) ? (
        0
      ) : (
        (
          ((static_cast<int>((blue - min) * 255.0f / (max - min) + 0.5f) > 128 ? 1 : 0) << 2) |
          ((static_cast<int>((green - min) * 255.0f / (max - min) + 0.5f) > 128 ? 1 : 0) << 1) |
          ((static_cast<int>((red - min) * 255.0f / (max - min) + 0.5f) > 128 ? 1 : 0))
        ) + (max > 192 ? 8 : 0)
      )
    );
    return (
      esc +
      to_string<bold>::value +
      ";38;5;" +
      to_string<color>::value +
      "m"
    );
  }

  static Coloring _detectColoring() {
    std::string colorterm;
    const char *env_colorterm = getenv("COLORTERM");
    if (env_colorterm) {
      colorterm = env_colorterm;
    }
    std::string term;
    const char* env_term = getenv("TERM");
    if (env_term) {
      term = env_term;
    }
    if (colorterm.find("truecolor") != std::string::npos || term.find("24bit") != std::string::npos) {
      return Coloring::TrueColor;
    } else if (term.find("256color") != std::string::npos) {
      return Coloring::Standard256;
    } else if (term.find("ansi") != std::string::npos || term.find("16color") != std::string::npos) {
      return Coloring::Standard16;
    } else {
      return Coloring::Standard16;
    }
  }

  static const std::string _col() {
    switch (ansi_color<0, 0, 0>::detectColoring()) {
      case Coloring::TrueColor: {
        constexpr const auto trueColor = ansi_color<red, green, blue, bold>::trueColor();
        return std::string(trueColor);
      }
      case Coloring::Palette:
      case Coloring::Standard256: {
        constexpr const auto standard256 = ansi_color<red, green, blue, bold>::standard256();
        return std::string(standard256);
      }
      case Coloring::Standard16: {
        constexpr const auto standard16 = ansi_color<red, green, blue, bold>::standard16();
        return std::string(standard16);
      }
      case Coloring::None: {
        return "";
      }
    };
  }

public:
  static Coloring detectColoring() {
    static Coloring coloring = _detectColoring();
    return coloring;
  }

  static const std::string& col() {
    static auto col = _col();
    return col;
  }
};

#define rgb(r, g, b)      ansi_color<static_cast<uint8_t>(r), static_cast<uint8_t>(g), static_cast<uint8_t>(b), false>::col()
#define rgba(r, g, b, a)  ansi_color<static_cast<uint8_t>(r * a + 0.5f), static_cast<uint8_t>(g * a + 0.5f), static_cast<uint8_t>(b * a + 0.5f), false>::col()
#define brgb(r, g, b)     ansi_color<static_cast<uint8_t>(r), static_cast<uint8_t>(g), static_cast<uint8_t>(b), true>::col()
#define brgba(r, g, b, a) ansi_color<static_cast<uint8_t>(r * a + 0.5f), static_cast<uint8_t>(g * a + 0.5f), static_cast<uint8_t>(b * a + 0.5f), true>::col()
<commit_msg>ansi_color: receiving integers (might be bigger than 255)<commit_after>/*
 * Copyright (C) 2017 deipi.com LLC and contributors. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include <string>             // for string
#include <stdlib.h>           // for getenv

#include "static_str.hh"

template <int N, int rem, char... a>
struct explode : explode<N + 1, rem / 10, ('0' + rem % 10), a...> { };

template <int N, char... a>
struct explode<N, 0, a...> {
  constexpr static const char value[N + 1]{a..., 0};
};

template <unsigned num>
struct to_string : explode<0, num> { };

template <>
struct to_string<0> : explode<1, 0, '0'> { };

enum class Coloring : uint8_t {
  TrueColor,
  Palette,
  Standard256,
  Standard16,
  None,
};

// Ansi colors:
template <int red, int green, int blue, bool bold = false>
class ansi_color {
  static constexpr uint8_t r = red < 0 ? 0 : red > 255 ? 255 : red;
  static constexpr uint8_t g = green < 0 ? 0 : green > 255 ? 255 : green;
  static constexpr uint8_t b = blue < 0 ? 0 : blue > 255 ? 255 : blue;
  static constexpr auto esc = static_str::literal("\033[");

  static constexpr auto trueColor() {
    return (
      esc +
      to_string<bold>::value +
      ";38;2;" +
      to_string<r>::value + ";" +
      to_string<g>::value + ";" +
      to_string<b>::value +
      "m"
    );
  }

  static constexpr auto standard256() {
    constexpr uint8_t color = (r == g && g == b) ? (
      r < 8 ? 16 :
      r > 238 ? 231 :
      (232 + static_cast<int>(((r - 8) * 23.0f / 230.0f) + 0.5f))
    ) : (
      16 +
      (static_cast<int>(r / 255.0f * 5.0f + 0.5f) * 36) +
      (static_cast<int>(g / 255.0f * 5.0f + 0.5f) * 6) +
      (static_cast<int>(b / 255.0f * 5.0f + 0.5f))
    );
    return (
      esc +
      to_string<bold>::value +
      ";38;5;" +
      to_string<color>::value +
      "m"
    );
  }

  static constexpr auto standard16() {
    constexpr auto _min = r < g ? r : g;
    constexpr auto min = _min < b ? _min : b;
    constexpr auto _max = r > g ? r : g;
    constexpr auto max = _max > b ? _max : b;
    constexpr uint8_t color = (r == g && g == b) ? (
      r > 192 ? 15 :
      r > 128 ? 7 :
      r > 32 ? 8 :
      0
    ) : (
      (max <= 32) ? (
        0
      ) : (
        (
          ((static_cast<int>((b - min) * 255.0f / (max - min) + 0.5f) > 128 ? 1 : 0) << 2) |
          ((static_cast<int>((g - min) * 255.0f / (max - min) + 0.5f) > 128 ? 1 : 0) << 1) |
          ((static_cast<int>((r - min) * 255.0f / (max - min) + 0.5f) > 128 ? 1 : 0))
        ) + (max > 192 ? 8 : 0)
      )
    );
    return (
      esc +
      to_string<bold>::value +
      ";38;5;" +
      to_string<color>::value +
      "m"
    );
  }

  static Coloring _detectColoring() {
    std::string colorterm;
    const char *env_colorterm = getenv("COLORTERM");
    if (env_colorterm) {
      colorterm = env_colorterm;
    }
    std::string term;
    const char* env_term = getenv("TERM");
    if (env_term) {
      term = env_term;
    }
    if (colorterm.find("truecolor") != std::string::npos || term.find("24bit") != std::string::npos) {
      return Coloring::TrueColor;
    } else if (term.find("256color") != std::string::npos) {
      return Coloring::Standard256;
    } else if (term.find("ansi") != std::string::npos || term.find("16color") != std::string::npos) {
      return Coloring::Standard16;
    } else {
      return Coloring::Standard16;
    }
  }

  static const std::string _col() {
    switch (ansi_color<0, 0, 0>::detectColoring()) {
      case Coloring::TrueColor: {
        constexpr const auto _ = trueColor();
        return std::string(_);
      }
      case Coloring::Palette:
      case Coloring::Standard256: {
        constexpr const auto _ = standard256();
        return std::string(_);
      }
      case Coloring::Standard16: {
        constexpr const auto _ = standard16();
        return std::string(_);
      }
      case Coloring::None: {
        return "";
      }
    };
  }

public:
  static Coloring detectColoring() {
    static Coloring coloring = _detectColoring();
    return coloring;
  }

  static const std::string& col() {
    static auto col = _col();
    return col;
  }
};

#define rgb(r, g, b)      ansi_color<static_cast<int>(r), static_cast<int>(g), static_cast<int>(b), false>::col()
#define rgba(r, g, b, a)  ansi_color<static_cast<int>(r * a + 0.5f), static_cast<int>(g * a + 0.5f), static_cast<int>(b * a + 0.5f), false>::col()
#define brgb(r, g, b)     ansi_color<static_cast<int>(r), static_cast<int>(g), static_cast<int>(b), true>::col()
#define brgba(r, g, b, a) ansi_color<static_cast<int>(r * a + 0.5f), static_cast<int>(g * a + 0.5f), static_cast<int>(b * a + 0.5f), true>::col()
<|endoftext|>