/*
    PIP - Platform Independent Primitives
    Basic PIScreen tile
    Ivan Pelipenko peri4ko@yandex.ru

    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 3 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, see <http://www.gnu.org/licenses/>.
*/

#include "piscreentile.h"

#include "piscreendrawer.h"


using namespace PIScreenTypes;


PIScreenTile::PIScreenTile(const PIString & n, Direction d, SizePolicy p): PIObject(n) {
	direction    = d;
	size_policy  = p;
	focus_flags  = 0;
	screen       = 0;
	minimumWidth = minimumHeight = x_ = y_ = width_ = height_ = pw = ph = 0;
	maximumWidth = maximumHeight = 65535;
	marginLeft = marginRight = marginTop = marginBottom = spacing = 0;
	parent                                                        = 0;
	back_symbol                                                   = ' ';
	visible                                                       = true;
	has_focus                                                     = false;
}


PIScreenTile::~PIScreenTile() {
	// piCout << this << "~";
	if (screen) screen->tileRemovedInternal(this);
	setScreen(0);
	deleteChildren();
	if (!parent) return;
	parent->tiles.removeOne(this);
}


void PIScreenTile::addTile(PIScreenTile * t) {
	if (t == 0) return;
	if (tiles.contains(t)) return;
	tiles << t;
	t->parent = this;
	t->setScreen(screen);
}


void PIScreenTile::takeTile(PIScreenTile * t) {
	if (!tiles.contains(t)) return;
	tiles.removeOne(t);
	t->parent = 0;
	t->setScreen(0);
}


void PIScreenTile::removeTile(PIScreenTile * t) {
	if (!tiles.contains(t)) return;
	tiles.removeOne(t);
	t->parent = 0;
	t->setScreen(0);
	delete t;
}


PIVector<PIScreenTile *> PIScreenTile::children(bool only_visible) {
	PIVector<PIScreenTile *> ret;
	for (auto * t: tiles)
		if (t->visible || !only_visible) ret << t << t->children(only_visible);
	return ret;
}


PIScreenTile * PIScreenTile::childUnderMouse(int x, int y) {
	for (auto * t: tiles) {
		if (!t->visible) continue;
		if (x >= t->x_ && (x - t->x_) < t->width_ && y >= t->y_ && (y - t->y_) < t->height_) {
			return t;
		}
	}
	return 0;
}


void PIScreenTile::raiseEvent(TileEvent e) {
	if (!screen) return;
	screen->tileEventInternal(this, e);
}


void PIScreenTile::setScreen(PIScreenBase * s) {
	screen = s;
	for (auto * t: tiles)
		t->setScreen(s);
}


void PIScreenTile::deleteChildren() {
	for (auto * t: tiles) {
		t->parent = 0;
		delete t;
	}
	tiles.clear();
}


void PIScreenTile::setFocus() {
	if (!screen || !focus_flags[CanHasFocus]) return;
	screen->tileSetFocusInternal(this);
}


void PIScreenTile::drawEventInternal(PIScreenDrawer * d) {
	if (!visible) {
		return;
	}
	d->fillRect(x_,
	            y_,
	            x_ + width_,
	            y_ + height_,
	            back_symbol,
	            (Color)back_format.color_char,
	            (Color)back_format.color_back,
	            back_format.flags);
	drawEvent(d);
	for (auto * t: tiles)
		t->drawEventInternal(d);
}


void PIScreenTile::sizeHint(int & w, int & h) const {
	w = 0;
	h = 0;
	if (tiles.isEmpty()) return;
	int sl = spacing * (tiles.size_s() - 1);
	if (direction == Horizontal)
		w += sl;
	else
		h += sl;
	for (const auto * t: tiles) {
		if (!t->visible) continue;
		int cw(0), ch(0);
		t->sizeHint(cw, ch);
		cw = piClampi(cw, t->minimumWidth, t->maximumWidth);
		ch = piClampi(ch, t->minimumHeight, t->maximumHeight);
		if (direction == Horizontal) {
			w += cw;
			h = piMaxi(h, ch);
		} else {
			h += ch;
			w = piMaxi(w, cw);
		}
	}
	w += marginLeft + marginRight;
	h += marginTop + marginBottom;
}


void PIScreenTile::layout() {
	if (tiles.isEmpty() || !visible) return;
	int as(0), ts(0), ts2(0), ecnt(0), pcnt(0);
	ts  = (direction == Horizontal) ? (width_ - marginLeft - marginRight) : (height_ - marginTop - marginBottom);
	ts2 = (direction != Horizontal) ? (width_ - marginLeft - marginRight) : (height_ - marginTop - marginBottom);
	ts -= spacing * (tiles.size_s() - 1);
	PIVector<int> hints(tiles.size_s());
	PIVector<float> asizes(tiles.size_s());
	for (int i = 0; i < tiles.size_s(); ++i) {
		PIScreenTile * t(tiles[i]);
		int cw(0), ch(0), cs(0);
		if (t->visible && t->needLayout()) {
			t->sizeHint(cw, ch);
			cw = piClampi(cw, t->minimumWidth, t->maximumWidth);
			ch = piClampi(ch, t->minimumHeight, t->maximumHeight);
			if (t->size_policy == Expanding) ++ecnt;
			if (t->size_policy == Preferred) ++pcnt;
			cs = (direction == Horizontal) ? cw : ch;
			as += cs;
		}
		hints[i]  = cs;
		asizes[i] = 0.f;
	}
	if (as <= ts) {
		int acnt(0);
		SizePolicy pol = Fixed;
		if (ecnt > 0) {
			acnt = ecnt;
			pol  = Expanding;
		} else if (pcnt > 0) {
			acnt = pcnt;
			pol  = Preferred;
		}
		if (acnt > 0) {
			float add_a = float(ts - as), add_s = add_a / acnt, add_da(0.);
			asizes.fill(add_s);
			PISet<int> max_tl;
			for (int i = 0; i < tiles.size_s(); ++i) {
				if (tiles[i]->size_policy == pol && tiles[i]->visible && tiles[i]->needLayout()) {
					float maxs = (direction == Horizontal) ? tiles[i]->maximumWidth : tiles[i]->maximumHeight;
					if (hints[i] + asizes[i] > maxs) {
						max_tl << i;
						float pas = asizes[i];
						asizes[i] = maxs - hints[i];
						acnt--;
						if (acnt > 0) {
							pas = (pas - asizes[i]) / acnt;
							for (int j = 0; j < tiles.size_s(); ++j) {
								if (i == j) continue;
								if (max_tl[j]) continue;
								if (tiles[j]->size_policy == pol && tiles[j]->visible && tiles[j]->needLayout()) asizes[j] += pas;
							}
						}
					}
				}
			}
			for (int i = 0; i < tiles.size_s(); ++i) {
				if (tiles[i]->size_policy == pol && tiles[i]->visible && tiles[i]->needLayout()) {
					int a = piRound(asizes[i] + add_da);
					add_da += asizes[i] - a;
					hints[i] += a;
				}
			}
		}
	}
	int cx = x_ + marginLeft, cy = y_ + marginTop;
	for (int i = 0; i < tiles.size_s(); ++i) {
		PIScreenTile * t = tiles[i];
		if (!t->visible || !t->needLayout()) continue;
		t->x_ = cx;
		t->y_ = cy;
		if (direction == Horizontal) {
			t->width_  = hints[i];
			t->height_ = ts2;
			cx += hints[i] + spacing;
		} else {
			t->width_  = ts2;
			t->height_ = hints[i];
			cy += hints[i] + spacing;
		}
		if (t->pw != t->width_ || t->ph != t->height_) t->resizeEvent(t->width_, t->height_);
		t->pw = t->width_;
		t->ph = t->height_;
		t->layout();
	}
}
