/***************************************************************************
 *   Copyright (c) 2015 Stefan Tröger <stefantroeger@gmx.net>              *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   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                                *
 *                                                                         *
 ***************************************************************************/


#ifndef PARTDESIGN_DATUMSHAPE_H
#define PARTDESIGN_DATUMSHAPE_H

#include <App/PropertyLinks.h>
#include <App/DocumentObserver.h>
#include <App/FeaturePython.h>
#include <Mod/Part/App/DatumFeature.h>
#include <Mod/PartDesign/PartDesignGlobal.h>

namespace PartDesign
{

/* This feature is not really a classical datum. It is a fully defined shape and not
 * infinite geometry like a plane and a line. Also it is not calculated by references and hence
 * is not "attached" to anything. Furthermore real shapes must be visualized. This makes it hard
 * to reuse the existing datum infrastructure and a special handling for this type is
 * created.
 */
// TODO Add better documentation (2015-09-11, Fat-Zer)

class PartDesignExport ShapeBinder : public Part::Feature
{
    PROPERTY_HEADER_WITH_OVERRIDE(PartDesign::ShapeBinder);

public:
    ShapeBinder();
    ~ShapeBinder() override;

    App::PropertyLinkSubListGlobal    Support;
    App::PropertyBool TraceSupport;

    static void getFilteredReferences(const App::PropertyLinkSubList* prop, App::GeoFeature*& object, std::vector< std::string >& subobjects);
    static Part::TopoShape buildShapeFromReferences(App::GeoFeature* obj, std::vector< std::string > subs);

    const char* getViewProviderName() const override {
        return "PartDesignGui::ViewProviderShapeBinder";
    }

protected:
    Part::TopoShape updatedShape() const;
    bool hasPlacementChanged() const;
    void handleChangedPropertyType(Base::XMLReader &reader, const char * TypeName, App::Property * prop) override;
    short int mustExecute() const override;
    App::DocumentObjectExecReturn* execute() override;
    void onChanged(const App::Property* prop) override;

private:
    void slotChangedObject(const App::DocumentObject& Obj, const App::Property& Prop);
    void onSettingDocument() override;

    using Connection = boost::signals2::connection;
    Connection connectDocumentChangedObject;
};

class PartDesignExport SubShapeBinder : public Part::Feature {
    PROPERTY_HEADER_WITH_OVERRIDE(PartDesign::SubShapeBinder);
public:
    using inherited = Part::Feature;

    SubShapeBinder();
    ~SubShapeBinder() override;

    const char* getViewProviderName() const override {
        return "PartDesignGui::ViewProviderSubShapeBinder";
    }

    void setLinks(std::map<App::DocumentObject *, std::vector<std::string> > &&values, bool reset=false);

    App::PropertyXLinkSubList Support;
    App::PropertyBool ClaimChildren;
    App::PropertyBool Relative;
    App::PropertyBool Fuse;
    App::PropertyBool MakeFace;
    App::PropertyEnumeration BindMode;
    App::PropertyBool PartialLoad;
    App::PropertyXLink Context;
    App::PropertyInteger _Version;
    App::PropertyEnumeration BindCopyOnChange;
    App::PropertyBool Refine;
    App::PropertyFloat Offset;
    App::PropertyEnumeration OffsetJoinType;
    App::PropertyBool OffsetFill;
    App::PropertyBool OffsetOpenResult;
    App::PropertyBool OffsetIntersection;

    enum UpdateOption {
        UpdateNone = 0,
        UpdateInit = 1,
        UpdateForced = 2,
    };
    void update(UpdateOption options = UpdateNone);

    int canLoadPartial() const override {
        return PartialLoad.getValue()?1:0;
    }

    bool canLinkProperties() const override {return false;}

    App::DocumentObject *getSubObject(const char *subname, PyObject **pyObj=nullptr,
            Base::Matrix4D *mat=nullptr, bool transform=true, int depth=0) const override;

protected:
    App::DocumentObjectExecReturn* execute() override;
    void onChanged(const App::Property *prop) override;

    void handleChangedPropertyType(
            Base::XMLReader &reader, const char * TypeName, App::Property * prop) override;

    void onDocumentRestored() override;
    void setupObject() override;

    void setupCopyOnChange();
    void checkCopyOnChange(const App::Property &prop);
    void clearCopiedObjects();

    void checkPropertyStatus();

    void collapseGeoChildren();

    void slotRecomputedObject(const App::DocumentObject& Obj);

    using Connection = boost::signals2::scoped_connection;
    Connection connRecomputedObj;
    App::Document *contextDoc = nullptr;

    std::vector<Connection> copyOnChangeConns;
    bool hasCopyOnChange = true;

    App::PropertyXLinkSub _CopiedLink;
    std::vector<App::DocumentObjectT> _CopiedObjs;
};

using SubShapeBinderPython = App::FeaturePythonT<SubShapeBinder>;

} //namespace PartDesign


#endif // PARTDESIGN_DATUMSHAPE_H
