/***************************************************************************
 *   Copyright (C) 2004 by Hans Oischinger                                 *
 *   hans.oischinger@kde-mail.net                                          *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "komposelayout.h"

#include "komposewidget.h"
#include "komposeviewmanager.h"
#include "komposetaskmanager.h"
#include "komposefullscreenwidget.h"

#include <math.h>

#include <twin.h>
#include <kdebug.h>


KomposeLayout::KomposeLayout( KomposeWidget *parent, int type, int dist, const char *name )
    : TQObject(parent, name),
    spacing(dist),
    widgetsChanged(false),
    currentRows(0),
    currentColumns(0),
    parentWidget(parent)
{
  setType( type );
  currentSize = TQSize( 1, 1 );
}

KomposeLayout::~KomposeLayout()
{}

void KomposeLayout::setType( int t )
{
  /*
  if ( t == TLAYOUT_TASKCONTAINERS &&
       !KomposeSettings::instance()->getDynamicVirtDeskLayout() )
    t = TLAYOUT_GENERIC;

  layoutType = t;
  */
  layoutType = TLAYOUT_GENERIC;
}


void KomposeLayout::add( KomposeWidget *w )
{
  //kdDebug() << "KomposeLayout::add()@%s - Added widget to layout", parent()->className());
  //kdDebug() << "KomposeLayout::add() - Added widget to layout");
  list.append( w );
  widgetsChanged = true;
}

void KomposeLayout::remove( KomposeWidget *w )
{
  list.remove( w );
  widgetsChanged = true;
}


void KomposeLayout::arrangeLayout()
{
  //kdDebug() << "KomposeLayout::arrangeLayout()");
  rearrangeContents();
}

void KomposeLayout::rearrangeContents()
{
  // Check or empty list
  if (list.count() == 0)
  {
    kdDebug() << "KomposeLayout::rearrangeContents() - empty list... skipping!" << endl;
    return;
  }


  // Check for layout Type and do the work
  if (layoutType==TLAYOUT_TASKCONTAINERS)
  {
    filledContainers.clear();
    emptyContainers.clear();

    // Check for empty containers
    TQPtrListIterator<KomposeWidget> it( list );
    KomposeWidget *task;
    while ( (task = it.current()) != 0 )
    {
      ++it;
      KomposeTaskContainerWidget *containerTask = dynamic_cast<KomposeTaskContainerWidget*>(task);

      if ( containerTask->getNumofChilds() > 0 )
      {
        filledContainers.append( containerTask );
      }
      else
      {
        emptyContainers.append( containerTask );
      }
    }

    // Arrange filled containers
    TQRect filledRect( 0,
                      0,
                      parentWidget->width(),
                      parentWidget->height() - ( 40 + 2*spacing ) );
    // arrange the filled desktops taking 90% of the screen
    rearrangeContents( filledRect, filledContainers );

    // Arrange empty containers
    TQRect emptyRect( 0,
                     parentWidget->height() - ( 40 + 2*spacing ),
                     parentWidget->width(),
                     ( 40 + 2*spacing ) );
    // arrange the empty widget in one row
    rearrangeContents( emptyRect, emptyContainers, 1, -1, false );


  }
  else  // default type (generic)
  {
    TQRect availRect( 0,
                     0,
                     parentWidget->width(),
                     parentWidget->height());
    rearrangeContents( availRect, list );
  }


  currentSize = parentWidget->size();
  widgetsChanged = false;
}


/**
 * availRect specifies the size&pos of the contents
 * Specify either rows or cols to set a fixed number of those (setting both won't work correctly)
 */
void KomposeLayout::rearrangeContents( const TQRect& availRect, const TQPtrList<KomposeWidget> widgets, int rows, int columns, bool setMemberRowsCols )
{
  // Check or empty list
  if (widgets.count() == 0)
  {
    kdDebug() << "KomposeLayout::rearrangeContents() - empty list... skipping!" << endl;
    return;
  }

  TQPtrListIterator<KomposeWidget> it( widgets );

  // Calculate grid's rows & cols
  if ( rows != -1 )         // rows have been specified
  {
    columns = (int)ceil(widgets.count() / rows);
  }
  else if ( columns != -1 ) // columns have been specified
  {
    rows = (int)ceil(widgets.count() / columns);
  }
  else                      // neither rows nor cols have been specified
  {
    double parentRatio = (double)availRect.width() / (double)availRect.height();
    // Use more columns than rows when parent's width > parent's height
    if ( parentRatio > 1 )
    {
      columns = (int)ceil( sqrt(widgets.count()) );
      rows = (int)ceil( (double)widgets.count() / (double)columns );
    }
    else
    {
      rows = (int)ceil( sqrt(widgets.count()) );
      columns = (int)ceil( (double)widgets.count() / (double)rows );
    }
  }

  kdDebug() << "KomposeLayout::rearrangeContents() - Relayouting " << widgets.count() << " child widgets with " << rows << " rows & " << columns << " columns" << endl;

  // Calculate width & height
  int w = (availRect.width() - (columns+1) * spacing ) / columns;
  int h = (availRect.height() - (rows+1) * spacing ) / rows;

  TQValueList<TQRect> geometryRects;
  TQValueList<int> maxRowHeights;
  // Process rows
  for ( int i=0; i<rows; ++i )
  {
    int xOffsetFromLastCol = 0;
    int maxHeightInRow = 0;
    // Process columns
    for ( int j=0; j<columns; ++j )
    {
      KomposeWidget *task;

      // Check for end of List
      if ( (task = it.current()) == 0)
        break;

      // Calculate width and height of widget
      double ratio = task->getAspectRatio();

      int widgetw = 100;
      int widgeth = 100;
      int usableW = w;
      int usableH = h;

      // use width of two boxes if there is no right neighbour
      if (it.atLast() && j!=columns-1)
      {
        usableW = 2*w;
      }
      ++it; // We need access to the neighbour in the following
      // expand if right neighbour has ratio < 1
      if (j!=columns-1 && it.current() && it.current()->getAspectRatio() < 1)
      {
        int addW = w - it.current()->getWidthForHeight(h);
        if ( addW > 0 )
        {
          usableW = w + addW;
        }
      }

      if ( ratio == -1 )
      {
        widgetw = w;
        widgeth = h;
      }
      else
      {
        double widthForHeight = task->getWidthForHeight(usableH);
        double heightForWidth = task->getHeightForWidth(usableW);
        if ( (ratio >= 1.0 && heightForWidth <= usableH) ||
             (ratio < 1.0 && widthForHeight > usableW)   )
        {
          widgetw = usableW;
          widgeth = (int)heightForWidth;
        }
        else if ( (ratio < 1.0 && widthForHeight <= usableW) ||
                  (ratio >= 1.0 && heightForWidth > usableH)   )
        {
          widgeth = usableH;
          widgetw = (int)widthForHeight;
        }
      }

      // Set the Widget's size

      int alignmentXoffset = 0;
      int alignmentYoffset = 0;
      if ( i==0 && h > widgeth )
        alignmentYoffset = h - widgeth;
      if ( j==0 && w > widgetw )
        alignmentXoffset = w - widgetw;
      TQRect geom( availRect.x() + j * (w + spacing) + spacing + alignmentXoffset + xOffsetFromLastCol,
                  availRect.y() + i * (h + spacing) + spacing + alignmentYoffset,
                  widgetw, widgeth );
      geometryRects.append(geom);

      // Set the x offset for the next column
      if (alignmentXoffset==0)
        xOffsetFromLastCol += widgetw-w;
      if (maxHeightInRow < widgeth)
        maxHeightInRow = widgeth;
    }
    maxRowHeights.append(maxHeightInRow);
  }

  it.toFirst();
  TQValueList<TQRect>::iterator geomIt = geometryRects.begin();
  TQValueList<int>::iterator maxRowHeightIt = maxRowHeights.begin();
  int topOffset = 0;
  for ( int i=0; i<rows; ++i )
  {
    // Process columns again
    for ( int j=0; j<columns; ++j )
    {
      KomposeWidget *task;
      if ( (task = it.current()) == 0)
        break;

      TQRect geom = *geomIt;
      geom.setY( geom.y() + topOffset );
//       geom.setHeight( geom.height() - topOffset );
      task->setGeometry( geom );
      kdDebug() << "KomposeLayout::rearrangeContents() - Put item " << task->className() << " at x: " <<  geom.x() << " y: " <<  geom.y() << " with size: " <<  geom.width() << "x" << geom.height() << endl;
      ++geomIt;
      ++it;
    }
    if ( *maxRowHeightIt-h > 0 )
      topOffset += *maxRowHeightIt-h;
    ++maxRowHeightIt;
  }

  // Sync cols/rows member vars to current cols/rows
  if ( setMemberRowsCols )
  {
    currentRows = rows;
    currentColumns = columns;
  }
}


/*
 * Search for neighbour (called from outside)
 */
KomposeWidget* KomposeLayout::getNeighbour( const KomposeWidget* widget, int direction, int wrap )
{
  kdDebug() << "KomposeLayout::getNeighbour() - Called with list.count: " << list.count() << endl;

  if (layoutType==TLAYOUT_TASKCONTAINERS)
  {
    KomposeWidget* neighbour = 0;
    if ( filledContainers.containsRef(widget) )
    {
      if ( ( neighbour = getNeighbour( filledContainers, widget, direction, WLAYOUT_HORIZONTAL ) ) == 0 )
        return emptyContainers.first();
    }
    else if ( emptyContainers.containsRef(widget) )
    {
      if ( ( neighbour = getNeighbour( emptyContainers, widget, direction, WLAYOUT_HORIZONTAL ) ) == 0 )
        if ( direction == DLAYOUT_TOP )
          return filledContainers.last();
        else
          return filledContainers.first();
    }
    return neighbour;
  }
  else if (layoutType==TLAYOUT_GENERIC)
    return getNeighbour( list, widget, direction, wrap );

  kdDebug() << "KomposeLayout::getNeighbour() - this should never happen!" << endl;
  return NULL;
}


/*
 * Search for neighbour in the given list (called from inside)
 */
KomposeWidget* KomposeLayout::getNeighbour(
  TQPtrList<KomposeWidget> listToSearch,
  const KomposeWidget* widget,
  int direction,
  int wrap )
{
  TQPtrListIterator<KomposeWidget> it( listToSearch );

  KomposeWidget *task;
  KomposeWidget *neighbour;
  int index = 0;

  if (widget == 0)
  {
    kdDebug() << "KomposeLayout::getNeighbour() - NULL startWidget given. using first()" << endl;
    return listToSearch.first();
  }

  while ( (task = it.current()) != 0 )
  {
    if ( task == widget )
    {
      switch ( direction )
      {
      case DLAYOUT_RIGHT:
        ++it;
        if ( (index)%currentColumns == currentColumns-1 || ( neighbour = it.current() ) == 0 )
        {
          if (wrap == WLAYOUT_HORIZONTAL || wrap == WLAYOUT_BOTH )
            return listToSearch.at(index+1-currentColumns);
          kdDebug() << "KomposeLayout::getNeighbour() - No valid neighbour available" << endl;
          return NULL;
        }
        return neighbour;
      case DLAYOUT_LEFT:
        --it;
        if ( index%currentColumns == 0 || ( neighbour = it.current() ) == 0 )
        {
          if (wrap == WLAYOUT_HORIZONTAL || wrap == WLAYOUT_BOTH )
            if ( (uint)(index+currentColumns-1) < listToSearch.count() )
              return listToSearch.at(index+currentColumns-1);
            else
              return listToSearch.last();
          kdDebug() << "KomposeLayout::getNeighbour() - No valid neighbour available" << endl;
          return NULL;
        }
        return neighbour;
      case DLAYOUT_TOP:
        if ( index < currentColumns || (neighbour = listToSearch.at( index - currentColumns )) == 0)
        {
          if (wrap == WLAYOUT_VERTICAL || wrap == WLAYOUT_BOTH )
            if ( listToSearch.count()%currentColumns == 0 || listToSearch.count()%currentColumns > (uint)(index) )
              return listToSearch.at( (currentRows-1)*currentColumns + index );
            else
              return listToSearch.at( (currentRows-2)*currentColumns + index );
          kdDebug() << "KomposeLayout::getNeighbour() - No valid neighbour available" << endl;
          return NULL;
        }
        return neighbour;
      case DLAYOUT_BOTTOM:
        if ( listToSearch.count() <= (uint)(index + currentColumns) || (neighbour = listToSearch.at( index + currentColumns )) == 0)
        {
          if (wrap == WLAYOUT_VERTICAL || wrap == WLAYOUT_BOTH )
            return listToSearch.at( index%currentColumns );
          kdDebug() << "KomposeLayout::getNeighbour() - No valid neighbour available" << endl;
          return NULL;
        }
        return neighbour;
      }
    }
    ++index;
    ++it;
  }

  kdDebug() << "KomposeLayout::getNeighbour() - this should never happen!" << endl;
  return NULL;
}


#include "komposelayout.moc"
