// This file is part of the AliceVision project.
// Copyright (c) 2024 AliceVision contributors.
// This Source Code Form is subject to the terms of the Mozilla Public License,
// v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

#pragma once

#include <aliceVision/types.hpp>
#include <aliceVision/track/TracksHandler.hpp>
#include <aliceVision/sfmData/SfMData.hpp>
#include <aliceVision/sfm/pipeline/expanding/ExpansionHistory.hpp>
#include <aliceVision/sfm/pipeline/expanding/SfmBundle.hpp>
#include <aliceVision/sfm/pipeline/expanding/PointFetcher.hpp>
#include <aliceVision/sfm/pipeline/expanding/SfmResection.hpp>

namespace aliceVision {
namespace sfm {

class ExpansionChunk
{
public:
    using uptr = std::unique_ptr<ExpansionChunk>;

public:

    /**
     * @brief Compute a chunk of views assuming the sfmData already has an initial set of 
     * reconstructed cameras and 3D points to connect to.
     * @param sfmData the sfmData which describes the current sfm state
     * @param tracksHandler the scene tracks handler
     * @param viewsChunks a list of view ids to process in this chunk
    */
    bool process(sfmData::SfMData & sfmData, 
                const track::TracksHandler & tracksHandler, 
                const std::set<IndexT> & viewsChunk);

    /**
     * @brief setup the bundle handler
     * @param bundleHandler a unique ptr. the Ownership will be taken
    */
    void setBundleHandler(SfmBundle::uptr & bundleHandler)
    {
        _bundleHandler = std::move(bundleHandler);
    }

    /**
     * brief setup the expansion history handler
     * @param expansionHistory a shared ptr
     */
    void setExpansionHistoryHandler(ExpansionHistory::sptr & expansionHistory)
    {
        _historyHandler = expansionHistory;
    }

    /**
     * brief setup the point fetcher handler
     * @param pointFetcher a unique ptr. the Ownership will be taken
    */
    void setPointFetcherHandler(PointFetcher::uptr & pointFetcherHandler)
    {
        _pointFetcherHandler = std::move(pointFetcherHandler);
    }

    /**
     * brief setup the point fetcher handler
     * @param resectionHandler a unique ptr. the Ownership will be taken
    */
    void setResectionHandler(SfmResection::uptr & resectionHandler)
    {
        _resectionHandler = std::move(resectionHandler);
    }

    /**
     * @brief set the minimal number of points to enable triangulation of a track
     * @param count the number of points
    */
    void setTriangulationMinPoints(size_t count)
    {
        _triangulationMinPoints = count;
    }

    /**
     * @brief set the maximal reprojection error in the triangulation process.
     * @param count the number of points
    */
    void setTriangulationMaxError(double error)
    {
        _maxTriangulationError = error;
    }

    /**
     * @brief set the minimal allowed parallax degree for triangulation
     * @param angle the angle in DEGREES
    */
    void setMinAngleTriangulation(double angle)
    {
        _minTriangulationAngleDegrees = angle;
    }

    /**
     * @brief set the minimal number of inliers under which a resection is considered weak
     * @param size the inliers count required
    */
    void setWeakResectionSize(size_t size)
    {
        _weakResectionSize = size;
    }

    const std::set<IndexT> & getIgnoredViews()
    {
        return _ignoredViews;
    }

     /**
     * @brief Are the optional depths priors used ?
     * @param flag boolean to set value to
    */
    void setEnableDepthPrior(bool flag)
    {
        _enableDepthPrior = flag;
    }

    /**
     * @brief Do we prefer prior depth over estimated multiview depth ?
     * @param flag boolean to set value to
    */
    void setIgnoreMultiviewOnPrior(bool flag)
    {
        _ignoreMultiviewOnPrior = flag;
    }

private:

    /**
     * @Brief assign the computed pose to the view
     * @param sfmData the sfmData to update
     * @param viewId the viewId of interest
     * @param pose the homogeneous matrix computed from the resection
    */
    void addPose(sfmData::SfMData & sfmData, IndexT viewId, const Eigen::Matrix4d & pose);

    /**
     * @brief Try to upgrade sfm with new landmarks
     * @param sfmData the object to update
     * @param tracks all tracks of the scene as a map {trackId, track}
     * @param viewIds the set of views to triangulate 
     */
    bool triangulate(sfmData::SfMData & sfmData, const track::TracksHandler & tracksHandler, const std::set<IndexT> & viewIds);

    /**
     * @brief Add constraints on points
     * @param sfmData the object to update
     * @param tracks all tracks of the scene as a map {trackId, track}
     * @param viewIds the set of views to process 
    */
    void setConstraints(sfmData::SfMData & sfmData, const track::TracksHandler & tracksHandler, const std::set<IndexT> & viewIds);

private:
    SfmBundle::uptr _bundleHandler;
    ExpansionHistory::sptr _historyHandler;
    PointFetcher::uptr _pointFetcherHandler;
    std::set<IndexT> _ignoredViews;
    SfmResection::uptr _resectionHandler;

private:    
    size_t _triangulationMinPoints = 2;
    double _minTriangulationAngleDegrees = 3.0;
    double _maxTriangulationError = 8.0;
    size_t _weakResectionSize = 100;
    bool _enableDepthPrior = true;
    bool _ignoreMultiviewOnPrior = false;
};

} // namespace sfm
} // namespace aliceVision

