/*
 * (C) Copyright 1996-2016 ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation nor
 * does it submit to any jurisdiction.
 */

/*! \file BasicGraphicsObject.h
    \brief Implementation of BasicGraphicsObject class.
    \author Meteorological Visualisation Section, ECMWF

    Started: March 2004

*/
#ifndef BasicGraphicsObject_H
#define BasicGraphicsObject_H

#include "magics.h"

#include <algorithm>

#include "AutoVector.h"
#include "MagException.h"
#include "MagLog.h"

namespace magics {

class BaseDriver;

class Layout;
class Transformation;

class BasicGraphicsObjectContainer;

class BasicGraphicsObject {
public:
    BasicGraphicsObject();
    virtual ~BasicGraphicsObject();

    virtual bool reproject(BasicGraphicsObjectContainer&) const {
        MagLog::error() << "BasicGraphicsObject::reproject(...)--->Need to be implemented!\n";
        ASSERT(0);
        return false;
    }

    virtual void redisplay(const BaseDriver&) const {
        MagLog::dev() << "BasicGraphicsObject::redisplay(...)--->Not yet implemented\n";
    }

    void parent(BasicGraphicsObjectContainer* parent) {
        // ASSERT(parent_ == 0);
        parent_ = parent;
    }
    void check();
    BasicGraphicsObjectContainer& parent() {
        ASSERT(parent_);
        return *parent_;
    }

    void makeBrother(const BasicGraphicsObject& brother) { parent_ = brother.parent_; }

    virtual void release() {}

    bool isOrphan() { return parent_ == 0; }
    void orphan() { parent_ = 0; }

    void name(const string& name) { name_ = name; }
    const string& name() const { return name_; }
    bool root() { return parent_ == 0; }

    void widthResolution(int width) { widthResolution_ = width; }
    void heightResolution(int height) { heightResolution_ = height; }
    int widthResolution() const { return widthResolution_; }
    int heightResolution() const { return heightResolution_; }

    virtual bool buildTree(const Layout&, unsigned int, const BaseDriver&) const { return false; }

protected:
    virtual void print(ostream&) const;
    int widthResolution_;
    int heightResolution_;

    BasicGraphicsObjectContainer* parent_;  // Just for reference : do not delete!
    string name_;

private:
    // No copy allowed
    // BasicGraphicsObject& operator=(const BasicGraphicsObject&);

    // -- Friends
    friend ostream& operator<<(ostream& s, const BasicGraphicsObject& p) {
        p.print(s);
        return s;
    }
};

/*!

 Inherited by SceneLayer

 \sa SceneLayer
*/
class BasicGraphicsObjectContainer : public BasicGraphicsObject {
public:
    BasicGraphicsObjectContainer() {}
    virtual ~BasicGraphicsObjectContainer() override;

    void push_back(BasicGraphicsObject* object) {
        object->check();  // here we make sure that the object is not in 2 containres!
        objects_.push_back(object);
        object->parent(this);
    }
    void push_last(BasicGraphicsObject* object) {
        object->check();  // here we make sure that the object is not in 2 containres!
        last_.push_back(object);
        object->parent(this);
    }

    void clear();
    bool buildTree(const Layout&, unsigned int, const BaseDriver&) const override;
    void release() override;
    void remove(BasicGraphicsObject* object) {
        objects_.erase(std::remove(objects_.begin(), objects_.end(), object), objects_.end());
    }

    void visit(const BaseDriver&) const;

    virtual void getDriverInfo(double& x, double& y, double& width, double& height) {
        if (parent_)
            parent_->getDriverInfo(x, y, width, height);
    }

    virtual double absoluteY() const  // absolute position from the root
    {
        ASSERT(parent_);
        return parent_->absoluteY();
    }

    virtual double absoluteWidth() const  // absolute position from the root
    {
        ASSERT(parent_);
        return parent_->absoluteWidth();
    }

    virtual double absoluteHeight() const  // absolute position from the root
    {
        ASSERT(parent_);
        return parent_->absoluteHeight();
    }

    virtual double absoluteWidth(double width)  // absolute position from the root
    {
        ASSERT(parent_);
        return parent_->absoluteWidth(width);
    }

    virtual double absoluteHeight(double height)  // absolute position from the root
    {
        ASSERT(parent_);
        return parent_->absoluteHeight(height);
    }

    virtual const Transformation& transformation() const  // returns the Transformation
    {
        ASSERT(parent_);
        return parent_->transformation();
    }
    const vector<BasicGraphicsObject*>& objects() {  //
        // first we add
        for (vector<BasicGraphicsObject*>::iterator l = last_.begin(); l != last_.end(); ++l)
            objects_.push_back(*l);
        last_.clear();
        return objects_;
    }

protected:
    virtual void print(ostream&) const override;
    vector<BasicGraphicsObject*> objects_;
    vector<BasicGraphicsObject*> last_;
};

}  // namespace magics
#endif
