//+=================================================================================================================
//
// file :               ClassPipe.cpp
//
// description :        C++ source code for the MultiClassPipe
//
// project :            TANGO
//
// author(s) :          E.Taurel
//
// Copyright (C) :      2014,2015
//                        European Synchrotron Radiation Facility
//                      BP 220, Grenoble 38043
//                      FRANCE
//
// This file is part of Tango.
//
// Tango is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Tango is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along with Tango.
// If not, see <http://www.gnu.org/licenses/>.
//
//
//-==================================================================================================================

#include <tango/server/classpipe.h>
#include <tango/server/pipe.h>
#include <tango/server/deviceclass.h>
#include <tango/client/Database.h>

namespace Tango
{

PipeProperty::~PipeProperty() = default;

PipeProperty::PipeProperty(const PipeProperty &) = default;
PipeProperty &PipeProperty::operator=(const PipeProperty &) = default;

PipeProperty::PipeProperty(PipeProperty &&) = default;
PipeProperty &PipeProperty::operator=(PipeProperty &&) = default;

//+-------------------------------------------------------------------------------------------------------------------
//
// method :
//        MultiClassPipe::MultiClassPipe
//
// description :
//        Constructor for the MultiClassPipe class
//
//--------------------------------------------------------------------------------------------------------------------

MultiClassPipe::MultiClassPipe()
{
    TANGO_LOG_DEBUG << "Entering MultiClassPipe constructor" << std::endl;
}

MultiClassPipe::~MultiClassPipe() = default;

//+-------------------------------------------------------------------------------------------------------------------
//
// method :
//        MultiClassPipe::init_class_pipe
//
// description :
//        Ask the database for properties defined at class level and build the ClassPipe object for each pipe
//        with defined properties
//
// argument :
//        in :
//            - cl_ptr : The device class object pointer
//
//-------------------------------------------------------------------------------------------------------------------

void MultiClassPipe::init_class_pipe(DeviceClass *cl_ptr)
{
    TANGO_LOG_DEBUG << "Entering MultiClassPipe::init_class_pipe" << std::endl;

    Tango::Util *tg = Tango::Util::instance();

    std::vector<Pipe *> &pi_list = cl_ptr->get_pipe_list();
    size_t nb_pipe = pi_list.size();

    //
    // Get class attribute(s) properties stored in DB. No need to implement a retry here (in case of db server restart)
    // because the db reconnection is forced by the get_property call executed during xxxClass construction
    // before we reach this code.
    //

    if((nb_pipe != 0) && (Tango::Util::instance()->use_db()))
    {
        size_t i;
        Tango::DbData db_list;
        std::string &class_name = cl_ptr->get_name();
        size_t nb_db_requested_pipe = 0;

        //
        // Ask for class default prop in DB only for pipe with label or desc not already set to some user default value
        //

        for(i = 0; i < nb_pipe; i++)
        {
            Pipe *pi_ptr = pi_list[i];
            std::string &pi_name = pi_ptr->get_name();
            if(pi_ptr->get_label() == pi_name || pi_ptr->get_desc() == DescNotSpec)
            {
                db_list.emplace_back(pi_name);
                nb_db_requested_pipe++;
            }
        }

        try
        {
            tg->get_database()->get_class_pipe_property(class_name, db_list, tg->get_db_cache());
        }
        catch(Tango::DevFailed &e)
        {
            std::stringstream ss;
            ss << "Can't get class pipe properties for class " << class_name;

            TANGO_RETHROW_EXCEPTION(e, API_DatabaseAccess, ss.str());
        }

        //
        // Sort property for each pipe
        //

        long ind = 0;
        for(i = 0; i < nb_db_requested_pipe; i++)
        {
            std::vector<PipeProperty> prop_list;

            std::string pipe_name = db_list[ind].name;
            long nb_prop = 0;
            db_list[ind] >> nb_prop;

            ind++;
            for(long j = 0; j < nb_prop; j++)
            {
                if(db_list[ind].size() > 1)
                {
                    std::string tmp(db_list[ind].value_string[0]);
                    long nb = db_list[ind].size();
                    for(int k = 1; k < nb; k++)
                    {
                        tmp = tmp + " ";
                        tmp = tmp + db_list[ind].value_string[k];
                    }
                    prop_list.emplace_back(db_list[ind].name, tmp);
                }
                else
                {
                    prop_list.emplace_back(db_list[ind].name, db_list[ind].value_string[0]);
                }
                ind++;
            }

            if(nb_prop != 0)
            {
                pipe_prop_list.insert({pipe_name, prop_list});
            }
        }
    }

    TANGO_LOG_DEBUG << "Leaving MultiClassPipe::init_class_pipe" << std::endl;
}

//+-------------------------------------------------------------------------------------------------------------------
//
// method :
//        MultiClassPipe::get_prop_list
//
// description :
//        Get list of pipe property (defined at class level) for the pipe specified as input arg
//
// argument :
//         in :
//            - pipe_name : The pipe name
//
// return :
//        Reference to the vector of pipe properties or throw an exception if the pipe is not found
//
//--------------------------------------------------------------------------------------------------------------------

std::vector<Tango::PipeProperty> &MultiClassPipe::get_prop_list(const std::string &pipe_name)
{
    std::map<std::string, std::vector<PipeProperty>>::iterator ite;
    ite = pipe_prop_list.find(pipe_name);
    if(ite == pipe_prop_list.end())
    {
        std::stringstream ss;
        ss << "Pipe " << pipe_name << " not found in class pipe(s) properties" << std::ends;

        TANGO_THROW_EXCEPTION(API_PipeNotFound, ss.str());
    }

    return ite->second;
}

} // namespace Tango
