/****************************************************************************
** Copyright (c) 2016, Fougue SAS <https://www.fougue.pro>
** SPDX-License-Identifier: BSD-2-Clause
****************************************************************************/

#include "io_occ_vrml_writer.h"

#include "../base/application_item.h"
#include "../base/caf_utils.h"
#include "../base/document.h"
#include "../base/io_system.h"
#include "../base/math_utils.h"
#include "../base/property_builtins.h"
#include "../base/property_enumeration.h"
#include "../base/task_progress.h"
#include "../base/tkernel_utils.h"

#include <OSD_OpenFile.hxx>
#include <VrmlData_ShapeConvert.hxx>
#include <fstream>

namespace Mayo::IO {

class OccVrmlWriter::Properties : public PropertyGroup {
    MAYO_DECLARE_TEXT_ID_FUNCTIONS(Mayo::IO::OccVrmlWriter::Properties)
public:
    explicit Properties(PropertyGroup* parentGroup)
        : PropertyGroup(parentGroup)
    {
        this->shapeRepresentation.mutableEnumeration().chopPrefix("VrmlAPI_");
    }

    void restoreDefaults() override {
        const OccVrmlWriter::Parameters params;
        this->shapeRepresentation.setValue(params.shapeRepresentation);
    }

//    PropertyBool m_meshDeflectionFromShapeRelativeSize;
//    PropertyDouble m_meshDeflection;
//    PropertyDouble scale;
    PropertyEnum<VrmlAPI_RepresentationOfShape> shapeRepresentation{ this, textId("shapeRepresentation") };
};

bool OccVrmlWriter::transfer(gsl::span<const ApplicationItem> spanAppItem, TaskProgress* progress)
{
    m_scene.reset(new VrmlData_Scene);
    VrmlData_ShapeConvert converter(*m_scene);

    int count = 0;
    System::visitUniqueItems(spanAppItem, [&](const ApplicationItem&) { ++count; });

    int iCount = 0;
    System::visitUniqueItems(spanAppItem, [&](const ApplicationItem& appItem) {
        if (appItem.isDocument()) {
#if OCC_VERSION_HEX >= OCC_VERSION_CHECK(7, 4, 0)
            converter.ConvertDocument(appItem.document());
#else
    // TODO Call VrmlData_ShapeConvert::AddShape() on each child entity of "shape" type
#endif
        }
        else if (appItem.isDocumentTreeNode()) {
            const TDF_Label label = appItem.documentTreeNode().label();
            if (XCaf::isShape(label))
                converter.AddShape(XCaf::shape(label));
        }

        progress->setValue(MathUtils::toPercent(++iCount, 0, count));
    });

    const auto rep = m_shapeRepresentation;
    converter.Convert(
                rep == VrmlAPI_ShadedRepresentation || rep == VrmlAPI_BothRepresentation,
                rep == VrmlAPI_WireFrameRepresentation || rep == VrmlAPI_BothRepresentation
    );
    return true;
}

bool OccVrmlWriter::writeFile(const FilePath& filepath, TaskProgress*)
{
    if (!m_scene)
        return false;

    std::ofstream outs;
    OSD_OpenStream(outs, filepath.u8string().c_str(), std::ios::out);
    if (outs) {
        outs << *m_scene;
        outs.close();
        return outs.good();
    }

    return false;
}

std::unique_ptr<PropertyGroup> OccVrmlWriter::createProperties(PropertyGroup* parentGroup)
{
    return std::make_unique<Properties>(parentGroup);
}

void OccVrmlWriter::applyProperties(const PropertyGroup* params)
{
    auto ptr = dynamic_cast<const Properties*>(params);
    if (ptr) {
        m_params.shapeRepresentation = static_cast<VrmlAPI_RepresentationOfShape>(ptr->shapeRepresentation);
    }
}

} // namespace Mayo::IO
