/*------------------------------------------------------------------------------
 src/lib/djWidget/rowLayout.cxx

 Copyright (c) 2004-2006 Darby Johnston.
 All rights reserved.
 Use is subject to license terms.
------------------------------------------------------------------------------*/

//#define DJ_DBG
#include "djLayout.h"

namespace dj {

class _RowLayoutSpacer : public LayoutSpacer {
public:

  _RowLayoutSpacer(const Vec2i & in) :
    _size(in)
  {}

  const Vec2i sizeHint() const { return _size; }
  SIZE_POLICY horizontal() const { return FIXED; }
  SIZE_POLICY vertical() const { return FIXED; }

private:
  Vec2i _size;
};
  
class _RowLayoutStretch : public LayoutSpacer {};

RowLayout::RowLayout(LayoutItem * parent) :
  Layout(parent),
  _stretch(-1)
{}

void RowLayout::spacerAdd(const Vec2i & in)
{
  add(new _RowLayoutSpacer(in));
}

void RowLayout::stretchAdd(bool stretchItem)
{
  add(new _RowLayoutStretch);
  if (stretchItem) stretch(itemList().size() - 1);
}

void RowLayout::stretch(int in)
{
  if (in == _stretch) return;
  
  _stretch = in;
  
  layoutUpdate();
}

void RowLayout::stretch(const LayoutItem * in)
{
  for (ConstListIt<LayoutItem *> it = itemList(); it.isValid(); ++it)
    if (in == it()) {
      stretch(it.count());
      break;
    }
}

const List<Box2i> RowLayout::layout(
  const List<LayoutItem *> & in,
  const Vec2i & size, TYPE type, int spacing, int stretch
) {
  DJ_DBG_FNC("dj::RowLayout::layout");
  DJ_DBG_PRN("in = " << in.size());
  DJ_DBG_PRN("size = " << size);
  DJ_DBG_PRN("type = " << type);
  DJ_DBG_PRN("stretch = " << stretch);
  
  List<Box2i> out;
  
  // Calculate sizes.
  //
  int totalSize = 0, stretchItemList = 0;
  LayoutItem * itemPrev = 0;
  for (ConstListIt<LayoutItem *> it = in; it.isValid(); ++it) {
    LayoutItem * item = it();
    Vec2i size;
    if (item->visible()) {
      //size = max(item->sizeHint(), item->geom().size());
      size = item->sizeHint();
      if (SPACER != item->type() && itemPrev)
        if (SPACER != itemPrev->type() && itemPrev->visible())
          totalSize += spacing;
      itemPrev = item;
      totalSize += size[type];
      if (STRETCH == item->sizePolicy()[type])
        ++stretchItemList;
    }
    out += size;
  }
  const int stretchSize = size[type] - totalSize;
  DJ_DBG_PRN("total size = " << totalSize);
  DJ_DBG_PRN("stretch items = " << stretchItemList);
  DJ_DBG_PRN("stretch size = " << stretchSize);
  
  // Layout items.
  //
  itemPrev = 0;
  ListIt<Box2i> boxIt = out;
  Vec2i position;
  for (ConstListIt<LayoutItem *> it = in; it.isValid(); ++it, ++boxIt) {
    LayoutItem * item = it();
    Vec2i size = boxIt().size();
    if (item->visible()) {
      if (SPACER != item->type() && itemPrev)
        if (SPACER != itemPrev->type() && itemPrev->visible()) {
          DJ_DBG_PRN("spacing");
          position[type] += spacing;
        }
      itemPrev = item;
      if (stretchSize > 0 && STRETCH == item->sizePolicy()[type]) {
        if (it.count() == stretch)
          size[type] += stretchSize;
        else if (-1 == stretch)
          size[type] += stretchSize / stretchItemList;
      }
    }
    DJ_DBG_PRN("item = " << Box2i(position, size));
    boxIt() = Box2i(position, size);
    position[type] += size[type];
  }
  
  return out;
}

HLayout::HLayout(LayoutItem * parent) :
  RowLayout(parent)
{
  //sizePolicy(STRETCH, FIXED);
}

void HLayout::spacerAdd(int in)
{
  RowLayout::spacerAdd(Vec2i(in, 0));
}

const Vec2i HLayout::sizeHint() const
{
  DJ_DBG_FNC("dj::HLayout::sizeHint");

  const List<Box2i> geom = layout(
    itemList(), Vec2i(), HORIZONTAL, spacing(), stretch()
  );
  int w = 0, h = 0;
  if (geom.size())
    w = geom[geom.size() - 1].p1().x() + 1 + margin() * 2;
  for (ConstListIt<Box2i> it = geom; it.isValid(); ++it)
    h = max(h, it().height());

  const Vec2i out(w, h + margin() * 2);
  DJ_DBG_PRN("out = " << out);
  return out;
}

void HLayout::resize(int x, int y, int w, int h)
{
  RowLayout::resize(x, y, w, h);
  
  DJ_DBG_FNC("dj::HLayout::resize");
  DJ_DBG_PRN("in = " << Box2i(x, y, w, h));
  
  const List<Box2i> geom = layout(
    itemList(), Vec2i(w, h) - margin() * 2, HORIZONTAL, spacing(), stretch()
  );
  for (ConstListIt<Box2i> it = geom; it.isValid(); ++it) {
    DJ_DBG_PRN("item = " << it());
    LayoutItem * item = itemList()[it.count()];
    
    int _y = it().y(), _h = it().height();
    if (STRETCH == item->sizePolicy()[VERTICAL])
      _h = max(h - margin() * 2, _h);
    else
      switch (item->alignPolicy()[VERTICAL]) {
        case LEFT: break;
        case CENTER: _y += ((h - margin() * 2) - _h) / 2; break;
        case RIGHT: break;
      }
    
    item->resize(
      x + margin() + it().x(), y + margin() + _y,
      it().width(), _h
    );
  }
}

VLayout::VLayout(LayoutItem * parent) :
  RowLayout(parent)
{
  //sizePolicy(FIXED, STRETCH);
}

void VLayout::spacerAdd(int in)
{
  RowLayout::spacerAdd(Vec2i(0, in));
}

const Vec2i VLayout::sizeHint() const
{
  DJ_DBG_FNC("dj::VLayout::sizeHint");
  
  const List<Box2i> geom = layout(
    itemList(), Vec2i(), VERTICAL, spacing(), stretch()
  );
  int w = 0, h = 0;
  for (ConstListIt<Box2i> it = geom; it.isValid(); ++it)
    w = max(w, it().width());
  if (geom.size())
    h = geom[geom.size() - 1].p1().y() + 1 + margin() * 2;
  
  const Vec2i out(w + margin() * 2, h);
  DJ_DBG_PRN("out = " << out);
  return out;
}

void VLayout::resize(int x, int y, int w, int h)
{
  RowLayout::resize(x, y, w, h);
  
  DJ_DBG_FNC("dj::VLayout::resize");
  DJ_DBG_PRN("in = " << Box2i(x, y, w, h));
  
  const List<Box2i> geom = layout(
    itemList(), Vec2i(w, h) - margin() * 2, VERTICAL, spacing(), stretch()
  );
  for (ConstListIt<Box2i> it = geom; it.isValid(); ++it) {
    DJ_DBG_PRN("item = " << it());
    LayoutItem * item = itemList()[it.count()];
    
    int _x = it().x(), _w = it().width();
    if (STRETCH == item->sizePolicy()[HORIZONTAL])
      _w = max(w - margin() * 2, _w);
    else
      switch (item->alignPolicy()[HORIZONTAL]) {
        case LEFT: break;
        case CENTER: _x += ((w - margin() * 2) - _w) / 2; break;
        case RIGHT: break;
      }
      
    item->resize(
      x + margin() + _x, y + margin() + it().y(),
      _w, it().height()
    );
  }
}

}
