/* This file is part of the KDE libraries
    Copyright (c) 1998 Emmeran Seehuber (the_emmy@hotmail.com)
 
    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library 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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.
*/

#include "kldesigner.h"
#include "kldesignerpriv.h"
#include "kllabel.h"
#include "qpainter.h"
#include "qregexp.h"

KLDesigner::KLDesigner()
{
  data = new KLDesignerPrivate;
}

KLDesigner::~KLDesigner()
{
  delete data;
}

void KLDesigner::showDesigner(ulong showFlags)
{
  data->showDesigner(showFlags);
}


void KLDesigner::hideDesigner()
{
  data->hideDesigner();
}

void KLDesigner::attachDevice(KLDevice *dev)
{
  data->attachDevice(dev);
}

void KLDesigner::detachDevice(KLDevice *dev)
{
  data->detachDevice(dev);
}  

///////////////////////////////////////////////////////////////////////////////
// KLDesLine
///////////////////////////////////////////////////////////////////////////////

void KLDesLine::setupTimer()
{
  connect(&timer,SIGNAL(timeout()),this,SLOT(drawUpdate()));
  timer.start(30);
}

void KLDesLine::cleanupTimer()
{
  timer.stop();
  timer.disconnect(this);
}

void KLDesLine::paintEvent ( QPaintEvent *)
{
  QPainter p;
  p.begin(this);
  //if( !asMark ) {
    // p.setPen(red);
    // p.moveTo(0,0);
    if(asMark)
      p.fillRect (0, 0, width() * 2, height() * 2, blue );
    else
      p.fillRect (0, 0, width() * 2, height() * 2, red );
    // ulong xSize = width() * 2;
    // ulong ySize = height() * 2;
    // p.lineTo(xSize,ySize);
  //}
  p.end();
}

void KLDesLine::drawUpdate()
{
  shiftval++;
  shiftval %= PARTSIZE;
  repaint();
}


///////////////////////////////////////////////////////////////////////////////
// DevInfo Node
///////////////////////////////////////////////////////////////////////////////

KLDesignerDevInfo::KLDesignerDevInfo(KLDevice *dev,KLDesignerPrivate *_despriv) 
{
  this->dev =  dev;
  t = new KLDesLine(dev->getDeviceHandle());
  b = new KLDesLine(dev->getDeviceHandle());
  l = new KLDesLine(dev->getDeviceHandle());
  r = new KLDesLine(dev->getDeviceHandle());
  mark = new KLDesLine(dev->getDeviceHandle());
  mark->asMark = true;
  doUpdateDropmarks = true;
  despriv = _despriv;
}


KLDesignerDevInfo::~KLDesignerDevInfo()
{
  delete t;
  delete b;
  delete l;
  delete r;
  delete mark;
}



///////////////////////////////////////////////////////////////////////////////
// KLDesignerPrivate -- The designer itself
///////////////////////////////////////////////////////////////////////////////


KLDesignerPrivate::KLDesignerPrivate()
{
  propwin = 0;
  klRegisterCommonClasses(&regList);
}

KLDesignerPrivate::~KLDesignerPrivate()
{
  hideDesigner();
}

void KLDesignerPrivate::showDesigner(ulong )
{
  propwin = new KLDialog();
  propwin->setCaption("Properties");
  propGroup = &klGroupBox("Attributes of selected child",false);
  propwin->setRootChild(propGroup);
  regClassTree = new KLRegClassTree();
  treewin = new KLDialog();
  treewin->setCaption("Avaible Widgets");
  treewin->setRootChild(regClassTree);
  actPropEdit = 0;
  actPropChild = 0;
  actSelectedChild = 0;
  actSelChildDev = 0;
  actMarkDev = 0;
  actMark = 0;
  updateStates();
  regClassTree->fill(&regList);
  propwin->show();
  treewin->show();
}


void KLDesignerPrivate::hideDesigner()
{
  if( propwin )
    delete propwin;
  propwin = 0;
  if( treewin ) 
    delete treewin;
  treewin = 0;
}

void KLDesignerPrivate::attachDevice(KLDevice *dev)
{
  HASSERT( dev );
  KLDesignerDevInfo *di = new KLDesignerDevInfo(dev,this);
  deviceList.append(di);
  dev->grapDeviceInput(this);
}


void KLDesignerPrivate::detachDevice(KLDevice *dev)
{
  HASSERT(dev);
  actSelectedChild = 0;
  updateStates();
  KLDesignerDevInfo *di = findDevInfo(dev);
  HASSERT(di);
  dev->releaseDeviceInput();
  deviceList.remove(di);
  delete di;
}


void KLDesignerPrivate::updateStates()
{
  propGroup->stopRelayout();
  if( actSelectedChild != actPropChild ) {
    clearProp();
    actPropChild = actSelectedChild;
    if( actPropChild ) {
      actPropEdit = actPropChild->getRealMetaClass()->createObjectEdit(actPropChild,true);
      if( actPropEdit )
        propGroup->group().addChild( actPropEdit );
    }
  }
  if( !actPropEdit ) {
    actPropEdit = klLabel("<< Nothing selected >>");
    propGroup->group().addChild( actPropEdit );
    actPropChild = 0;
  }
  propGroup->startRelayout();
}

void KLDesignerPrivate::clearProp()
{
  if( actPropEdit ) {
    propGroup->group().remChild( actPropEdit );
    delete actPropEdit;
    actPropEdit = 0;
  }
}

KLDesignerDevInfo *KLDesignerPrivate::findDevInfo( KLDevice *dev ) 
{
  QListIterator<KLDesignerDevInfo> it(deviceList);
  for( ;it.current();++it ) {
    if( it.current()->dev == dev ) 
      return it.current();
  }
  HASSERT( false ); // Theres something wrong !!
  return 0;
}

KLDesignerDevInfo *KLDesignerPrivate::findDevInfo( QObject *widget ) 
{
  for( ; widget; widget = (QWidget *)widget->parent() ) {
    if( !widget->inherits("QWidget") )
      break;
    QListIterator<KLDesignerDevInfo> it(deviceList);
    for( ;it.current();++it ) {
      if( it.current()->dev->getDeviceHandle() == widget ) 
        return it.current();
    }
  }
  HASSERT( false );
  return 0;
}



void KLDesignerPrivate::updateSelDisplay(KLDesignerDevInfo *di)
{
  HASSERT( di );
  KLDevice *dev = di->dev;

  
  // Check: Is there a actSelectedChild ?
  if( actSelectedChild  && (actSelChildDev == di->dev) ) {

    // Calculate coords and for the four "rectangle" marks
    long addX = 1;
    long addY = 1;
    long x = actSelectedChild->showInfo().y + dev->getLeftBorder();
    long y = actSelectedChild->showInfo().x + dev->getTopBorder();
    if( !x )
      addX = 0;
    if( !y ) 
      addY = 0;
    x -= addX;
    y -= addY;
    long xSize = actSelectedChild->showInfo().ySize + addX;
    long ySize = actSelectedChild->showInfo().xSize + addY;

    // Set geometry, show and raise the marks
    di->l->setGeometry(x,y,1,ySize);
    di->b->setGeometry(x,y,xSize,1);
    di->r->setGeometry(x,y+ySize,xSize,1);
    di->t->setGeometry(x+xSize,y,1,ySize);
    di->l->show();
    di->r->show();
    di->b->show();
    di->t->show();
    di->l->raise();
    di->r->raise();
    di->b->raise();
    di->t->raise();
  }
  else {
    // Else: Hide marks
    di->l->hide();
    di->r->hide();
    di->b->hide();
    di->t->hide();
  }

  // Is there an actual dropmark ?
  if( actMark && (actMarkDev == di->dev) ) {
    long x = actMark->y + dev->getLeftBorder();
    long y = actMark->x + dev->getTopBorder();
    di->mark->setGeometry(x,y,actMark->ySize,actMark->xSize);
    di->mark->show();
    di->mark->raise();
  }
  else
    di->mark->hide();

}

void KLDesignerPrivate::updateDropMarks(KLDesignerDevInfo *di)
{
  if( di->doUpdateDropmarks ) {
    HASSERT( di );
    di->dml.clear();
    di->dev->rootChild()->addDropMarks(&di->dml);
    di->doUpdateDropmarks = false;
  }
}


bool KLDesignerPrivate::eventFilter ( QObject *widget, QEvent *e )
{
   QMouseEvent *event = 0;
   switch(e->type()) 
   {
     case Event_MouseButtonPress       :
     case Event_MouseButtonRelease     :
     case Event_MouseButtonDblClick    :
     case Event_MouseMove              : {
       if( widget->inherits("QWidget") ) {
         QPoint point = ((QWidget *)widget)->mapToGlobal(Q_MOUSE_EVENT(e)->pos());
         event = new QMouseEvent(e->type(),point,Q_MOUSE_EVENT(e)->button(), Q_MOUSE_EVENT(e)->state());
         e = event;
       }
     }; break;
     default: break;
   }
   
   bool cont = grapEvent(e,findDevInfo(widget));
   if( event )
     delete event;
   return cont;
}

bool KLDesignerPrivate::grapEvent(QEvent *e, KLDesignerDevInfo *di )
{
  KLDevice *dev = di->dev;
  switch(e->type()) 
  {
    case Event_MouseButtonPress : {
      QMouseEvent *me = Q_MOUSE_EVENT(e);
      QPoint pos = dev->getDeviceHandle()->mapFromGlobal(me->pos());
      pos.rx() -= dev->getTopBorder();
      pos.ry() -= dev->getLeftBorder();
      actSelChildDev = dev;
      actSelectedChild = dev->rootChild()->findChild(pos.y(),pos.x());
      updateSelDisplay(di);
      updateStates();
    }; break;
    case Event_Resize : {
      updateSelDisplay(di);
      di->doUpdateDropmarks = true;
      return false;
    }; break;
    case Event_DragEnter : {
      QDragEnterEvent *de = (QDragEnterEvent *)e;
      if( de->provides(MIME_TYPE_METACLASS) ) {
        de->accept();
      }
      return true;
    }; break;
    case Event_DragMove : {
      checkDragMove((QDragMoveEvent *e),di);
      return true;
    }; break;
    case Event_Paint : {
      return false;
    };
    default: break;
  }
  return true;
}


bool KLDesignerPrivate::checkDragMove(QDragMoveEvent *e, KLDesignerDevInfo *di )
{
}

///////////////////////////////////////////////////////////////////////////////
// Drop Mark List
///////////////////////////////////////////////////////////////////////////////

void KLDropMarkList::addDropMark(ulong x, ulong y, ulong xSize, ulong ySize, KLDropMark::DropAction dropAction, 
                   KLGroup *groupToInsert, KLChild *toAddBefore, KLChild *dontAccept1, KLChild *dontAccept2)
{
  KLDropMark *dm = new KLDropMark;
  append(dm);
  dm->x = x;
  dm->y = y;
  dm->xSize = xSize;
  dm->ySize = ySize;
  
  dm->dropAction = dropAction;

  dm->groupToInsert = groupToInsert;
  dm->toAddBefore = toAddBefore;
  dm->dontAccept1 = dontAccept1;
  dm->dontAccept2 = dontAccept2;
}

///////////////////////////////////////////////////////////////////////////////
// KLRegClassTree
///////////////////////////////////////////////////////////////////////////////
void KLRegClassTree::fill(KLMetaRegList *reglist)
{
  treelist.clear();
  clear();
  QStrList pathList;
  KLMetaRegListIt it(*reglist);
  it.toFirst();
  for( ; it.current(); ++it) {
    if( it.current()->displayPath.isEmpty() )
      continue;
    if( pathList.contains(it.current()->displayPath) == 0 ) {
      pathList.inSort(it.current()->displayPath);
    }
  }
  
  KPath path;
  pathList.first();
  createPath(pathList,"",path);

  it.toFirst();
  for( ; it.current(); ++it) {
    QString pathName = it.current()->displayPath;
    KPath path;
    KLDesTreeListItem *item = new KLDesTreeListItem();
    item->setText(it.current()->userClassName);
    item->metaClass = it.current();
    treelist.append(item);
    // insertItem(item,&path);
    insertItem(item);
  }
}

void KLRegClassTree::createPath(QStrList &paths, QString currPath, KPath path)  
{

  QString myPath = paths.current();

  if( !strncmp(currPath,myPath,strlen(currPath)) ) {
    myPath = myPath.mid(currPath.length(),myPath.length());
  }
  else 
    path.clear();

  QString pathPart = myPath.left(myPath.find("/"));
  if( pathPart.isEmpty() )
    pathPart = myPath;

  myPath = myPath.mid(pathPart.length(),myPath.length());
  pathPart.replace(QRegExp("/"),""); 
  insertItem(pathPart,(QPixmap *)0,&path);

  if( !myPath.isEmpty() ) {
    paths.next();
    path.push(&pathPart);
    createPath(paths,currPath + pathPart + "/", path );
    path.pop();
  }
}

KLRegClassTree::KLRegClassTree() 
{
  connect(this,SIGNAL(singleSelected(int)),this,SLOT(startDrag(int)));
  // iconPix.load("/home/emmy/kle/klelib/pics/dnd.xpm"); // HARDCODED WHILE TESTING !!!!!
  drag = new QStoredDrag(MIME_TYPE_METACLASS,this);

}

KLRegClassTree::~KLRegClassTree()
{
  delete drag;
}

void KLRegClassTree::mousePressEvent (QMouseEvent *e) 
{
  _mouse = e;
  KLTreeList::mousePressEvent(e);
}


void KLRegClassTree::startDrag(int index)
{
  KLDesTreeListItem *item = (KLDesTreeListItem *)itemAt(index);
  if( treelist.containsRef(item) ) {
    QByteArray ba;
    ba.assign(item->metaClass->className.data(),item->metaClass->className.length());
    drag->setEncodedData(ba);
    drag->dragCopy();
    //dndWidget.startDrag(new KDNDIcon(iconPix,p.x() + dx, p.y() + dy),item->metaClass->className.data(),item->metaClass->className.length(),DndKLEChildMeta,dx,dy);
  }
}

///////////////////////////////////////////////////////////////////////////////
// 
///////////////////////////////////////////////////////////////////////////////

#include "kldesigner.moc"
