//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/Model/Sample/FormFactorItems.h
//! @brief     Defines class FormFactorItemses.
//!
//! @homepage  http://www.bornagainproject.org
//! @license   GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2018
//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
//
//  ************************************************************************************************

#ifndef BORNAGAIN_GUI_MODEL_SAMPLE_FORMFACTORITEMS_H
#define BORNAGAIN_GUI_MODEL_SAMPLE_FORMFACTORITEMS_H

#include "GUI/Model/Descriptor/DoubleProperty.h"
#include <memory>

class IFormFactor;

class FormFactorItem {
public:
    virtual ~FormFactorItem() = default;

public:
    virtual std::unique_ptr<IFormFactor> createFormFactor() const = 0;
    virtual DoubleProperties geometryProperties() = 0;

    virtual void writeTo(QXmlStreamWriter* w) const = 0;
    virtual void readFrom(QXmlStreamReader* r) = 0;
};

class Pyramid2Item : public FormFactorItem {
public:
    Pyramid2Item();
    std::unique_ptr<IFormFactor> createFormFactor() const override;

    void setLength(double v) { m_length.setValue(v); }
    void setWidth(double v) { m_width.setValue(v); }
    void setHeight(double v) { m_height.setValue(v); }
    void setAlpha(double v) { m_alpha.setValue(v); }

    DoubleProperties geometryProperties() override
    {
        return {&m_length, &m_width, &m_height, &m_alpha};
    }
    void writeTo(QXmlStreamWriter* w) const override;
    void readFrom(QXmlStreamReader* r) override;

protected:
    DoubleProperty m_length;
    DoubleProperty m_width;
    DoubleProperty m_height;
    DoubleProperty m_alpha;
};

class BarGaussItem : public FormFactorItem {
public:
    BarGaussItem();
    std::unique_ptr<IFormFactor> createFormFactor() const override;

    void setLength(double v) { m_length.setValue(v); }
    void setWidth(double v) { m_width.setValue(v); }
    void setHeight(double v) { m_height.setValue(v); }

    DoubleProperties geometryProperties() override { return {&m_length, &m_width, &m_height}; }

    void writeTo(QXmlStreamWriter* w) const override;
    void readFrom(QXmlStreamReader* r) override;

protected:
    DoubleProperty m_length;
    DoubleProperty m_width;
    DoubleProperty m_height;
};

class BarLorentzItem : public FormFactorItem {
public:
    BarLorentzItem();
    std::unique_ptr<IFormFactor> createFormFactor() const override;

    void setLength(double v) { m_length.setValue(v); }
    void setWidth(double v) { m_width.setValue(v); }
    void setHeight(double v) { m_height.setValue(v); }

    DoubleProperties geometryProperties() override { return {&m_length, &m_width, &m_height}; }

    void writeTo(QXmlStreamWriter* w) const override;
    void readFrom(QXmlStreamReader* r) override;

protected:
    DoubleProperty m_length;
    DoubleProperty m_width;
    DoubleProperty m_height;
};

class BoxItem : public FormFactorItem {
public:
    BoxItem();
    std::unique_ptr<IFormFactor> createFormFactor() const override;

    void setLength(double v) { m_length.setValue(v); }
    void setWidth(double v) { m_width.setValue(v); }
    void setHeight(double v) { m_height.setValue(v); }

    DoubleProperties geometryProperties() override { return {&m_length, &m_width, &m_height}; }

    void writeTo(QXmlStreamWriter* w) const override;
    void readFrom(QXmlStreamReader* r) override;

protected:
    DoubleProperty m_length;
    DoubleProperty m_width;
    DoubleProperty m_height;
};

class ConeItem : public FormFactorItem {
public:
    ConeItem();
    std::unique_ptr<IFormFactor> createFormFactor() const override;

    void setRadius(double v) { m_radius.setValue(v); }
    void setHeight(double v) { m_height.setValue(v); }
    void setAlpha(double v) { m_alpha.setValue(v); }

    DoubleProperties geometryProperties() override { return {&m_radius, &m_height, &m_alpha}; }

    void writeTo(QXmlStreamWriter* w) const override;
    void readFrom(QXmlStreamReader* r) override;

protected:
    DoubleProperty m_radius;
    DoubleProperty m_height;
    DoubleProperty m_alpha;
};

class Pyramid6Item : public FormFactorItem {
public:
    Pyramid6Item();
    std::unique_ptr<IFormFactor> createFormFactor() const override;

    void setBaseEdge(double v) { m_base_edge.setValue(v); }
    void setHeight(double v) { m_height.setValue(v); }
    void setAlpha(double v) { m_alpha.setValue(v); }

    DoubleProperties geometryProperties() override { return {&m_base_edge, &m_height, &m_alpha}; }

    void writeTo(QXmlStreamWriter* w) const override;
    void readFrom(QXmlStreamReader* r) override;

protected:
    DoubleProperty m_base_edge;
    DoubleProperty m_height;
    DoubleProperty m_alpha;
};

class Bipyramid4Item : public FormFactorItem {
public:
    Bipyramid4Item();
    std::unique_ptr<IFormFactor> createFormFactor() const override;

    void setLength(double v) { m_length.setValue(v); }
    void setBaseHeight(double v) { m_base_height.setValue(v); }
    void setHeightRatio(double v) { m_height_ratio.setValue(v); }
    void setAlpha(double v) { m_alpha.setValue(v); }

    DoubleProperties geometryProperties() override
    {
        return {&m_length, &m_base_height, &m_height_ratio, &m_alpha};
    }

    void writeTo(QXmlStreamWriter* w) const override;
    void readFrom(QXmlStreamReader* r) override;

protected:
    DoubleProperty m_length;
    DoubleProperty m_base_height;
    DoubleProperty m_height_ratio;
    DoubleProperty m_alpha;
};

class CylinderItem : public FormFactorItem {
public:
    CylinderItem();
    std::unique_ptr<IFormFactor> createFormFactor() const override;

    void setRadius(double v) { m_radius.setValue(v); }
    void setHeight(double v) { m_height.setValue(v); }

    DoubleProperties geometryProperties() override { return {&m_radius, &m_height}; }

    void writeTo(QXmlStreamWriter* w) const override;
    void readFrom(QXmlStreamReader* r) override;

protected:
    DoubleProperty m_radius;
    DoubleProperty m_height;
};

class EllipsoidalCylinderItem : public FormFactorItem {
public:
    EllipsoidalCylinderItem();
    std::unique_ptr<IFormFactor> createFormFactor() const override;

    void setRadiusX(double v) { m_radiusX.setValue(v); }
    void setRadiusY(double v) { m_radiusY.setValue(v); }
    void setHeight(double v) { m_height.setValue(v); }

    DoubleProperties geometryProperties() override { return {&m_radiusX, &m_radiusY, &m_height}; }

    void writeTo(QXmlStreamWriter* w) const override;
    void readFrom(QXmlStreamReader* r) override;

protected:
    DoubleProperty m_radiusX;
    DoubleProperty m_radiusY;
    DoubleProperty m_height;
};

class SphereItem : public FormFactorItem {
public:
    SphereItem();
    std::unique_ptr<IFormFactor> createFormFactor() const override;

    void setRadius(double v) { m_radius.setValue(v); }

    DoubleProperties geometryProperties() override { return {&m_radius}; }

    void writeTo(QXmlStreamWriter* w) const override;
    void readFrom(QXmlStreamReader* r) override;

protected:
    DoubleProperty m_radius;
};

class SpheroidItem : public FormFactorItem {
public:
    SpheroidItem();
    std::unique_ptr<IFormFactor> createFormFactor() const override;

    void setRadius(double v) { m_radius.setValue(v); }
    void setHeight(double v) { m_height.setValue(v); }

    DoubleProperties geometryProperties() override { return {&m_radius, &m_height}; }

    void writeTo(QXmlStreamWriter* w) const override;
    void readFrom(QXmlStreamReader* r) override;

protected:
    DoubleProperty m_radius;
    DoubleProperty m_height;
};

class HemiEllipsoidItem : public FormFactorItem {
public:
    HemiEllipsoidItem();
    std::unique_ptr<IFormFactor> createFormFactor() const override;

    void setRadiusX(double v) { m_radiusX.setValue(v); }
    void setRadiusY(double v) { m_radiusY.setValue(v); }
    void setHeight(double v) { m_height.setValue(v); }

    DoubleProperties geometryProperties() override { return {&m_radiusX, &m_radiusY, &m_height}; }

    void writeTo(QXmlStreamWriter* w) const override;
    void readFrom(QXmlStreamReader* r) override;

protected:
    DoubleProperty m_radiusX;
    DoubleProperty m_radiusY;
    DoubleProperty m_height;
};

class Prism3Item : public FormFactorItem {
public:
    Prism3Item();
    std::unique_ptr<IFormFactor> createFormFactor() const override;

    void setBaseEdge(double v) { m_base_edge.setValue(v); }
    void setHeight(double v) { m_height.setValue(v); }

    DoubleProperties geometryProperties() override { return {&m_base_edge, &m_height}; }

    void writeTo(QXmlStreamWriter* w) const override;
    void readFrom(QXmlStreamReader* r) override;

protected:
    DoubleProperty m_base_edge;
    DoubleProperty m_height;
};

class Prism6Item : public FormFactorItem {
public:
    Prism6Item();
    std::unique_ptr<IFormFactor> createFormFactor() const override;

    void setBaseEdge(double v) { m_base_edge.setValue(v); }
    void setHeight(double v) { m_height.setValue(v); }

    DoubleProperties geometryProperties() override { return {&m_base_edge, &m_height}; }

    void writeTo(QXmlStreamWriter* w) const override;
    void readFrom(QXmlStreamReader* r) override;

protected:
    DoubleProperty m_base_edge;
    DoubleProperty m_height;
};

class Pyramid4Item : public FormFactorItem {
public:
    Pyramid4Item();
    std::unique_ptr<IFormFactor> createFormFactor() const override;

    void setBaseEdge(double v) { m_base_edge.setValue(v); }
    void setHeight(double v) { m_height.setValue(v); }
    void setAlpha(double v) { m_alpha.setValue(v); }

    DoubleProperties geometryProperties() override { return {&m_base_edge, &m_height, &m_alpha}; }

    void writeTo(QXmlStreamWriter* w) const override;
    void readFrom(QXmlStreamReader* r) override;

protected:
    DoubleProperty m_base_edge;
    DoubleProperty m_height;
    DoubleProperty m_alpha;
};

class CosineRippleBoxItem : public FormFactorItem {
public:
    CosineRippleBoxItem();
    std::unique_ptr<IFormFactor> createFormFactor() const override;

    void setLength(double v) { m_length.setValue(v); }
    void setWidth(double v) { m_width.setValue(v); }
    void setHeight(double v) { m_height.setValue(v); }

    DoubleProperties geometryProperties() override { return {&m_length, &m_width, &m_height}; }

    void writeTo(QXmlStreamWriter* w) const override;
    void readFrom(QXmlStreamReader* r) override;

protected:
    DoubleProperty m_length;
    DoubleProperty m_width;
    DoubleProperty m_height;
};

class CosineRippleGaussItem : public FormFactorItem {
public:
    CosineRippleGaussItem();
    std::unique_ptr<IFormFactor> createFormFactor() const override;

    void setLength(double v) { m_length.setValue(v); }
    void setWidth(double v) { m_width.setValue(v); }
    void setHeight(double v) { m_height.setValue(v); }

    DoubleProperties geometryProperties() override { return {&m_length, &m_width, &m_height}; }

    void writeTo(QXmlStreamWriter* w) const override;
    void readFrom(QXmlStreamReader* r) override;

protected:
    DoubleProperty m_length;
    DoubleProperty m_width;
    DoubleProperty m_height;
};

class CosineRippleLorentzItem : public FormFactorItem {
public:
    CosineRippleLorentzItem();
    std::unique_ptr<IFormFactor> createFormFactor() const override;

    void setLength(double v) { m_length.setValue(v); }
    void setWidth(double v) { m_width.setValue(v); }
    void setHeight(double v) { m_height.setValue(v); }

    DoubleProperties geometryProperties() override { return {&m_length, &m_width, &m_height}; }

    void writeTo(QXmlStreamWriter* w) const override;
    void readFrom(QXmlStreamReader* r) override;

protected:
    DoubleProperty m_length;
    DoubleProperty m_width;
    DoubleProperty m_height;
};

class SawtoothRippleBoxItem : public FormFactorItem {
public:
    SawtoothRippleBoxItem();
    std::unique_ptr<IFormFactor> createFormFactor() const override;

    void setLength(double v) { m_length.setValue(v); }
    void setWidth(double v) { m_width.setValue(v); }
    void setHeight(double v) { m_height.setValue(v); }
    void setAsymmetry(double v) { m_asymmetry.setValue(v); }

    DoubleProperties geometryProperties() override
    {
        return {&m_length, &m_width, &m_height, &m_asymmetry};
    }
    void writeTo(QXmlStreamWriter* w) const override;
    void readFrom(QXmlStreamReader* r) override;

protected:
    DoubleProperty m_length;
    DoubleProperty m_width;
    DoubleProperty m_height;
    DoubleProperty m_asymmetry;
};

class SawtoothRippleGaussItem : public FormFactorItem {
public:
    SawtoothRippleGaussItem();
    std::unique_ptr<IFormFactor> createFormFactor() const override;

    void setLength(double v) { m_length.setValue(v); }
    void setWidth(double v) { m_width.setValue(v); }
    void setHeight(double v) { m_height.setValue(v); }
    void setAsymmetry(double v) { m_asymmetry.setValue(v); }

    DoubleProperties geometryProperties() override
    {
        return {&m_length, &m_width, &m_height, &m_asymmetry};
    }
    void writeTo(QXmlStreamWriter* w) const override;
    void readFrom(QXmlStreamReader* r) override;

protected:
    DoubleProperty m_length;
    DoubleProperty m_width;
    DoubleProperty m_height;
    DoubleProperty m_asymmetry;
};

class SawtoothRippleLorentzItem : public FormFactorItem {
public:
    SawtoothRippleLorentzItem();
    std::unique_ptr<IFormFactor> createFormFactor() const override;

    void setLength(double v) { m_length.setValue(v); }
    void setWidth(double v) { m_width.setValue(v); }
    void setHeight(double v) { m_height.setValue(v); }
    void setAsymmetry(double v) { m_asymmetry.setValue(v); }

    DoubleProperties geometryProperties() override
    {
        return {&m_length, &m_width, &m_height, &m_asymmetry};
    }
    void writeTo(QXmlStreamWriter* w) const override;
    void readFrom(QXmlStreamReader* r) override;

protected:
    DoubleProperty m_length;
    DoubleProperty m_width;
    DoubleProperty m_height;
    DoubleProperty m_asymmetry;
};

class Pyramid3Item : public FormFactorItem {
public:
    Pyramid3Item();
    std::unique_ptr<IFormFactor> createFormFactor() const override;

    void setBaseEdge(double v) { m_base_edge.setValue(v); }
    void setHeight(double v) { m_height.setValue(v); }
    void setAlpha(double v) { m_alpha.setValue(v); }

    DoubleProperties geometryProperties() override { return {&m_base_edge, &m_height, &m_alpha}; }

    void writeTo(QXmlStreamWriter* w) const override;
    void readFrom(QXmlStreamReader* r) override;

protected:
    DoubleProperty m_base_edge;
    DoubleProperty m_height;
    DoubleProperty m_alpha;
};

class TruncatedCubeItem : public FormFactorItem {
public:
    TruncatedCubeItem();
    std::unique_ptr<IFormFactor> createFormFactor() const override;

    void setLength(double v) { m_length.setValue(v); }
    void setRemovedLength(double v) { m_removed_length.setValue(v); }

    DoubleProperties geometryProperties() override { return {&m_length, &m_removed_length}; }

    void writeTo(QXmlStreamWriter* w) const override;
    void readFrom(QXmlStreamReader* r) override;

protected:
    DoubleProperty m_length;
    DoubleProperty m_removed_length;
};

class TruncatedSphereItem : public FormFactorItem {
public:
    TruncatedSphereItem();
    std::unique_ptr<IFormFactor> createFormFactor() const override;

    void setRadius(double v) { m_radius.setValue(v); }
    void setUntruncatedHeight(double v) { m_untruncated_height.setValue(v); }
    void setRemovedTop(double v) { m_removed_top.setValue(v); }

    DoubleProperties geometryProperties() override
    {
        return {&m_radius, &m_untruncated_height, &m_removed_top};
    }
    void writeTo(QXmlStreamWriter* w) const override;
    void readFrom(QXmlStreamReader* r) override;

protected:
    DoubleProperty m_radius;
    DoubleProperty m_untruncated_height;
    DoubleProperty m_removed_top;
};

class TruncatedSpheroidItem : public FormFactorItem {
public:
    TruncatedSpheroidItem();
    std::unique_ptr<IFormFactor> createFormFactor() const override;

    void setRadius(double v) { m_radius.setValue(v); }
    void setUntruncatedHeight(double v) { m_untruncated_height.setValue(v); }
    void setHeightFlattening(double v) { m_height_flattening.setValue(v); }
    void setRemovedTop(double v) { m_removed_top.setValue(v); }

    DoubleProperties geometryProperties() override
    {
        return {&m_radius, &m_untruncated_height, &m_height_flattening, &m_removed_top};
    }
    void writeTo(QXmlStreamWriter* w) const override;
    void readFrom(QXmlStreamReader* r) override;

protected:
    DoubleProperty m_radius;
    DoubleProperty m_untruncated_height;
    DoubleProperty m_height_flattening;
    DoubleProperty m_removed_top;
};

class CantellatedCubeItem : public FormFactorItem {
public:
    CantellatedCubeItem();
    std::unique_ptr<IFormFactor> createFormFactor() const override;

    void setLength(double v) { m_length.setValue(v); }
    void setRemovedLength(double v) { m_removed_length.setValue(v); }

    DoubleProperties geometryProperties() override { return {&m_length, &m_removed_length}; }

    void writeTo(QXmlStreamWriter* w) const override;
    void readFrom(QXmlStreamReader* r) override;

protected:
    DoubleProperty m_length;
    DoubleProperty m_removed_length;
};

class HorizontalCylinderItem : public FormFactorItem {
public:
    HorizontalCylinderItem();
    std::unique_ptr<IFormFactor> createFormFactor() const override;

    void setRadius(double v) { m_radius.setValue(v); }
    void setLength(double v) { m_length.setValue(v); }
    void setSliceBottom(double v) { m_slice_bottom.setValue(v); }
    void setSliceTop(double v) { m_slice_top.setValue(v); }

    DoubleProperties geometryProperties() override
    {
        return {&m_radius, &m_length, &m_slice_bottom, &m_slice_top};
    }
    void writeTo(QXmlStreamWriter* w) const override;
    void readFrom(QXmlStreamReader* r) override;

protected:
    DoubleProperty m_radius;
    DoubleProperty m_length;
    DoubleProperty m_slice_bottom;
    DoubleProperty m_slice_top;
};

// Platonic solids

class PlatonicItem : public FormFactorItem {
public:
    void setEdge(double v) { m_edge.setValue(v); }

    DoubleProperties geometryProperties() override { return {&m_edge}; }

    void writeTo(QXmlStreamWriter* w) const override;
    void readFrom(QXmlStreamReader* r) override;

protected:
    DoubleProperty m_edge;
};

class PlatonicOctahedronItem : public PlatonicItem {
public:
    PlatonicOctahedronItem();
    std::unique_ptr<IFormFactor> createFormFactor() const override;
};

class PlatonicTetrahedronItem : public PlatonicItem {
public:
    PlatonicTetrahedronItem();
    std::unique_ptr<IFormFactor> createFormFactor() const override;
};

class DodecahedronItem : public PlatonicItem {
public:
    DodecahedronItem();
    std::unique_ptr<IFormFactor> createFormFactor() const override;
};

class IcosahedronItem : public PlatonicItem {
public:
    IcosahedronItem();
    std::unique_ptr<IFormFactor> createFormFactor() const override;
};

#endif // BORNAGAIN_GUI_MODEL_SAMPLE_FORMFACTORITEMS_H
