template<typename T, // expected-note {{declared here}}
         typename T> struct X {}; // expected-error {{declaration of 'T' shadows template parameter}}

template<typename T> struct Y { // expected-note 15{{declared here}}
  template<typename T> struct A {}; // expected-error {{declaration of 'T' shadows template parameter}}

  struct B {
    template<typename> struct T {}; // FIXME: desired-error {{declaration of 'T' shadows template parameter}}
  };
  struct C {
    template<typename> void T(); // expected-error {{declaration of 'T' shadows template parameter}}
  };
  struct D {
    struct T {}; // expected-error {{declaration of 'T' shadows template parameter}}
  };
  struct E {
    typedef int T; // expected-error {{declaration of 'T' shadows template parameter}}
  };
  struct F {
    using T = int; // expected-error {{declaration of 'T' shadows template parameter}}
  };
  struct G {
    int T; // expected-error {{declaration of 'T' shadows template parameter}}
  };
  struct H {
    static int T; // expected-error {{declaration of 'T' shadows template parameter}}
  };
  struct I {
    void T(); // expected-error {{declaration of 'T' shadows template parameter}}
  };
  struct J {
    enum T { e }; // expected-error {{declaration of 'T' shadows template parameter}}
  };
  struct K {
    enum E { T }; // expected-error {{declaration of 'T' shadows template parameter}}
  };

  void a() {
    extern int T; // expected-error {{declaration of 'T' shadows template parameter}}
  }
  void b() {
    int T; // expected-error {{declaration of 'T' shadows template parameter}}
  }
  void c() {

 // expected-error {{declaration of 'T' shadows template parameter}}
  }
  void d() {
    void T(); // expected-error {{declaration of 'T' shadows template parameter}}
  }

  friend struct T; // expected-error {{declaration of 'T' shadows template parameter}}
};

template<typename T> // expected-note {{declared here}}
void f(int T) {} // expected-error {{declaration of 'T' shadows template parameter}}

// FIXME: These are ill-formed: a template-parameter shall not have the same name as the template name.
namespace A {
  template<typename T> struct T {};
}
namespace B {
  template<typename T> void T() {}
}
namespace C {
  template<typename T> int T;
}


Rect Ctrl::GetVirtualScreenArea()
{
	GuiLock __;
	return framebuffer.GetSize();
}

#include "Local.h"

#ifdef GUI_SDL20

NAMESPACE_UPP

#define LLOG(x)   //DLOG(x)
#define LDUMP(x)  //DDUMP(x)
#define LDUMPC(x) //DDUMPC(x)

ImageBuffer    Ctrl::framebuffer;
Vector<Rect>   Ctrl::invalid;
Vector<Rect>   Ctrl::update;

Ptr<Ctrl>      Ctrl::desktop;
Vector<Ctrl *> Ctrl::topctrl;

Point          Ctrl::fbCursorPos = Null;
Image          Ctrl::fbCursorImage;
Point          Ctrl::fbCursorBakPos = Null;
Image          Ctrl::fbCursorBak;
Rect           Ctrl::fbCaretRect;
Image          Ctrl::fbCaretBak;
int            Ctrl::fbCaretTm;
int            Ctrl::renderingMode = MODE_ANTIALIASED;
bool           Ctrl::fbEndSession;
bool           Ctrl::FullWindowDrag;
int            Ctrl::PaintLock;

void Ctrl::SetDesktop(Ctrl& q)
{
	desktop = &q;
	desktop->SetRect(framebuffer.GetSize());
	desktop->SetOpen(true);
	desktop->NewTop();
	invalid.Add(framebuffer.GetSize());
}

void Ctrl::SetRenderingMode(int mode)
{
	renderingMode = mode;
	invalid.Add(framebuffer.GetSize());
}

void Ctrl::InitFB()
{
	Ctrl::GlobalBackBuffer();
	Ctrl::InitTimer();
	framebuffer.Create(1, 1);

#ifdef PLATFORM_POSIX
	SetStdFont(ScreenSans(12)); //FIXME general handling
#endif
	ChStdSkin();

	static StaticRect x;
	x.Color(Cyan());
	SetDesktop(x);
}

void Ctrl::EndSession()
{
	GuiLock __;
	LLOG("Ctrl::EndSession");
	fbEndSession = true;
	EndSessionLoopNo = EventLoopNo;
}

void Ctrl::ExitFB()
{
	TopWindow::ShutdownWindows();
	Ctrl::CloseTopCtrls();
	if(fbEndSession)
		FBQuitSession();
}

void Ctrl::SetFramebufferSize(Size sz)
{
	framebuffer.Create(sz);
	if(desktop)
		desktop->SetRect(sz);
	invalid.Add(sz);
	SyncTopWindows();
}

int Ctrl::FindTopCtrl() const
{
	for(int i = 0; i < topctrl.GetCount(); i++)
		if(this == topctrl[i])
			return i;
	return -1;
}

bool Ctrl::IsAlphaSupported()
{
	return false;
}

bool Ctrl::IsCompositedGui()
{
	return false;
}

Vector<Ctrl *> Ctrl::GetTopCtrls()
{
	Vector<Ctrl *> ctrl;
	if(desktop)
		ctrl.Add(desktop);
	for(int i = 0; i < topctrl.GetCount(); i++)
		if(!dynamic_cast<TopWindowFrame *>(topctrl[i]))
			ctrl.Add(topctrl[i]);
	return ctrl;
}

Ctrl *Ctrl::GetOwner()
{
	GuiLock __;
	int q = FindTopCtrl();
	if(q > 0 && topctrl[q]->top) {
		Ctrl *x = topctrl[q]->top->owner_window;
		LDUMP(Upp::Name(x));
		return dynamic_cast<TopWindowFrame *>(x) ? x->GetOwner() : x;
	}
	return NULL;
}

Ctrl *Ctrl::GetActiveCtrl()
{
	GuiLock __;
	return focusCtrl ? focusCtrl->GetTopCtrl() : NULL;
}

int Ctrl::RegisterSystemHotKey(dword key, Callback cb)
{
/*	ASSERT(key >= K_DELTA);
	int q = hotkey.GetCount();
	for(int i = 0; i < hotkey.GetCount(); i++)
		if(!hotkey[i]) {
			q = i;
			break;
		}
	hotkey.At(q) = cb;
	dword mod = 0;
	if(key & K_ALT)
		mod |= MOD_ALT;
	if(key & K_SHIFT)
		mod |= MOD_SHIFT;
	if(key & K_CTRL)
		mod |= MOD_CONTROL;

	return RegisterHotKey(NULL, q, mod, key & 0xffff) ? q : -1;*/
	return -1;
}

void Ctrl::UnregisterSystemHotKey(int id)
{
/*	if(id >= 0 && id < hotkey.GetCount()) {
		UnregisterHotKey(NULL, id);
		hotkey[id].Clear();
	}*/
}

bool Ctrl::IsWaitingEvent()
{
	return FBIsWaitingEvent();
}

void Ctrl::AddUpdate(const Rect& rect)
{
	LLOG("@AddUpdate " << rect);
	AddRefreshRect(update, rect);
}

void Ctrl::AddInvalid(const Rect& rect)
{
	LLOG("@AddInvalid " << rect);
	AddRefreshRect(invalid, rect);
}

void Ctrl::SyncTopWindows()
{
	for(int i = 0; i < topctrl.GetCount(); i++) {
		TopWindow *w = dynamic_cast<TopWindow *>(topctrl[i]);
		if(w)
			w->SyncRect();
	}
}

bool Ctrl::ProcessEvent(bool *quit)
{
	LLOG("@ ProcessEvent");
	ASSERT(IsMainThread());
	if(!GetMouseLeft() && !GetMouseRight() && !GetMouseMiddle())
		ReleaseCtrlCapture();
	if(FBProcessEvent(quit)) {
		LLOG("FBProcesEvent returned true");
		SyncTopWindows();
		DefferedFocusSync();
		SyncCaret();
		return true;
	}
	return false;
}

Rect Ctrl::GetClipBound(const Vector<Rect>& inv, const Rect& r)
{
	Rect ri = Null;
	for(int j = 0; j < inv.GetCount(); j++) {
		Rect rr = inv[j] & r;
		if(!rr.IsEmpty())
			ri = IsNull(ri) ? rr : rr | ri;
	}
	return ri;
}

ViewDraw::ViewDraw(Ctrl *ctrl)
{
	if(Ctrl::invalid.GetCount())
		Ctrl::DoPaint();
	Ctrl::invalid.Clear();
	Ctrl::RemoveCursor();
	Ctrl::RemoveCaret();
	Rect r = ctrl->GetScreenView();
	Ctrl::invalid.Add(r);
	Ctrl::AddUpdate(r);
	for(int i = max(ctrl->GetTopCtrl()->FindTopCtrl() + 1, 0); i < Ctrl::topctrl.GetCount(); i++) {
		Rect rr = Ctrl::topctrl[i]->GetScreenRect();
		ExcludeClip(rr);
		Subtract(Ctrl::invalid, rr);
	}
	Offset(r.TopLeft());
}

ViewDraw::~ViewDraw()
{
	FBInitUpdate();
	Ctrl::DoUpdate();
	FBFlush();
}

void Ctrl::DoUpdate()
{
	LLOG("DoUpdate");
	invalid.Clear();
	CursorSync();
	LDUMPC(update);
	screen.Present();
#if 0
	FBUpdate(framebuffer.GetSize());
#else
	for(int i = 0; i < update.GetCount(); i++) {
		LDUMP(update[i]);
		FBUpdate(update[i]);
	}
#endif
	update.Clear();
}

Vector<Rect> Ctrl::GetPaintRects()
{
	Vector<Rect> r;
	r.Add(GetScreenRect());
	for(int i = max(FindTopCtrl() + 1, 0); i < topctrl.GetCount(); i++)
		Subtract(r, topctrl[i]->GetScreenRect());
	return r;
}

void DDRect(RGBA *t, int dir, const byte *pattern, int pos, int count)
{
	while(count-- > 0) {
		byte p = pattern[7 & pos++];
		t->r ^= p;
		t->g ^= p;
		t->b ^= p;
		t += dir;
	}
}

void Ctrl::DrawLine(const Vector<Rect>& clip, int x, int y, int cx, int cy, bool horz, const byte *pattern, int animation)
{
	if(cx <= 0 || cy <= 0)
		return;
	Vector<Rect> rr = Intersection(clip, RectC(x, y, cx, cy));
	for(int i = 0; i < rr.GetCount(); i++) {
		Rect r = rr[i];
		AddUpdate(r);
		if(horz)
			for(int y = r.top; y < r.bottom; y++)
				DDRect(framebuffer[y] + r.left, 1, pattern, r.left + animation, r.GetWidth());
		else
			for(int x = r.left; x < r.right; x++)
				DDRect(framebuffer[r.top] + x, framebuffer.GetWidth(), pattern, r.top + animation, r.GetHeight());
	}
}

void Ctrl::DragRectDraw0(const Vector<Rect>& clip, const Rect& rect, int n, const byte *pattern, int animation)
{
	int hn = min(rect.GetHeight(), n);
	int vn = min(rect.GetWidth(), n);
	DrawLine(clip, rect.left, rect.top, rect.GetWidth(), hn, true, pattern, animation);
	DrawLine(clip, rect.left, rect.top + hn, vn, rect.GetHeight() - hn, false, pattern, animation);
	DrawLine(clip, rect.right - vn, rect.top + hn, vn, rect.GetHeight() - hn, false, pattern, animation);
	DrawLine(clip, rect.left + vn, rect.bottom - hn, rect.GetWidth() - 2 * vn, hn, true, pattern, animation);
}

void Ctrl::DragRectDraw(const Rect& rect1, const Rect& rect2, const Rect& clip, int n,
                        Color color, int type, int animation)
{
	static byte solid[] =  { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
	static byte normal[] = { 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00 };
	static byte dashed[] = { 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 };
	Point p = GetScreenView().TopLeft();
	Vector<Rect> pr;
	if(type & DRAWDRAGRECT_SCREEN) {
		pr.Add(Rect(framebuffer.GetSize()));
		type &= ~DRAWDRAGRECT_SCREEN;
		p = Point(0, 0);
	}
	else
		pr = Intersection(GetPaintRects(), clip.Offseted(p));
	const byte *pattern = type == DRAWDRAGRECT_DASHED ? dashed :
	                      type == DRAWDRAGRECT_NORMAL ? normal : solid;
	RemoveCursor();
	RemoveCaret();
	DragRectDraw0(pr, rect1.Offseted(p), n, pattern, animation);
	DragRectDraw0(pr, rect2.Offseted(p), n, pattern, animation);
}

void Ctrl::DoPaint()
{
	LLOG("@ DoPaint");
	if(!PaintLock) {
		bool scroll = false;
		if(desktop)
			desktop->SyncScroll();
		for(int i = 0; i < topctrl.GetCount(); i++)
			topctrl[i]->SyncScroll();
		if((invalid.GetCount() || scroll) && desktop) {
			RemoveCursor();
			RemoveCaret();
			for(int phase = 0; phase < 2; phase++) {
				LLOG("DoPaint invalid phase " << phase);
				LDUMPC(invalid);
				SystemDraw draw;
				draw.Set(screen);
				draw.Init(screen_size);
				for(int i = 0; i < invalid.GetCount(); i++) {
					draw.Clip(invalid[i]);
					AddUpdate(invalid[i]);
				}
				for(int i = topctrl.GetCount() - 1; i >= 0; i--) {
					Rect r = topctrl[i]->GetRect();
					Rect ri = GetClipBound(invalid, r);
					if(!IsNull(ri)) {
						draw.Clipoff(r);
						topctrl[i]->UpdateArea(draw, ri - r.TopLeft());
						draw.End();
						Subtract(invalid, r);
						draw.ExcludeClip(r);
					}
				}
				Rect ri = GetClipBound(invalid, framebuffer.GetSize());
				if(!IsNull(ri))
					desktop->UpdateArea(draw, ri);
			}
		}
	}
	DoUpdate();
}

void Ctrl::WndUpdate(const Rect& r)
{
	GuiLock __;
	Rect rr = r + GetRect().TopLeft();
	bool dummy;
	Vector<Rect> h;
	h <<= invalid;
	invalid = Intersect(invalid, rr, dummy);
	FBInitUpdate();
	DoPaint();
	invalid <<= h;
	Subtract(invalid, rr);
	FBFlush();
}

bool Ctrl::ProcessEvents(bool *quit)
{
	if(!ProcessEvent(quit))
		return false;
	while(ProcessEvent(quit) && (!LoopCtrl || LoopCtrl->InLoop()));
	TimeStop tm;
	TimerProc(GetTickCount());
	LLOG("TimerProc elapsed: " << tm);
	SweepMkImageCache();
	FBInitUpdate();
	DoPaint();
	FBFlush();
	return true;
}

void Ctrl::EventLoop(Ctrl *ctrl)
{
	GuiLock __;
	ASSERT(IsMainThread());
	ASSERT(LoopLevel == 0 || ctrl);
	LoopLevel++;
	LLOG("Entering event loop at level " << LoopLevel << LOG_BEGIN);
	Ptr<Ctrl> ploop;
	if(ctrl) {
		ploop = LoopCtrl;
		LoopCtrl = ctrl;
		ctrl->inloop = true;
	}

	bool quit = false;
	int64 loopno = ++EventLoopNo;
	ProcessEvents(&quit);
	while(loopno > EndSessionLoopNo && !quit && (ctrl ? ctrl->IsOpen() && ctrl->InLoop() : GetTopCtrls().GetCount()))
	{
		SyncCaret();
		GuiSleep(20);
		ProcessEvents(&quit);
		LDUMP(loopno);
		LDUMP(fbEndSessionLoop);
	}

	if(ctrl)
		LoopCtrl = ploop;
	LoopLevel--;
	LLOG(LOG_END << "Leaving event loop ");
}

void Ctrl::GuiSleep(int ms)
{
	GuiLock __;
	ASSERT(IsMainThread());
	LLOG("GuiSleep");
	int level = LeaveGuiMutexAll();
	FBSleep(ms);
	EnterGuiMutex(level);
}

Rect Ctrl::GetWndScreenRect() const
{
	GuiLock __;
	return GetRect();
}

void Ctrl::WndShow(bool b)
{
	GuiLock __;
}

void Ctrl::WndUpdate()
{
	GuiLock __;
}

bool Ctrl::IsWndOpen() const {
	GuiLock __;
	return FindTopCtrl() >= 0 || this == desktop;
}

void Ctrl::SetAlpha(byte alpha)
{
	GuiLock __;
}

Rect Ctrl::GetWorkArea() const
{
	GuiLock __;
	return framebuffer.GetSize();
}

void Ctrl::GetWorkArea(Array<Rect>& rc)
{
	GuiLock __;
	Array<Rect> r;
	r.Add(framebuffer.GetSize());
}

Rect Ctrl::GetVirtualWorkArea()
{
	return framebuffer.GetSize();
}

/*
Rect Ctrl::GetWorkArea(Point pt)
{
	return framebuffer.GetSize();
}
*/

Rect Ctrl::GetPrimaryWorkArea()
{
	Rect r;
	return framebuffer.GetSize();
}

Rect Ctrl::GetPrimaryScreenArea()
{
	return framebuffer.GetSize();
}

int Ctrl::GetKbdDelay()
{
	GuiLock __;
	return 500;
}

int Ctrl::GetKbdSpeed()
{
	GuiLock __;
	return 1000 / 32;
}

void Ctrl::DestroyWnd()
{
	for(int i = 0; i < topctrl.GetCount(); i++)
		if(topctrl[i]->top && topctrl[i]->top->owner_window == this)
			topctrl[i]->WndDestroy();
	int q = FindTopCtrl();
	if(q >= 0) {
		AddInvalid(GetRect());
		topctrl.Remove(q);
	}
	if(top) {
		delete top;
		top = NULL;
	}
	isopen = false;
	TopWindow *win = dynamic_cast<TopWindow *>(this);
	if(win)
		win->DestroyFrame();
}

void Ctrl::WndDestroy()
{
	DestroyWnd();
	if(topctrl.GetCount())
		topctrl.Top()->ActivateWnd();
}

void Ctrl::PutForeground()
{
	int q = FindTopCtrl();
	if(q >= 0) {
		AddInvalid(GetRect());
		topctrl.Remove(q);
		topctrl.Add(this);
	}
	Vector< Ptr<Ctrl> > fw;
	for(int i = 0; i < topctrl.GetCount(); i++)
		if(topctrl[i] && topctrl[i]->top && topctrl[i]->top->owner_window == this && topctrl[i] != this)
			fw.Add(topctrl[i]);
	for(int i = 0; i < fw.GetCount(); i++)
		if(fw[i])
			fw[i]->PutForeground();
}

void Ctrl::SetWndForeground()
{
	GuiLock __;
	ASSERT(IsOpen());
	if(IsWndForeground())
		return;
	Ctrl *to = this;
	while(to->top && to->top->owner_window)
		to = to->top->owner_window;
	to->PutForeground();
	if(this != focusCtrl)
		ActivateWnd();
}

bool Ctrl::IsWndForeground() const
{
	GuiLock __;
	bool b = false;
	for(int i = 0; i < topctrl.GetCount(); i++) {
		const TopWindow *tw = dynamic_cast<const TopWindow *>(topctrl[i]);
		if(tw)
			b = tw == this;
	}
	return b;
}

void Ctrl::WndEnable(bool)
{
	GuiLock __;
}

bool Ctrl::SetWndFocus()
{
	GuiLock __;
	return true;
}

bool Ctrl::HasWndFocus() const
{
	GuiLock __;
	return focusCtrl && focusCtrl->GetTopCtrl() == this;
}

bool Ctrl::SetWndCapture()
{
	GuiLock __;
	ASSERT(IsMainThread());
	return true;
}

bool Ctrl::ReleaseWndCapture()
{
	GuiLock __;
	ASSERT(IsMainThread());
	return true;
}

bool Ctrl::HasWndCapture() const
{
	GuiLock __;
	return captureCtrl && captureCtrl->GetTopCtrl() == this;
}

void Ctrl::WndInvalidateRect(const Rect& r)
{
	GuiLock __;
	int q = FindTopCtrl();
	if(q >= 0)
		AddInvalid(r + topctrl[q]->GetRect().TopLeft());
	else
		AddInvalid(r);
}

void Ctrl::WndSetPos(const Rect& rect)
{
	GuiLock __;
	TopWindow *w = dynamic_cast<TopWindow *>(this);
	if(w)
		w->SyncFrameRect(rect);
	invalid.Add(GetRect());
	SetWndRect(rect);
	invalid.Add(rect);
}

void  Ctrl::WndScrollView(const Rect& r, int dx, int dy)
{
	GuiLock __;
	if(dx == 0 && dy == 0)
		return;
	if(dx && dy) {
		Refresh(r);
		return;
	}
	RemoveCursor();
	RemoveCaret();
	Rect sr = r.Offseted(GetScreenRect().TopLeft());
	Vector<Rect> pr = Intersection(GetPaintRects(), sr);
	for(int i = 0; i < pr.GetCount(); i++) {
		Rect r = pr[i];
		if(dx) {
			int n = r.GetWidth() - abs(dx);
			if(n > 0) {
				int to = r.left + dx * (dx > 0);
				int from = r.left - dx * (dx < 0);
				for(int y = r.top; y < r.bottom; y++)
					memmove(framebuffer[y] + to, framebuffer[y] + from, n * sizeof(RGBA));
			}
			n = min(abs(dx), r.GetWidth());
			Refresh(dx < 0 ? r.left : r.right - n, r.top, n, r.GetHeight());
		}
		else {
			int n = r.GetHeight() - abs(dy);
			for(int y = 0; y < n; y++)
				memmove(framebuffer[dy < 0 ? r.top + y : r.bottom - 1 - y] + r.left,
				        framebuffer[dy < 0 ? r.top + y - dy : r.bottom - 1 - y - dy] + r.left,
				        r.GetWidth() * sizeof(RGBA));
			n = min(abs(dy), r.GetHeight());
			Refresh(r.left, dy < 0 ? r.bottom - n : r.top, r.GetWidth(), n);
		}
	}

	Vector<Rect> ur;
	for(int i = 0; i < invalid.GetCount(); i++)
		if(invalid[i].Intersects(sr))
			ur.Add(invalid[i]);
	for(int i = 0; i < ur.GetCount(); i++)
		AddInvalid(ur[i].Offseted(dx, dy));
}

void Ctrl::PopUp(Ctrl *owner, bool savebits, bool activate, bool dropshadow, bool topmost)
{
	ASSERT(!IsChild() && !IsOpen() && FindTopCtrl() < 0);
	NewTop();
	if(owner) {
		Ctrl *owner_window = owner->GetTopWindow();
		if(!owner_window)
			owner_window = owner->GetTopCtrl();
		ASSERT(owner_window->IsOpen());
		if(owner_window != desktop) {
			owner_window->SetForeground();
			top->owner_window = owner_window;
		}
	}
	topctrl.Add(this);
	popup = isopen = true;
	RefreshLayoutDeep();
	if(activate) SetFocusWnd();
	AddInvalid(GetRect());
}

Rect Ctrl::GetDefaultWindowRect() {
	GuiLock __;
	static int ii = 0;
	Size sz = framebuffer.GetSize();
	Rect rect = framebuffer.GetSize();
	rect.Deflate(sz / 10);
	rect.Offset(Size(GetStdFontCy(), 2 * GetStdFontCy()) * (++ii % 8));
	return rect;
}

Vector<WString> SplitCmdLine__(const char *cmd)
{
	Vector<WString> out;
	while(*cmd)
		if((byte)*cmd <= ' ')
			cmd++;
		else if(*cmd == '\"') {
			WString quoted;
			while(*++cmd && (*cmd != '\"' || *++cmd == '\"'))
				quoted.Cat(FromSystemCharset(String(cmd, 1)).ToWString());
			out.Add(quoted);
		}
		else {
			const char *begin = cmd;
			while((byte)*cmd > ' ')
				cmd++;
			out.Add(String(begin, cmd).ToWString());
		}
	return out;
}

void Ctrl::InstallPanicBox()
{
}

void Ctrl::SysEndLoop()
{
}

END_UPP_NAMESPACE

#endif


void KungFuMasterSettings::reset() {

    m_reward   = 0;
    m_score    = 0;
    m_terminal = false;
    m_lives    = 4;
}

/* *****************************************************************************
 * Xitari
 *
 *
 * *****************************************************************************
 * A.L.E (Arcade Learning Environment)
 *
 * Based on: Stella  --  "An Atari 2600 VCS Emulator"
 *
 * *****************************************************************************
 */
#include "KungFuMaster.hpp"

#include "../RomUtils.hpp"

using namespace ale;

KungFuMasterSettings::KungFuMasterSettings() {

    reset();
}

/* create a new instance of the rom */
RomSettings* KungFuMasterSettings::clone() const {

    RomSettings* rval = new KungFuMasterSettings();
    *rval = *this;
    return rval;
}

/* process the latest information from ALE */
void KungFuMasterSettings::step(const System& system) {

    // update the reward
    int score = getDecimalScore(0x9A, 0x99, 0x98, &system);
    int reward = score - m_score;
    m_reward = reward;
    m_score = score;

    // update terminal status
    int lives_byte = readRam(&system, 0x9D);
    m_terminal = lives_byte == 0xFF;
    m_lives = (lives_byte & 0x7) + 1;
}

/* is end of game */
bool KungFuMasterSettings::isTerminal() const {

    return m_terminal;
};

/* get the most recently observed reward */
reward_t KungFuMasterSettings::getReward() const {

    return m_reward;
}

/* is an action part of the minimal set? */
bool KungFuMasterSettings::isMinimal(const Action &a) const {

    switch (a) {
        case PLAYER_A_NOOP:
        case PLAYER_A_UP:
        case PLAYER_A_RIGHT:
        case PLAYER_A_LEFT:
        case PLAYER_A_DOWN:
        case PLAYER_A_DOWNRIGHT:
        case PLAYER_A_DOWNLEFT:
        case PLAYER_A_RIGHTFIRE:
        case PLAYER_A_LEFTFIRE:
        case PLAYER_A_DOWNFIRE:
        case PLAYER_A_UPRIGHTFIRE:
        case PLAYER_A_UPLEFTFIRE:
        case PLAYER_A_DOWNRIGHTFIRE:
        case PLAYER_A_DOWNLEFTFIRE:
            return true;
        default:
            return false;
    }
}

/* reset the state of the game */

/* saves the state of the rom settings */
void KungFuMasterSettings::saveState(Serializer & ser) {
  ser.putInt(m_reward);
  ser.putInt(m_score);
  ser.putBool(m_terminal);
  ser.putInt(m_lives);
}

// loads the state of the rom settings
void KungFuMasterSettings::loadState(Deserializer & ser) {
  m_reward = ser.getInt();
  m_score = ser.getInt();
  m_terminal = ser.getBool();
  m_lives = ser.getInt();
}


bool CGameLogic::IsTongHua(BYTE cbCardData[], BYTE cbCardCount)
{
	//数目判断
	if (cbCardCount!=5) return false;

	//变量定义
	BYTE bFirstColor=GetCardColor(cbCardData[0]);

	//分析处理
	for (BYTE i=1;i<cbCardCount;i++)
	{
		if (GetCardColor(cbCardData[i])!=bFirstColor) return false;
	}

	return true;
}

#include "StdAfx.h"
#include "GameLogic.h"

//静态变量

//扑克数据
const BYTE	CGameLogic::m_bCardListData[52]=
{
	0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,	//方块 A - K
	0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,	//梅花 A - K
	0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,	//红桃 A - K
	0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,	//黑桃 A - K
};

//构造函数
CGameLogic::CGameLogic()
{
}

//析构函数
CGameLogic::~CGameLogic()
{
}

//获取类型
BYTE CGameLogic::GetCardType(BYTE cbCardData[], BYTE cbCardCount)
{
	//类型判断
	if (IsTongHuaShun(cbCardData,cbCardCount)) return CT_TONG_HUA_SHUN;
	if (IsTieZhi(cbCardData,cbCardCount)) return CT_TIE_ZHI;
	if (IsHuLu(cbCardData,cbCardCount)) return CT_HU_LU;
	if (IsTongHua(cbCardData,cbCardCount)) return CT_TONG_HUA;
	if (IsShunZhi(cbCardData,cbCardCount)) return CT_SHUN_ZI;
	if (IsSanTiao(cbCardData,cbCardCount)) return CT_THREE_TIAO;
	if (IsDouble(cbCardData,cbCardCount)) return CT_DOUBLE;
	if (cbCardCount==1) return CT_SINGLE;

	return CT_ERROR;
}

//排列扑克
void CGameLogic::SortCardList(BYTE cbCardData[], BYTE cbCardCount)
{
	//转换数值
	BYTE cbLogicValue[MAX_COUNT];
	for (BYTE i=0;i<cbCardCount;i++) cbLogicValue[i]=GetCardLogicValue(cbCardData[i]);

	//排序操作
	bool bSorted=true;
	BYTE cbTempData,bLast=cbCardCount-1;
	do
	{
		bSorted=true;
		for (BYTE i=0;i<bLast;i++)
		{
			if ((cbLogicValue[i]<cbLogicValue[i+1])||
				((cbLogicValue[i]==cbLogicValue[i+1])&&(cbCardData[i]<cbCardData[i+1])))
			{
				//交换位置
				cbTempData=cbCardData[i];
				cbCardData[i]=cbCardData[i+1];
				cbCardData[i+1]=cbTempData;
				cbTempData=cbLogicValue[i];
				cbLogicValue[i]=cbLogicValue[i+1];
				cbLogicValue[i+1]=cbTempData;
				bSorted=false;
			}
		}
		bLast--;
	} while(bSorted==false);

	return;
}

//混乱扑克
void CGameLogic::RandCardList(BYTE cbCardBuffer[], BYTE cbBufferCount)
{
	//混乱准备
	BYTE cbCardData[sizeof(m_bCardListData)];
	CopyMemory(cbCardData,m_bCardListData,sizeof(m_bCardListData));

	//混乱扑克
	BYTE bRandCount=0,bPosition=0;
	do
	{
		bPosition=rand()%(cbBufferCount-bRandCount);
		cbCardBuffer[bRandCount++]=cbCardData[bPosition];
		cbCardData[bPosition]=cbCardData[cbBufferCount-bRandCount];
	} while (bRandCount<cbBufferCount);

	return;
}

//删除扑克
bool CGameLogic::RemoveCard(const BYTE bRemoveCard[], BYTE bRemoveCount, BYTE cbCardData[], BYTE cbCardCount)
{
	//检验数据
	ASSERT(bRemoveCount<=cbCardCount);

	//定义变量
	BYTE bDeleteCount=0,bTempCardData[MAX_COUNT];
	if (cbCardCount>CountArray(bTempCardData)) return false;
	CopyMemory(bTempCardData,cbCardData,cbCardCount*sizeof(cbCardData[0]));

	//置零扑克
	for (BYTE i=0;i<bRemoveCount;i++)
	{
		for (BYTE j=0;j<cbCardCount;j++)
		{
			if (bRemoveCard[i]==bTempCardData[j])
			{
				bDeleteCount++;
				bTempCardData[j]=0;
				break;
			}
		}
	}
	if (bDeleteCount!=bRemoveCount) return false;

	//清理扑克
	BYTE bCardPos=0;
	for (BYTE i=0;i<cbCardCount;i++)
	{
		if (bTempCardData[i]!=0) cbCardData[bCardPos++]=bTempCardData[i];
	}

	return true;
}

//有效判断
bool CGameLogic::IsValidCard(BYTE cbCardData)
{
	//变量定义
	BYTE cbColor=GetCardColor(cbCardData);
	BYTE cbValue=GetCardValue(cbCardData);

	//有效判断
	if ((cbColor<=0x30)&&(cbValue>=0x01)&&(cbValue<=0x0D)) return true;

	return false;
}

//逻辑数值
BYTE CGameLogic::GetCardLogicValue(BYTE cbCardData)
{
	//扑克属性
	BYTE bCardColor=GetCardColor(cbCardData);
	BYTE bCardValue=GetCardValue(cbCardData);

	//转换数值
	return (bCardValue<=2)?(bCardValue+13):bCardValue;
}

//是否对子
bool CGameLogic::IsDouble(BYTE cbCardData[], BYTE cbCardCount)
{
	//数目判断
	if (cbCardCount!=2) return false;

	//分析处理
	BYTE bNextValue=GetCardLogicValue(cbCardData[1]);
	BYTE bFirstValue=GetCardLogicValue(cbCardData[0]);

	return (bNextValue==bFirstValue);
}

//是否铁支
bool CGameLogic::IsTieZhi(BYTE cbCardData[], BYTE cbCardCount)
{
	//数目判断
	if (cbCardCount!=5) return false;

	//变量定义
	BYTE bNoFixCount=0;
	BYTE bCenterValue=GetCardLogicValue(cbCardData[1]);

	//分析处理
	for (BYTE i=0;i<cbCardCount;i++)
	{
		if (GetCardLogicValue(cbCardData[i])!=bCenterValue)
		{
			bNoFixCount++;
			if (bNoFixCount>1) return false;
		}
	}

	return (bNoFixCount==1);
}

//是否葫芦
bool CGameLogic::IsHuLu(BYTE cbCardData[], BYTE cbCardCount)
{
	//数目判断
	if (cbCardCount!=5) return false;

	//分析处理
	BYTE bMidValue=GetCardLogicValue(cbCardData[2]);
	BYTE bLastValue=GetCardLogicValue(cbCardData[4]);
	BYTE bFirstValue=GetCardLogicValue(cbCardData[0]);
	if (GetCardLogicValue(cbCardData[3])!=bLastValue) return false;
	if (GetCardLogicValue(cbCardData[1])!=bFirstValue) return false;
	if ((bMidValue!=bFirstValue)&&(bMidValue!=bLastValue)) return false;

	return true;
}

//是否同花

//是否顺子
bool CGameLogic::IsShunZhi(BYTE cbCardData[], BYTE cbCardCount)
{
	//数目判断
	if (cbCardCount!=5) return false;

	//变量定义
	BYTE bFirstValue=GetCardLogicValue(cbCardData[0]);

	//特殊处理
	if (bFirstValue==15)
	{
		for (BYTE i=1;i<cbCardCount;i++)
		{
			BYTE cbLogicValue=GetCardLogicValue(cbCardData[i]);
			if ((bFirstValue!=(cbLogicValue+i))&&(bFirstValue!=(cbLogicValue+i+8))) return false;
		}

		return true;
	}

	//分析处理
	for (BYTE i=1;i<cbCardCount;i++)
	{
		if (bFirstValue!=(GetCardLogicValue(cbCardData[i])+i)) return false;
	}

	return true;
}

//是否三条
bool CGameLogic::IsSanTiao(BYTE cbCardData[], BYTE cbCardCount)
{
	//数目判断
	if (cbCardCount!=3) return false;

	//分析处理
	BYTE bFirstValue=GetCardLogicValue(cbCardData[0]);
	for (BYTE i=1;i<cbCardCount;i++)
	{
		if (GetCardLogicValue(cbCardData[i])!=bFirstValue) return false;
	}

	return true;
}

//是否同花顺
bool CGameLogic::IsTongHuaShun(BYTE cbCardData[], BYTE cbCardCount)
{
	//数目判断
	if (cbCardCount!=5) return false;

	//变量定义
	BYTE bFirstColor=GetCardColor(cbCardData[0]);
	BYTE bFirstValue=GetCardLogicValue(cbCardData[0]);

	//特殊处理
	if (bFirstValue==15)
	{
		for (BYTE i=1;i<cbCardCount;i++)
		{
			BYTE cbLogicValue=GetCardLogicValue(cbCardData[i]);
			if (GetCardColor(cbCardData[i])!=bFirstColor) return false;
			if ((bFirstValue!=(cbLogicValue+i))&&(bFirstValue!=(cbLogicValue+i+8))) return false;
		}

		return true;
	}

	//分析处理
	for (BYTE i=1;i<cbCardCount;i++)
	{
		if (GetCardColor(cbCardData[i])!=bFirstColor) return false;
		if (bFirstValue!=(GetCardLogicValue(cbCardData[i])+i)) return false;
	}

	return true;
}

//对比扑克
bool CGameLogic::CompareCard(BYTE cbFirstCardData, BYTE cbNextCardData)
{
	//数值判断
	BYTE cbNextValue=GetCardLogicValue(cbNextCardData);
	BYTE cbFirstValue=GetCardLogicValue(cbFirstCardData);
	if (cbFirstValue!=cbNextValue) return (cbFirstValue>cbNextValue);

	//花色判断
	BYTE cbNextColor=GetCardColor(cbNextCardData);
	BYTE cbFirstColor=GetCardColor(cbFirstCardData);

	return (cbFirstColor>cbNextColor);
}

//对比扑克
bool CGameLogic::CompareCard(BYTE bFirstList[], BYTE bNextList[], BYTE cbFirstCount, BYTE cbNextCount)
{
	//数目判断
	if (cbFirstCount!=cbNextCount) return false;

	//获取类型
	BYTE bNextType=GetCardType(bNextList,cbNextCount);
	BYTE bFirstType=GetCardType(bFirstList,cbFirstCount);

	//类型过滤
	ASSERT(bFirstType!=CT_ERROR);
	if ((bFirstType==CT_ERROR)||(bNextType==CT_ERROR)) return false;

	//简单类型
	switch(bFirstType)
	{
	case CT_SINGLE:			//单牌
	case CT_DOUBLE:			//对子
	case CT_THREE_TIAO:		//三条
		{
			if (bNextType!=bFirstType) return false;
			return CompareCard(bFirstList[0],bNextList[0]);
		}
	}

	//类型判断
	if (bFirstType!=bNextType) return (bFirstType>bNextType);

	//五条类型
	switch(bFirstType)
	{
	case CT_SHUN_ZI:		//顺子
	case CT_TONG_HUA:		//同花
	case CT_TONG_HUA_SHUN:	//同花顺
		{
			return CompareCard(bFirstList[0],bNextList[0]);
		}
	case CT_HU_LU:			//葫芦
		{
			return CompareCard(bFirstList[2],bNextList[2]);
		}
	case CT_TIE_ZHI:		//铁支
		{
			return CompareCard(bFirstList[1],bNextList[1]);
		}
	}

	return false;
}

//出牌判断
bool CGameLogic::SearchOutCard(BYTE cbCardData[], BYTE cbCardCount, BYTE bTurnCardData[], BYTE cbTurnCardCount, BYTE cbTurnOutType, BYTE cbOutCard[MAX_COUNT])
{
	//分析过滤
	if (cbCardCount<cbTurnCardCount) return false;

	return false;
}

//分析扑克
void CGameLogic::AnalysebCardData(const BYTE bCardData[], BYTE bCardCount, tagAnalyseResult & AnalyseResult)
{
	//变量定义
	BYTE bSameCount=1,bCardValueTemp=0;
	BYTE bLogicValue=GetCardLogicValue(bCardData[0]);

	//设置结果
	memset(&AnalyseResult,0,sizeof(AnalyseResult));

	//扑克分析
	for (BYTE i=1;i<bCardCount;i++)
	{
		//获取扑克
		bCardValueTemp=GetCardLogicValue(bCardData[i]);
		if (bCardValueTemp==bLogicValue) bSameCount++;

		//保存结果
		if ((bCardValueTemp!=bLogicValue)||(i==(bCardCount-1)))
		{
			switch (bSameCount)
			{
			case 2:		//两张
				{
					CopyMemory(&AnalyseResult.m_bDCardData[AnalyseResult.bDoubleCount*2],&bCardData[i-2+((i==bCardCount-1&&bCardValueTemp==bLogicValue)?1:0)],2);
					AnalyseResult.bDoubleLogicVolue[AnalyseResult.bDoubleCount++]=bLogicValue;
					break;
				}
			case 3:		//三张
				{
					CopyMemory(&AnalyseResult.m_bTCardData[AnalyseResult.bThreeCount*3],&bCardData[i-3+((i==bCardCount-1&&bCardValueTemp==bLogicValue)?1:0)],3);
					AnalyseResult.bThreeLogicVolue[AnalyseResult.bThreeCount++]=bLogicValue;
					break;
				}
			case 4:		//四张
				{
					CopyMemory(&AnalyseResult.m_bFCardData[AnalyseResult.bFourCount*4],&bCardData[i-4+((i==bCardCount-1&&bCardValueTemp==bLogicValue)?1:0)],4);
					AnalyseResult.bFourLogicVolue[AnalyseResult.bFourCount++]=bLogicValue;
					break;
				}
			}
		}

		//设置变量
		if (bCardValueTemp!=bLogicValue)
		{
			if(bSameCount==1)
			{
				if(i!=bCardCount-1)
					AnalyseResult.m_bSCardData[AnalyseResult.bSignedCount++]=bCardData[i-1];
				else
				{
					AnalyseResult.m_bSCardData[AnalyseResult.bSignedCount++]=bCardData[i-1];
					AnalyseResult.m_bSCardData[AnalyseResult.bSignedCount++]=bCardData[i];
				}
			}
			else
			{
				if(i==bCardCount-1)
					AnalyseResult.m_bSCardData[AnalyseResult.bSignedCount++]=bCardData[i];
			}
			bSameCount=1;
			bLogicValue=bCardValueTemp;

		}
	}

	//单牌数目
	BYTE bOtherCount=AnalyseResult.bDoubleCount*2+AnalyseResult.bThreeCount*3+AnalyseResult.bFourCount*4;
	return;
}


FontPlatformData::FontPlatformData(float size, bool bold, bool oblique)
    : m_font(0)
    , m_size(size)
    , m_scriptCache(0)
    , m_scriptFontProperties(0)
{
}

/*
 *
 */

#include "config.h"
#include "FontPlatformData.h"

#include <windows.h>
#include <objidl.h>
#include <mlang.h>

#include "PlatformSupport.h"
#include "SkiaFontWin.h"
#include "StdLibExtras.h"

namespace WebCore {

FontPlatformData::FontPlatformData(WTF::HashTableDeletedValueType)
    : m_font(hashTableDeletedFontValue())
    , m_size(-1)
    , m_scriptCache(0)
    , m_scriptFontProperties(0)
{
}

FontPlatformData::FontPlatformData()
    : m_font(0)
    , m_size(0)
    , m_scriptCache(0)
    , m_scriptFontProperties(0)
{
}

FontPlatformData::FontPlatformData(HFONT font, float size)
    : m_font(RefCountedHFONT::create(font))
    , m_size(size)
    , m_scriptCache(0)
    , m_scriptFontProperties(0)
{
}

// FIXME: this constructor is needed for SVG fonts but doesn't seem to do much

FontPlatformData::FontPlatformData(const FontPlatformData& data)
    : m_font(data.m_font)
    , m_size(data.m_size)
    , m_scriptCache(0)
    , m_scriptFontProperties(0)
{
}

FontPlatformData& FontPlatformData::operator=(const FontPlatformData& data)
{
    if (this != &data) {
        m_font = data.m_font;
        m_size = data.m_size;

        // The following fields will get re-computed if necessary.
        ScriptFreeCache(&m_scriptCache);
        m_scriptCache = 0;

        delete m_scriptFontProperties;
        m_scriptFontProperties = 0;
    }
    return *this;
}

FontPlatformData::~FontPlatformData()
{
    ScriptFreeCache(&m_scriptCache);
    m_scriptCache = 0;

    delete m_scriptFontProperties;
    m_scriptFontProperties = 0;
}

FontPlatformData::RefCountedHFONT::~RefCountedHFONT()
{
    if (m_hfont != reinterpret_cast<HFONT>(-1)) {
        DeleteObject(m_hfont);
    }
}

FontPlatformData::RefCountedHFONT* FontPlatformData::hashTableDeletedFontValue()
{
    DEFINE_STATIC_LOCAL(RefPtr<RefCountedHFONT>, deletedValue,
                        (RefCountedHFONT::create(reinterpret_cast<HFONT>(-1))));
    return deletedValue.get();
}

SCRIPT_FONTPROPERTIES* FontPlatformData::scriptFontProperties() const
{
    if (!m_scriptFontProperties) {
        m_scriptFontProperties = new SCRIPT_FONTPROPERTIES;
        memset(m_scriptFontProperties, 0, sizeof(SCRIPT_FONTPROPERTIES));
        m_scriptFontProperties->cBytes = sizeof(SCRIPT_FONTPROPERTIES);
        HRESULT result = ScriptGetFontProperties(0, scriptCache(),
                                                 m_scriptFontProperties);
        if (result == E_PENDING) {
            HDC dc = GetDC(0);
            HGDIOBJ oldFont = SelectObject(dc, hfont());
            HRESULT hr = ScriptGetFontProperties(dc, scriptCache(),
                                                 m_scriptFontProperties);
            if (S_OK != hr) {
                if (PlatformSupport::ensureFontLoaded(hfont())) {
                    // FIXME: Handle gracefully the error if this call also fails.
                    hr = ScriptGetFontProperties(dc, scriptCache(),
                                                 m_scriptFontProperties);
                    if (S_OK != hr) {
                        LOG_ERROR("Unable to get the font properties after second attempt");
                    }
                }
            }

            SelectObject(dc, oldFont);
            ReleaseDC(0, dc);
        }
    }
    return m_scriptFontProperties;
}

#ifndef NDEBUG
String FontPlatformData::description() const
{
    return String();
}
#endif

}


bool
StackSlotColoring::OverlapWithAssignments(LiveInterval *li, int Color) const {
  const SmallVectorImpl<LiveInterval *> &OtherLIs = Assignments[Color];
  for (unsigned i = 0, e = OtherLIs.size(); i != e; ++i) {
    LiveInterval *OtherLI = OtherLIs[i];
    if (OtherLI->overlaps(*li))
      return true;
  }
  return false;
}

//===- StackSlotColoring.cpp - Stack slot coloring pass. ------------------===//
// This file implements the stack slot coloring pass.

#include "llvm/ADT/BitVector.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/CodeGen/LiveInterval.h"
#include "llvm/CodeGen/LiveIntervals.h"
#include "llvm/CodeGen/LiveStacks.h"
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/MachineBlockFrequencyInfo.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/CodeGen/MachineMemOperand.h"
#include "llvm/CodeGen/MachineOperand.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/PseudoSourceValue.h"
#include "llvm/CodeGen/SlotIndexes.h"
#include "llvm/CodeGen/TargetInstrInfo.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/Pass.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <iterator>
#include <vector>

using namespace llvm;

#define DEBUG_TYPE "stack-slot-coloring"

static cl::opt<bool>
DisableSharing("no-stack-slot-sharing",
             cl::init(false), cl::Hidden,
             cl::desc("Suppress slot sharing during stack coloring"));

static cl::opt<int> DCELimit("ssc-dce-limit", cl::init(-1), cl::Hidden);

STATISTIC(NumEliminated, "Number of stack slots eliminated due to coloring");
STATISTIC(NumDead,       "Number of trivially dead stack accesses eliminated");

namespace {

  class StackSlotColoring : public MachineFunctionPass {
    LiveStacks* LS;
    MachineFrameInfo *MFI;
    const TargetInstrInfo  *TII;
    const MachineBlockFrequencyInfo *MBFI;

    // SSIntervals - Spill slot intervals.
    std::vector<LiveInterval*> SSIntervals;

    // SSRefs - Keep a list of MachineMemOperands for each spill slot.
    // MachineMemOperands can be shared between instructions, so we need
    // to be careful that renames like [FI0, FI1] -> [FI1, FI2] do not
    SmallVector<SmallVector<MachineMemOperand *, 8>, 16> SSRefs;

    // OrigAlignments - Alignments of stack objects before coloring.
    SmallVector<unsigned, 16> OrigAlignments;

    // OrigSizes - Sizess of stack objects before coloring.
    SmallVector<unsigned, 16> OrigSizes;

    // AllColors - If index is set, it's a spill slot, i.e. color.
    // FIXME: This assumes PEI locate spill slot with smaller indices
    // closest to stack pointer / frame pointer. Therefore, smaller
    // index == better color. This is per stack ID.
    SmallVector<BitVector, 2> AllColors;

    // NextColor - Next "color" that's not yet used. This is per stack ID.
    SmallVector<int, 2> NextColors = { -1 };

    // UsedColors - "Colors" that have been assigned. This is per stack ID
    SmallVector<BitVector, 2> UsedColors;

    // Assignments - Color to intervals mapping.
    SmallVector<SmallVector<LiveInterval*,4>, 16> Assignments;

  public:
    static char ID; // Pass identification

    StackSlotColoring() : MachineFunctionPass(ID) {
      initializeStackSlotColoringPass(*PassRegistry::getPassRegistry());
    }

    void getAnalysisUsage(AnalysisUsage &AU) const override {
      AU.setPreservesCFG();
      AU.addRequired<SlotIndexes>();
      AU.addPreserved<SlotIndexes>();
      AU.addRequired<LiveStacks>();
      AU.addRequired<MachineBlockFrequencyInfo>();
      AU.addPreserved<MachineBlockFrequencyInfo>();
      AU.addPreservedID(MachineDominatorsID);
      MachineFunctionPass::getAnalysisUsage(AU);
    }

    bool runOnMachineFunction(MachineFunction &MF) override;

  private:
    void InitializeSlots();
    void ScanForSpillSlotRefs(MachineFunction &MF);
    bool OverlapWithAssignments(LiveInterval *li, int Color) const;
    int ColorSlot(LiveInterval *li);
    bool ColorSlots(MachineFunction &MF);
    void RewriteInstruction(MachineInstr &MI, SmallVectorImpl<int> &SlotMapping,
                            MachineFunction &MF);
    bool RemoveDeadStores(MachineBasicBlock* MBB);
  };

} // end anonymous namespace

char StackSlotColoring::ID = 0;

char &llvm::StackSlotColoringID = StackSlotColoring::ID;

INITIALIZE_PASS_BEGIN(StackSlotColoring, DEBUG_TYPE,
                "Stack Slot Coloring", false, false)
INITIALIZE_PASS_DEPENDENCY(SlotIndexes)
INITIALIZE_PASS_DEPENDENCY(LiveStacks)
INITIALIZE_PASS_DEPENDENCY(MachineLoopInfo)
INITIALIZE_PASS_END(StackSlotColoring, DEBUG_TYPE,
                "Stack Slot Coloring", false, false)

namespace {

// IntervalSorter - Comparison predicate that sort live intervals by
// their weight.
struct IntervalSorter {
  bool operator()(LiveInterval* LHS, LiveInterval* RHS) const {
    return LHS->weight > RHS->weight;
  }
};

} // end anonymous namespace

/// ScanForSpillSlotRefs - Scan all the machine instructions for spill slot
/// references and update spill slot weights.
void StackSlotColoring::ScanForSpillSlotRefs(MachineFunction &MF) {
  SSRefs.resize(MFI->getObjectIndexEnd());

  // FIXME: Need the equivalent of MachineRegisterInfo for frameindex operands.
  for (MachineFunction::iterator MBBI = MF.begin(), E = MF.end();
       MBBI != E; ++MBBI) {
    MachineBasicBlock *MBB = &*MBBI;
    for (MachineBasicBlock::iterator MII = MBB->begin(), EE = MBB->end();
         MII != EE; ++MII) {
      MachineInstr &MI = *MII;
      for (unsigned i = 0, e = MI.getNumOperands(); i != e; ++i) {
        MachineOperand &MO = MI.getOperand(i);
        if (!MO.isFI())
          continue;
        int FI = MO.getIndex();
        if (FI < 0)
          continue;
        if (!LS->hasInterval(FI))
          continue;
        LiveInterval &li = LS->getInterval(FI);
        if (!MI.isDebugValue())
          li.weight += LiveIntervals::getSpillWeight(false, true, MBFI, MI);
      }
      for (MachineInstr::mmo_iterator MMOI = MI.memoperands_begin(),
                                      EE = MI.memoperands_end();
           MMOI != EE; ++MMOI) {
        MachineMemOperand *MMO = *MMOI;
        if (const FixedStackPseudoSourceValue *FSV =
            dyn_cast_or_null<FixedStackPseudoSourceValue>(
                MMO->getPseudoValue())) {
          int FI = FSV->getFrameIndex();
          if (FI >= 0)
            SSRefs[FI].push_back(MMO);
        }
      }
    }
  }
}

/// InitializeSlots - Process all spill stack slot liveintervals and add them
/// to a sorted (by weight) list.
void StackSlotColoring::InitializeSlots() {
  int LastFI = MFI->getObjectIndexEnd();

  // There is always at least one stack ID.
  AllColors.resize(1);
  UsedColors.resize(1);

  OrigAlignments.resize(LastFI);
  OrigSizes.resize(LastFI);
  AllColors[0].resize(LastFI);
  UsedColors[0].resize(LastFI);
  Assignments.resize(LastFI);

  using Pair = std::iterator_traits<LiveStacks::iterator>::value_type;

  SmallVector<Pair *, 16> Intervals;

  Intervals.reserve(LS->getNumIntervals());
  for (auto &I : *LS)
    Intervals.push_back(&I);
  llvm::sort(Intervals,
             [](Pair *LHS, Pair *RHS) { return LHS->first < RHS->first; });

  // Gather all spill slots into a list.
  LLVM_DEBUG(dbgs() << "Spill slot intervals:\n");
  for (auto *I : Intervals) {
    LiveInterval &li = I->second;
    LLVM_DEBUG(li.dump());
    int FI = TargetRegisterInfo::stackSlot2Index(li.reg);
    if (MFI->isDeadObjectIndex(FI))
      continue;

    SSIntervals.push_back(&li);
    OrigAlignments[FI] = MFI->getObjectAlignment(FI);
    OrigSizes[FI]      = MFI->getObjectSize(FI);

    auto StackID = MFI->getStackID(FI);
    if (StackID != 0) {
      AllColors.resize(StackID + 1);
      UsedColors.resize(StackID + 1);
      AllColors[StackID].resize(LastFI);
      UsedColors[StackID].resize(LastFI);
    }

    AllColors[StackID].set(FI);
  }
  LLVM_DEBUG(dbgs() << '\n');

  // Sort them by weight.
  std::stable_sort(SSIntervals.begin(), SSIntervals.end(), IntervalSorter());

  NextColors.resize(AllColors.size());

  for (unsigned I = 0, E = AllColors.size(); I != E; ++I)
    NextColors[I] = AllColors[I].find_first();
}

/// OverlapWithAssignments - Return true if LiveInterval overlaps with any
/// LiveIntervals that have already been assigned to the specified color.

/// ColorSlot - Assign a "color" (stack slot) to the specified stack slot.
int StackSlotColoring::ColorSlot(LiveInterval *li) {
  int Color = -1;
  bool Share = false;
  int FI = TargetRegisterInfo::stackSlot2Index(li->reg);
  uint8_t StackID = MFI->getStackID(FI);

  if (!DisableSharing) {

    // Check if it's possible to reuse any of the used colors.
    Color = UsedColors[StackID].find_first();
    while (Color != -1) {
      if (!OverlapWithAssignments(li, Color)) {
        Share = true;
        ++NumEliminated;
        break;
      }
      Color = UsedColors[StackID].find_next(Color);
    }
  }

  if (Color != -1 && MFI->getStackID(Color) != MFI->getStackID(FI)) {
    LLVM_DEBUG(dbgs() << "cannot share FIs with different stack IDs\n");
    Share = false;
  }

  // Assign it to the first available color (assumed to be the best) if it's
  // not possible to share a used color with other objects.
  if (!Share) {
    assert(NextColors[StackID] != -1 && "No more spill slots?");
    Color = NextColors[StackID];
    UsedColors[StackID].set(Color);
    NextColors[StackID] = AllColors[StackID].find_next(NextColors[StackID]);
  }

  assert(MFI->getStackID(Color) == MFI->getStackID(FI));

  // Record the assignment.
  Assignments[Color].push_back(li);
  LLVM_DEBUG(dbgs() << "Assigning fi#" << FI << " to fi#" << Color << "\n");

  // Change size and alignment of the allocated slot. If there are multiple
  // objects sharing the same slot, then make sure the size and alignment
  // are large enough for all.
  unsigned Align = OrigAlignments[FI];
  if (!Share || Align > MFI->getObjectAlignment(Color))
    MFI->setObjectAlignment(Color, Align);
  int64_t Size = OrigSizes[FI];
  if (!Share || Size > MFI->getObjectSize(Color))
    MFI->setObjectSize(Color, Size);
  return Color;
}

/// Colorslots - Color all spill stack slots and rewrite all frameindex machine
/// operands in the function.
bool StackSlotColoring::ColorSlots(MachineFunction &MF) {
  unsigned NumObjs = MFI->getObjectIndexEnd();
  SmallVector<int, 16> SlotMapping(NumObjs, -1);
  SmallVector<float, 16> SlotWeights(NumObjs, 0.0);
  SmallVector<SmallVector<int, 4>, 16> RevMap(NumObjs);
  BitVector UsedColors(NumObjs);

  LLVM_DEBUG(dbgs() << "Color spill slot intervals:\n");
  bool Changed = false;
  for (unsigned i = 0, e = SSIntervals.size(); i != e; ++i) {
    LiveInterval *li = SSIntervals[i];
    int SS = TargetRegisterInfo::stackSlot2Index(li->reg);
    int NewSS = ColorSlot(li);
    assert(NewSS >= 0 && "Stack coloring failed?");
    SlotMapping[SS] = NewSS;
    RevMap[NewSS].push_back(SS);
    SlotWeights[NewSS] += li->weight;
    UsedColors.set(NewSS);
    Changed |= (SS != NewSS);
  }

  LLVM_DEBUG(dbgs() << "\nSpill slots after coloring:\n");
  for (unsigned i = 0, e = SSIntervals.size(); i != e; ++i) {
    LiveInterval *li = SSIntervals[i];
    int SS = TargetRegisterInfo::stackSlot2Index(li->reg);
    li->weight = SlotWeights[SS];
  }
  // Sort them by new weight.
  std::stable_sort(SSIntervals.begin(), SSIntervals.end(), IntervalSorter());

#ifndef NDEBUG
  for (unsigned i = 0, e = SSIntervals.size(); i != e; ++i)
    LLVM_DEBUG(SSIntervals[i]->dump());
  LLVM_DEBUG(dbgs() << '\n');
#endif

  if (!Changed)
    return false;

  // Rewrite all MachineMemOperands.
  for (unsigned SS = 0, SE = SSRefs.size(); SS != SE; ++SS) {
    int NewFI = SlotMapping[SS];
    if (NewFI == -1 || (NewFI == (int)SS))
      continue;

    const PseudoSourceValue *NewSV = MF.getPSVManager().getFixedStack(NewFI);
    SmallVectorImpl<MachineMemOperand *> &RefMMOs = SSRefs[SS];
    for (unsigned i = 0, e = RefMMOs.size(); i != e; ++i)
      RefMMOs[i]->setValue(NewSV);
  }

  // Rewrite all MO_FrameIndex operands.  Look for dead stores.
  for (MachineBasicBlock &MBB : MF) {
    for (MachineInstr &MI : MBB)
      RewriteInstruction(MI, SlotMapping, MF);
    RemoveDeadStores(&MBB);
  }

  // Delete unused stack slots.
  for (int StackID = 0, E = AllColors.size(); StackID != E; ++StackID) {
    int NextColor = NextColors[StackID];
    while (NextColor != -1) {
      LLVM_DEBUG(dbgs() << "Removing unused stack object fi#" << NextColor << "\n");
      MFI->RemoveStackObject(NextColor);
      NextColor = AllColors[StackID].find_next(NextColor);
    }
  }

  return true;
}

/// RewriteInstruction - Rewrite specified instruction by replacing references
/// to old frame index with new one.
void StackSlotColoring::RewriteInstruction(MachineInstr &MI,
                                           SmallVectorImpl<int> &SlotMapping,
                                           MachineFunction &MF) {
  // Update the operands.
  for (unsigned i = 0, ee = MI.getNumOperands(); i != ee; ++i) {
    MachineOperand &MO = MI.getOperand(i);
    if (!MO.isFI())
      continue;
    int OldFI = MO.getIndex();
    if (OldFI < 0)
      continue;
    int NewFI = SlotMapping[OldFI];
    if (NewFI == -1 || NewFI == OldFI)
      continue;

    assert(MFI->getStackID(OldFI) == MFI->getStackID(NewFI));
    MO.setIndex(NewFI);
  }

  // The MachineMemOperands have already been updated.
}

/// RemoveDeadStores - Scan through a basic block and look for loads followed
/// by stores.  If they're both using the same stack slot, then the store is
/// definitely dead.  This could obviously be much more aggressive (consider
/// pairs with instructions between them), but such extensions might have a
/// considerable compile time impact.
bool StackSlotColoring::RemoveDeadStores(MachineBasicBlock* MBB) {
  // FIXME: This could be much more aggressive, but we need to investigate
  // the compile time impact of doing so.
  bool changed = false;

  SmallVector<MachineInstr*, 4> toErase;

  for (MachineBasicBlock::iterator I = MBB->begin(), E = MBB->end();
       I != E; ++I) {
    if (DCELimit != -1 && (int)NumDead >= DCELimit)
      break;
    int FirstSS, SecondSS;
    if (TII->isStackSlotCopy(*I, FirstSS, SecondSS) && FirstSS == SecondSS &&
        FirstSS != -1) {
      ++NumDead;
      changed = true;
      toErase.push_back(&*I);
      continue;
    }

    MachineBasicBlock::iterator NextMI = std::next(I);
    MachineBasicBlock::iterator ProbableLoadMI = I;

    unsigned LoadReg = 0;
    unsigned StoreReg = 0;
    unsigned LoadSize = 0;
    unsigned StoreSize = 0;
    if (!(LoadReg = TII->isLoadFromStackSlot(*I, FirstSS, LoadSize)))
      continue;
    // Skip the ...pseudo debugging... instructions between a load and store.
    while ((NextMI != E) && NextMI->isDebugInstr()) {
      ++NextMI;
      ++I;
    }
    if (NextMI == E) continue;
    if (!(StoreReg = TII->isStoreToStackSlot(*NextMI, SecondSS, StoreSize)))
      continue;
    if (FirstSS != SecondSS || LoadReg != StoreReg || FirstSS == -1 ||
        LoadSize != StoreSize)
      continue;

    ++NumDead;
    changed = true;

    if (NextMI->findRegisterUseOperandIdx(LoadReg, true, nullptr) != -1) {
      ++NumDead;
      toErase.push_back(&*ProbableLoadMI);
    }

    toErase.push_back(&*NextMI);
    ++I;
  }

  for (SmallVectorImpl<MachineInstr *>::iterator I = toErase.begin(),
       E = toErase.end(); I != E; ++I)
    (*I)->eraseFromParent();

  return changed;
}

bool StackSlotColoring::runOnMachineFunction(MachineFunction &MF) {
  LLVM_DEBUG({
    dbgs() << "********** Stack Slot Coloring **********\n"
           << "********** Function: " << MF.getName() << '\n';
  });

  if (skipFunction(MF.getFunction()))
    return false;

  MFI = &MF.getFrameInfo();
  TII = MF.getSubtarget().getInstrInfo();
  LS = &getAnalysis<LiveStacks>();
  MBFI = &getAnalysis<MachineBlockFrequencyInfo>();

  bool Changed = false;

  unsigned NumSlots = LS->getNumIntervals();
  if (NumSlots == 0)
    return false;

  // If there are calls to setjmp or sigsetjmp, don't perform stack slot
  // coloring. The stack could be modified before the longjmp is executed,
  // resulting in the wrong value being used afterwards. (See
  if (MF.exposesReturnsTwice())
    return false;

  // Gather spill slot references
  ScanForSpillSlotRefs(MF);
  InitializeSlots();
  Changed = ColorSlots(MF);

  for (int &Next : NextColors)
    Next = -1;

  SSIntervals.clear();
  for (unsigned i = 0, e = SSRefs.size(); i != e; ++i)
    SSRefs[i].clear();
  SSRefs.clear();
  OrigAlignments.clear();
  OrigSizes.clear();
  AllColors.clear();
  UsedColors.clear();
  for (unsigned i = 0, e = Assignments.size(); i != e; ++i)
    Assignments[i].clear();
  Assignments.clear();

  return Changed;
}


label_t load_optional_label(const config& conf, string section, string name, string def) {
    return load_label(conf, move(section), move(name), false, move(def));
  }

#include "drawtypes/label.hpp"

#include <cmath>
#include <utility>

#include "utils/string.hpp"

POLYBAR_NS

namespace drawtypes {
  /**
   * Gets the text from the label as it should be rendered
   *
   * Here tokens are replaced with values and minlen and maxlen properties are applied
   */
  string label::get() const {
    const size_t len = string_util::char_len(m_tokenized);
    if (len >= m_minlen) {
      string text = m_tokenized;
      if (m_maxlen > 0 && len > m_maxlen) {
        if (m_ellipsis) {
          text = string_util::utf8_truncate(std::move(text), m_maxlen - 3) + "...";
        } else {
          text = string_util::utf8_truncate(std::move(text), m_maxlen);
        }
      }
      return text;
    }

    const size_t num_fill_chars = m_minlen - len;
    size_t right_fill_len = 0;
    size_t left_fill_len = 0;
    if (m_alignment == alignment::RIGHT) {
      left_fill_len = num_fill_chars;
    } else if (m_alignment == alignment::LEFT) {
      right_fill_len = num_fill_chars;
    } else {
      right_fill_len = std::ceil(num_fill_chars / 2.0);
      left_fill_len = right_fill_len;
      // The text is positioned one character to the left if we can't perfectly center it
      if (len + left_fill_len + right_fill_len > m_minlen) {
        --left_fill_len;
      }
    }
    return string(left_fill_len, ' ') + m_tokenized + string(right_fill_len, ' ');
  }

  label::operator bool() {
    return !m_tokenized.empty();
  }

  label_t label::clone() {
    vector<token> tokens;
    if (!m_tokens.empty()) {
      std::back_insert_iterator<decltype(tokens)> back_it(tokens);
      std::copy(m_tokens.begin(), m_tokens.end(), back_it);
    }
    return std::make_shared<label>(m_text, m_foreground, m_background, m_underline, m_overline, m_font, m_padding,
        m_margin, m_minlen, m_maxlen, m_alignment, m_ellipsis, move(tokens));
  }

  void label::clear() {
    m_tokenized.clear();
  }

  void label::reset_tokens() {
    m_tokenized = m_text;
  }

  void label::reset_tokens(const string& tokenized) {
    m_tokenized = tokenized;
  }

  bool label::has_token(const string& token) const {
    return m_tokenized.find(token) != string::npos;
  }

  void label::replace_token(const string& token, string replacement) {
    if (!has_token(token)) {
      return;
    }

    for (auto&& tok : m_tokens) {
      string repl{replacement};
      if (token == tok.token) {
        if (tok.max != 0_z && string_util::char_len(repl) > tok.max) {
          repl = string_util::utf8_truncate(std::move(repl), tok.max) + tok.suffix;
        } else if (tok.min != 0_z && repl.length() < tok.min) {
          repl.insert(0_z, tok.min - repl.length(), tok.zpad ? '0' : ' ');
        }

        /*
         * Only replace first occurence, so that the proper token objects can be used
         */
        m_tokenized = string_util::replace(m_tokenized, token, move(repl));
      }
    }
  }

  void label::replace_defined_values(const label_t& label) {
    if (label->m_foreground.has_color()) {
      m_foreground = label->m_foreground;
    }
    if (label->m_background.has_color()) {
      m_background = label->m_background;
    }
    if (label->m_underline.has_color()) {
      m_underline = label->m_underline;
    }
    if (label->m_overline.has_color()) {
      m_overline = label->m_overline;
    }
    if (label->m_font != 0) {
      m_font = label->m_font;
    }
    if (label->m_padding.left != 0U) {
      m_padding.left = label->m_padding.left;
    }
    if (label->m_padding.right != 0U) {
      m_padding.right = label->m_padding.right;
    }
    if (label->m_margin.left != 0U) {
      m_margin.left = label->m_margin.left;
    }
    if (label->m_margin.right != 0U) {
      m_margin.right = label->m_margin.right;
    }
    if (label->m_maxlen != 0_z) {
      m_maxlen = label->m_maxlen;
      m_ellipsis = label->m_ellipsis;
    }
  }

  void label::copy_undefined(const label_t& label) {
    if (!m_foreground.has_color() && label->m_foreground.has_color()) {
      m_foreground = label->m_foreground;
    }
    if (!m_background.has_color() && label->m_background.has_color()) {
      m_background = label->m_background;
    }
    if (!m_underline.has_color() && label->m_underline.has_color()) {
      m_underline = label->m_underline;
    }
    if (!m_overline.has_color() && label->m_overline.has_color()) {
      m_overline = label->m_overline;
    }
    if (m_font == 0 && label->m_font != 0) {
      m_font = label->m_font;
    }
    if (m_padding.left == 0U && label->m_padding.left != 0U) {
      m_padding.left = label->m_padding.left;
    }
    if (m_padding.right == 0U && label->m_padding.right != 0U) {
      m_padding.right = label->m_padding.right;
    }
    if (m_margin.left == 0U && label->m_margin.left != 0U) {
      m_margin.left = label->m_margin.left;
    }
    if (m_margin.right == 0U && label->m_margin.right != 0U) {
      m_margin.right = label->m_margin.right;
    }
    if (m_maxlen == 0_z && label->m_maxlen != 0_z) {
      m_maxlen = label->m_maxlen;
      m_ellipsis = label->m_ellipsis;
    }
  }

  /**
   * Create a label by loading values from the configuration
   */
  label_t load_label(const config& conf, const string& section, string name, bool required, string def) {
    vector<token> tokens;
    size_t start, end, pos;

    name = string_util::ltrim(string_util::rtrim(move(name), '>'), '<');

    string text;

    struct side_values padding {
    }, margin{};

    if (required) {
      text = conf.get(section, name);
    } else {
      text = conf.get(section, name, move(def));
    }

    const auto get_left_right = [&](string key) {
      auto value = conf.get(section, key, 0U);
      auto left = conf.get(section, key + "-left", value);
      auto right = conf.get(section, key + "-right", value);
      return side_values{static_cast<unsigned short int>(left), static_cast<unsigned short int>(right)};
    };

    padding = get_left_right(name + "-padding");
    margin = get_left_right(name + "-margin");

    string line{text};

    while ((start = line.find('%')) != string::npos && (end = line.find('%', start + 1)) != string::npos) {
      auto token_str = line.substr(start, end - start + 1);

      // ignore false positives
      //   trailing percentage signs %token%%
      if (token_str.find_first_of("abcdefghijklmnopqrstuvwxyz") != 1) {
        line.erase(0, end);
        continue;
      }

      line.erase(start, end - start + 1);
      tokens.emplace_back(token{token_str, 0_z, 0_z});
      auto& token = tokens.back();

      // find min delimiter
      if ((pos = token_str.find(':')) == string::npos) {
        continue;
      }

      // strip min/max specifiers from the label string token
      token.token = token_str.substr(0, pos) + '%';
      text = string_util::replace(text, token_str, token.token);

      try {
        token.min = std::stoul(&token_str[pos + 1], nullptr, 10);
        // When the number starts with 0 the string is 0-padded
        token.zpad = token_str[pos + 1] == '0';
      } catch (const std::invalid_argument& err) {
        continue;
      }

      // find max delimiter
      if ((pos = token_str.find(':', pos + 1)) == string::npos) {
        continue;
      }

      try {
        token.max = std::stoul(&token_str[pos + 1], nullptr, 10);
      } catch (const std::invalid_argument& err) {
        continue;
      }

      // ignore max lengths less than min
      if (token.max < token.min) {
        token.max = 0_z;
      }

      // find suffix delimiter
      if ((pos = token_str.find(':', pos + 1)) != string::npos) {
        token.suffix = token_str.substr(pos + 1, token_str.size() - pos - 2);
      }
    }
    size_t minlen = conf.get(section, name + "-minlen", 0_z);
    string alignment_conf_value = conf.get(section, name + "-alignment", "left"s);
    alignment label_alignment;
    if (alignment_conf_value == "right") {
      label_alignment = alignment::RIGHT;
    } else if (alignment_conf_value == "left") {
      label_alignment = alignment::LEFT;
    } else if (alignment_conf_value == "center") {
      label_alignment = alignment::CENTER;
    } else {
      throw application_error(sstream() << "Label " << section << "." << name << " has invalid alignment "
                                        << alignment_conf_value << ", expecting one of: right, left, center.");
    }

    size_t maxlen = conf.get(section, name + "-maxlen", 0_z);
    if (maxlen > 0 && maxlen < minlen) {
      throw application_error(sstream() << "Label " << section << "." << name << " has maxlen " << maxlen
                                        << " which is smaller than minlen " << minlen);
    }
    bool ellipsis = conf.get(section, name + "-ellipsis", true);

    if (ellipsis && maxlen > 0 && maxlen < 3) {
      throw application_error(sstream() << "Label " << section << "." << name << " has maxlen " << maxlen
                                        << ", which is smaller than length of ellipsis (3)");
    }

    // clang-format off
    return std::make_shared<label>(text,
        conf.get(section, name + "-foreground", rgba{}),
        conf.get(section, name + "-background", rgba{}),
        conf.get(section, name + "-underline", rgba{}),
        conf.get(section, name + "-overline", rgba{}),
        conf.get(section, name + "-font", 0),
        padding,
        margin,
        minlen,
        maxlen,
        label_alignment,
        ellipsis,
        move(tokens));
    // clang-format on
  }

  /**
   * Create a label by loading optional values from the configuration
   */

}  // namespace drawtypes

POLYBAR_NS_END


try {
        token.min = std::stoul(&token_str[pos + 1], nullptr, 10);
        // When the number starts with 0 the string is 0-padded
        token.zpad = token_str[pos + 1] == '0';
      } catch (const std::invalid_argument& err) {
        continue;
      }

#include "drawtypes/label.hpp"

#include <cmath>
#include <utility>

#include "utils/string.hpp"

POLYBAR_NS

namespace drawtypes {
  /**
   * Gets the text from the label as it should be rendered
   *
   * Here tokens are replaced with values and minlen and maxlen properties are applied
   */
  string label::get() const {
    const size_t len = string_util::char_len(m_tokenized);
    if (len >= m_minlen) {
      string text = m_tokenized;
      if (m_maxlen > 0 && len > m_maxlen) {
        if (m_ellipsis) {
          text = string_util::utf8_truncate(std::move(text), m_maxlen - 3) + "...";
        } else {
          text = string_util::utf8_truncate(std::move(text), m_maxlen);
        }
      }
      return text;
    }

    const size_t num_fill_chars = m_minlen - len;
    size_t right_fill_len = 0;
    size_t left_fill_len = 0;
    if (m_alignment == alignment::RIGHT) {
      left_fill_len = num_fill_chars;
    } else if (m_alignment == alignment::LEFT) {
      right_fill_len = num_fill_chars;
    } else {
      right_fill_len = std::ceil(num_fill_chars / 2.0);
      left_fill_len = right_fill_len;
      // The text is positioned one character to the left if we can't perfectly center it
      if (len + left_fill_len + right_fill_len > m_minlen) {
        --left_fill_len;
      }
    }
    return string(left_fill_len, ' ') + m_tokenized + string(right_fill_len, ' ');
  }

  label::operator bool() {
    return !m_tokenized.empty();
  }

  label_t label::clone() {
    vector<token> tokens;
    if (!m_tokens.empty()) {
      std::back_insert_iterator<decltype(tokens)> back_it(tokens);
      std::copy(m_tokens.begin(), m_tokens.end(), back_it);
    }
    return std::make_shared<label>(m_text, m_foreground, m_background, m_underline, m_overline, m_font, m_padding,
        m_margin, m_minlen, m_maxlen, m_alignment, m_ellipsis, move(tokens));
  }

  void label::clear() {
    m_tokenized.clear();
  }

  void label::reset_tokens() {
    m_tokenized = m_text;
  }

  void label::reset_tokens(const string& tokenized) {
    m_tokenized = tokenized;
  }

  bool label::has_token(const string& token) const {
    return m_tokenized.find(token) != string::npos;
  }

  void label::replace_token(const string& token, string replacement) {
    if (!has_token(token)) {
      return;
    }

    for (auto&& tok : m_tokens) {
      string repl{replacement};
      if (token == tok.token) {
        if (tok.max != 0_z && string_util::char_len(repl) > tok.max) {
          repl = string_util::utf8_truncate(std::move(repl), tok.max) + tok.suffix;
        } else if (tok.min != 0_z && repl.length() < tok.min) {
          repl.insert(0_z, tok.min - repl.length(), tok.zpad ? '0' : ' ');
        }

        /*
         * Only replace first occurence, so that the proper token objects can be used
         */
        m_tokenized = string_util::replace(m_tokenized, token, move(repl));
      }
    }
  }

  void label::replace_defined_values(const label_t& label) {
    if (label->m_foreground.has_color()) {
      m_foreground = label->m_foreground;
    }
    if (label->m_background.has_color()) {
      m_background = label->m_background;
    }
    if (label->m_underline.has_color()) {
      m_underline = label->m_underline;
    }
    if (label->m_overline.has_color()) {
      m_overline = label->m_overline;
    }
    if (label->m_font != 0) {
      m_font = label->m_font;
    }
    if (label->m_padding.left != 0U) {
      m_padding.left = label->m_padding.left;
    }
    if (label->m_padding.right != 0U) {
      m_padding.right = label->m_padding.right;
    }
    if (label->m_margin.left != 0U) {
      m_margin.left = label->m_margin.left;
    }
    if (label->m_margin.right != 0U) {
      m_margin.right = label->m_margin.right;
    }
    if (label->m_maxlen != 0_z) {
      m_maxlen = label->m_maxlen;
      m_ellipsis = label->m_ellipsis;
    }
  }

  void label::copy_undefined(const label_t& label) {
    if (!m_foreground.has_color() && label->m_foreground.has_color()) {
      m_foreground = label->m_foreground;
    }
    if (!m_background.has_color() && label->m_background.has_color()) {
      m_background = label->m_background;
    }
    if (!m_underline.has_color() && label->m_underline.has_color()) {
      m_underline = label->m_underline;
    }
    if (!m_overline.has_color() && label->m_overline.has_color()) {
      m_overline = label->m_overline;
    }
    if (m_font == 0 && label->m_font != 0) {
      m_font = label->m_font;
    }
    if (m_padding.left == 0U && label->m_padding.left != 0U) {
      m_padding.left = label->m_padding.left;
    }
    if (m_padding.right == 0U && label->m_padding.right != 0U) {
      m_padding.right = label->m_padding.right;
    }
    if (m_margin.left == 0U && label->m_margin.left != 0U) {
      m_margin.left = label->m_margin.left;
    }
    if (m_margin.right == 0U && label->m_margin.right != 0U) {
      m_margin.right = label->m_margin.right;
    }
    if (m_maxlen == 0_z && label->m_maxlen != 0_z) {
      m_maxlen = label->m_maxlen;
      m_ellipsis = label->m_ellipsis;
    }
  }

  /**
   * Create a label by loading values from the configuration
   */
  label_t load_label(const config& conf, const string& section, string name, bool required, string def) {
    vector<token> tokens;
    size_t start, end, pos;

    name = string_util::ltrim(string_util::rtrim(move(name), '>'), '<');

    string text;

    struct side_values padding {
    }, margin{};

    if (required) {
      text = conf.get(section, name);
    } else {
      text = conf.get(section, name, move(def));
    }

    const auto get_left_right = [&](string key) {
      auto value = conf.get(section, key, 0U);
      auto left = conf.get(section, key + "-left", value);
      auto right = conf.get(section, key + "-right", value);
      return side_values{static_cast<unsigned short int>(left), static_cast<unsigned short int>(right)};
    };

    padding = get_left_right(name + "-padding");
    margin = get_left_right(name + "-margin");

    string line{text};

    while ((start = line.find('%')) != string::npos && (end = line.find('%', start + 1)) != string::npos) {
      auto token_str = line.substr(start, end - start + 1);

      // ignore false positives
      //   trailing percentage signs %token%%
      if (token_str.find_first_of("abcdefghijklmnopqrstuvwxyz") != 1) {
        line.erase(0, end);
        continue;
      }

      line.erase(start, end - start + 1);
      tokens.emplace_back(token{token_str, 0_z, 0_z});
      auto& token = tokens.back();

      // find min delimiter
      if ((pos = token_str.find(':')) == string::npos) {
        continue;
      }

      // strip min/max specifiers from the label string token
      token.token = token_str.substr(0, pos) + '%';
      text = string_util::replace(text, token_str, token.token);

      // find max delimiter
      if ((pos = token_str.find(':', pos + 1)) == string::npos) {
        continue;
      }

      try {
        token.max = std::stoul(&token_str[pos + 1], nullptr, 10);
      } catch (const std::invalid_argument& err) {
        continue;
      }

      // ignore max lengths less than min
      if (token.max < token.min) {
        token.max = 0_z;
      }

      // find suffix delimiter
      if ((pos = token_str.find(':', pos + 1)) != string::npos) {
        token.suffix = token_str.substr(pos + 1, token_str.size() - pos - 2);
      }
    }
    size_t minlen = conf.get(section, name + "-minlen", 0_z);
    string alignment_conf_value = conf.get(section, name + "-alignment", "left"s);
    alignment label_alignment;
    if (alignment_conf_value == "right") {
      label_alignment = alignment::RIGHT;
    } else if (alignment_conf_value == "left") {
      label_alignment = alignment::LEFT;
    } else if (alignment_conf_value == "center") {
      label_alignment = alignment::CENTER;
    } else {
      throw application_error(sstream() << "Label " << section << "." << name << " has invalid alignment "
                                        << alignment_conf_value << ", expecting one of: right, left, center.");
    }

    size_t maxlen = conf.get(section, name + "-maxlen", 0_z);
    if (maxlen > 0 && maxlen < minlen) {
      throw application_error(sstream() << "Label " << section << "." << name << " has maxlen " << maxlen
                                        << " which is smaller than minlen " << minlen);
    }
    bool ellipsis = conf.get(section, name + "-ellipsis", true);

    if (ellipsis && maxlen > 0 && maxlen < 3) {
      throw application_error(sstream() << "Label " << section << "." << name << " has maxlen " << maxlen
                                        << ", which is smaller than length of ellipsis (3)");
    }

    // clang-format off
    return std::make_shared<label>(text,
        conf.get(section, name + "-foreground", rgba{}),
        conf.get(section, name + "-background", rgba{}),
        conf.get(section, name + "-underline", rgba{}),
        conf.get(section, name + "-overline", rgba{}),
        conf.get(section, name + "-font", 0),
        padding,
        margin,
        minlen,
        maxlen,
        label_alignment,
        ellipsis,
        move(tokens));
    // clang-format on
  }

  /**
   * Create a label by loading optional values from the configuration
   */
  label_t load_optional_label(const config& conf, string section, string name, string def) {
    return load_label(conf, move(section), move(name), false, move(def));
  }

}  // namespace drawtypes

POLYBAR_NS_END