// Copyright (C) 2016 EDF
// All Rights Reserved
// This code is published under the GNU Lesser General Public License (GNU LGPL)
#ifndef  GRIDANDREGRESSEDVALUEGENERS_H
#define  GRIDANDREGRESSEDVALUEGENERS_H
#include <Eigen/Dense>
#include "geners/GenericIO.hh"
#include "StOpt/regression/GridAndRegressedValue.h"
#include "StOpt/regression/BaseRegressionGeners.h"
#include "StOpt/regression/LocalLinearRegressionGeners.h"
#include "StOpt/regression/SparseRegressionGeners.h"
#include "StOpt/core/grids/SpaceGridGeners.h"
#include "StOpt/core/grids/RegularSpaceGridGeners.h"
#include "StOpt/core/grids/GeneralSpaceGridGeners.h"
#include "StOpt/core/grids/SparseSpaceGridNoBoundGeners.h"
#include "StOpt/core/grids/SparseSpaceGridBoundGeners.h"
#include "StOpt/core/grids/SparseInterpolatorSpectralGeners.h"
#include "StOpt/core/grids/InterpolatorSpectralGeners.h"
#include "StOpt/core/grids/LinearInterpolatorSpectralGeners.h"
#include "StOpt/core/grids/LegendreInterpolatorSpectralGeners.h"


/** \file GridAndRegressedValueGeners.h
 * \brief Define non intrusive serialization with random access
*  \author Xavier Warin
 */

/// specialize the ClassIdSpecialization template
/// so that a ClassId object can be associated with the class we want to
/// serialize.  The second argument is the version number.
///@{
gs_specialize_class_id(StOpt::GridAndRegressedValue, 1)
/// an external class
gs_declare_type_external(StOpt::GridAndRegressedValue)
///@}

namespace gs
{
//
/// \brief  This is how the specialization of GenericWriter should look like
//
template <class Stream, class State >
struct GenericWriter < Stream, State, StOpt::GridAndRegressedValue,
           Int2Type<IOTraits<int>::ISEXTERNAL> >
{
    inline static bool process(const StOpt::GridAndRegressedValue  &p_state, Stream &p_os,
                               State *, const bool p_processClassId)
    {
        // If necessary, serialize the class id
        static const ClassId current(ClassId::makeId<StOpt::GridAndRegressedValue >());
        const bool status = p_processClassId ? ClassId::makeId<StOpt::GridAndRegressedValue >().write(p_os) : true;
        // Serialize object data if the class id was successfully
        // written out
        if (status)
        {
            write_item(p_os, p_state.getRegressor());
            std::shared_ptr< StOpt::SpaceGrid > ptrGrid = p_state.getGrid();
            bool bSharedPtr = (ptrGrid ? true : false);
            write_pod(p_os, bSharedPtr);
            if (bSharedPtr)
            {
                write_item(p_os, ptrGrid);
                write_item(p_os, p_state.getInterpolators());
            }
        }
        // Return "true" on success, "false" on failure
        return status && !p_os.fail();
    }
};

/// \brief  And this is the specialization of GenericReader
//
template <class Stream, class State  >
struct GenericReader < Stream, State, StOpt::GridAndRegressedValue, Int2Type<IOTraits<int>::ISEXTERNAL> >
{
    inline static bool readIntoPtr(StOpt::GridAndRegressedValue  *&ptr, Stream &p_is,
                                   State *, const bool p_processClassId)
    {

        if (p_processClassId)
        {
            static const ClassId current(ClassId::makeId<StOpt::GridAndRegressedValue>());
            ClassId id(p_is, 1);
            current.ensureSameName(id);
        }

        /* // Deserialize object data. */
        CPP11_auto_ptr<StOpt::BaseRegression > preg =  read_item<StOpt::BaseRegression>(p_is);
        std::shared_ptr<StOpt::BaseRegression> pregShared(std::move(preg));
        bool bSharedPtr ;
        read_pod(p_is, &bSharedPtr);
        std::shared_ptr<StOpt::SpaceGrid > pgridShared;
        CPP11_auto_ptr<std::vector< std::shared_ptr<StOpt::InterpolatorSpectral> > > pinterp;
        if (bSharedPtr)
        {
            CPP11_auto_ptr<StOpt::SpaceGrid> pgrid = read_item<StOpt::SpaceGrid>(p_is);
            pgridShared = std::move(pgrid);
            pinterp = read_item< std::vector< std::shared_ptr<StOpt::InterpolatorSpectral> > >(p_is);
            /// now affect grids to interpolator
            for (size_t i = 0 ; i < pinterp->size(); ++i)
                (*pinterp)[i]->setGrid(& *pgridShared);
        }

        if (p_is.fail())
            // Return "false" on failure
            return false;
        //Build the object from the stored data
        if (ptr)
        {
            if (bSharedPtr)
                *ptr = StOpt::GridAndRegressedValue(pgridShared, pregShared, *pinterp);
            else
                *ptr = StOpt::GridAndRegressedValue(pregShared);
        }
        else
        {
            if (bSharedPtr)
                ptr = new  StOpt::GridAndRegressedValue(pgridShared, pregShared, *pinterp);
            else
                ptr = new  StOpt::GridAndRegressedValue(pregShared);
        }
        return true;
    }

    inline static bool process(StOpt::GridAndRegressedValue &s, Stream &is,
                               State *st, const bool p_processClassId)
    {
        // Simply convert reading by reference into reading by pointer
        StOpt::GridAndRegressedValue *ps = &s;
        return readIntoPtr(ps, is, st, p_processClassId);
    }
};
}



#endif/*  GRIDANDREGRESSEDVALUEGENERS_H */
