/***************************************************************************
**
**  This file is part of SciFigs.
**
**  SciFigs 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 3 of the License, or
**  (at your option) any later version.
**
**  SciFigs 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 Foobar.  If not, see <http://www.gnu.org/licenses/>
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2020-05-04
**  Copyright: 2020
**    Marc Wathelet (ISTerre, Grenoble, France)
**
***************************************************************************/

#include "GraphicObjectGroup.h"
#include "GraphicObjectFactory.h"
#include "SciFigsGlobal.h"
#include "GraphicSheet.h"
#include "XMLSciFigs.h"

namespace SciFigs {

  /*!
    \class GraphicObjectGroup GraphicObjectGroup.h
    \brief Brief description of class still missing

    Full description of class still missing
  */

  const QString GraphicObjectGroup::xmlGroupTag="Group";

  REGISTER_GRAPHICOBJECT(GraphicObjectGroup, GraphicObjectGroup::xmlGroupTag,
                         "", "", "", "");

  /*!
    Description of constructor still missing
  */
  GraphicObjectGroup::GraphicObjectGroup(QWidget * parent)
    : GraphicObject(parent)
  {
    TRACE;
  }

  /*!
    Description of destructor still missing
  */
  GraphicObjectGroup::~GraphicObjectGroup()
  {
    TRACE;
  }

  void GraphicObjectGroup::setSheet(GraphicSheet * s)
  {
    TRACE;
    GraphicObject::setSheet(s);
    GraphicObjectList l=objects();
    for(int i=l.count()-1; i>=0; i--) {
      l.at(i)->setSheet(s);
    }
  }

  void GraphicObjectGroup::addActions()
  {
    TRACE;
    GraphicObject::addActions();

    QAction *ag=action("Group");
    if(ag) {
      QAction * a;
      a=new QAction(tr("&Ungroup"), this);
      a->setObjectName("Ungroup");
      connect(a, SIGNAL(triggered()), sheet(), SLOT(ungroupObjects()));
      insertAction(ag, a);
      removeAction(ag);
      insertAction(a, ag);
    }
  }

  void GraphicObjectGroup::addObject(GraphicObject * obj)
  {
    TRACE;
    obj->setParent(this);
    obj->show();
    obj->setSelectable(false); // Before setting postions that can be negative

    // Set relative coordinates to obj
    obj->setPrintLeft(obj->printLeft()-printLeft());
    obj->setPrintTop(obj->printTop()-printTop());

    connect(obj, SIGNAL(sizeChanged()), this, SLOT(resize()));
  }

  void GraphicObjectGroup::resize()
  {
    TRACE;
    GraphicObjectList l=objects();
    Rect r=l.boundingRect();
    double x0=printLeft();
    double y0=printTop();
    bool touched=false;
    if(r.width()!=printWidth()) {
      setPrintWidth(r.width());
      touched=true;
    }
    if(r.height()!=printHeight()) {
      setPrintHeight(r.height());
      touched=true;
    }
    if(r.x1()!=0.0) {
      for(int i=l.count()-1; i>=0; i--) {
        GraphicObject * obj=l.at(i);
        obj->setPrintLeft(obj->printLeft()-r.x1());
      }
      setPrintLeft(x0+r.x1());
      touched=true;
    }
    if(r.y1()!=0.0) {
      for(int i=l.count()-1; i>=0; i--) {
        GraphicObject * obj=l.at(i);
        obj->setPrintTop(obj->printTop()-r.y1());
      }
      setPrintTop(y0+r.y1());
      touched=true;
    }
    l.setPos();
    l.setSize();
    if(touched) {
      updateGeometry();
    }
  }

  void GraphicObjectGroup::addProperties(PropertyProxy * pp)
  {
    TRACE;
    GraphicObjectList l=objects();
    for(int i=l.count()-1; i>=0; i--) {
      GraphicObject * obj=l.at(i);
      obj->addProperties(pp);
      // Remove GraphicObject properties
      obj->GraphicObject::removeProperties(pp);
    }
    // Add only GraphicObject properties of the group
    GraphicObject::addProperties(pp);
  }

  void GraphicObjectGroup::removeProperties(PropertyProxy * pp)
  {
    TRACE;
    GraphicObjectList l=objects();
    for(int i=l.count()-1; i>=0; i--) {
      GraphicObject * obj=l.at(i);
      obj->removeProperties(pp);
    }
    GraphicObject::removeProperties(pp);
  }

  void GraphicObjectGroup::xml_writeChildren(XML_WRITECHILDREN_ARGS) const
  {
    TRACE;
    GraphicObject::xml_writeChildren(s, context);
    GraphicObjectList l=objects();
    static const QString key("objectName");
    XMLSaveAttributes att;
    QString& value=att.add(key);
    int n=l.count();
    for(int i=0; i<n; i++) {
      GraphicObject * obj=l.at(i);
      value=obj->objectName();
      obj->xml_save(s, context, att);
    }
  }

  XMLMember GraphicObjectGroup::xml_member(XML_MEMBER_ARGS)
  {
    TRACE;
    static const QString tmp("objectName");
    XMLSciFigs * scifigsContext=static_cast<XMLSciFigs *>(context);
    XMLRestoreAttributeIterator it=attributes.find(tmp);
    if(it!=attributes.end()) {
      if(scifigsContext->data()) {
        GraphicObject * obj=GraphicObjectFactory::instance()->create(tag.toString());
        if(obj) {
          obj->setRemovable(true);
          obj->setParent(this);
          obj->setSelectable(false);
          sheet()->addObject(obj, false);
          return XMLMember(obj);
        } else {
          App::log(tr("Unknown object type %1\n").arg(tag.toString()) );
          return XMLMember(XMLMember::Unknown);
        }
      } else {
        GraphicObjectList list=objects();
        GraphicObject * obj=list.findObject(tag.toString(), it.value().toString());
        if(obj) {
          return XMLMember(obj);
        } else {
          obj=list.findObject(tag.toString(), nullptr);
          if(obj) {
            if(Message::warning(MSG_ID,"Restore",
                                     tr("Cannot find an object of type %1 and named %2,\n"
                                        "but an object of type %3 and named %4 has been found.\n"
                                        "Do you want to restore properties to this object?").
                                     arg(tag.toString()).arg(it.value().toString()).
                                     arg(tag.toString()).arg(obj->objectName()),
                                     Message::yes(),Message::no(), true)==Message::Answer0) {
              return XMLMember(obj);
            } else return XMLMember(XMLMember::Unknown);
          } else {
            App::log(tr("Cannot find an object of type %1\n").arg(tag.toString()) );
            return XMLMember(XMLMember::Unknown);
          }
        }
      }
    }
    return GraphicObject::xml_member(tag, attributes, context);
  }

  bool GraphicObjectGroup::xml_polish(XML_POLISH_ARGS)
  {
    TRACE;
    Q_UNUSED(context);
    GraphicObjectList list=objects();
    // Only connect here to avoid many resize of the group
    for(int i=list.count()-1; i>=0; i--) {
      connect(list.at(i), SIGNAL(sizeChanged()), this, SLOT(resize()));
    }
    resize();
    list.setPos();
    list.setSize();
    list.show();
    return true;
  }

  /*!
    For some high resolution bitmap image, fonts must scaled

    Re-implentation to scale fonts in sub-widgets
  */
  void GraphicObjectGroup::scaleFonts(FontScales& original, double scale)
  {
    TRACE;
    GraphicObjectList l=objects();
    for(int i=l.count()-1; i>=0; i--) {
      l.at(i)->scaleFonts(original, scale);
    }
  }

  /*!
    Restore a saved scale for fonts
  */
  void GraphicObjectGroup::restoreScaleFonts(const FontScales& original)
  {
    TRACE;
    GraphicObjectList l=objects();
    for(int i=l.count()-1; i>=0; i--) {
      l.at(i)->restoreScaleFonts(original);
    }
  }

  /*!
    Draw the object to any device. This exclude the screen. For screen refresh the children repaint their
    area alone. This function is used only for printing and to export the graph as an image.
  */
  void GraphicObjectGroup::print(QPainter& p, double dotpercm, int x0Sheet, int y0Sheet, bool mask)
  {
    TRACE;
    int x0=x0Sheet-qRound(printLeft()*dotpercm);
    int y0=y0Sheet-qRound(printTop()*dotpercm);
    GraphicObjectList l=objects();
    int n=l.count();
    for(int i=0; i<n; i++) {
      l.at(i)->print(p, dotpercm, x0, y0, mask);
    }
  }

  void GraphicObjectGroup::deepUpdate()
  {
    TRACE;
    GraphicObjectList l=objects();
    for(int i=l.count()-1; i>=0; i--) {
      l.at(i)->deepUpdate();
    }
  }

} // namespace SciFigs

