/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */

/*!
 *  \file bitflags.hpp
 *  \ingroup building_blocks
 *
 *
 */

/* ***************************************************************************
 *
 *  This program is free software; you can redistribute it and/or modify it
 *  under the terms of the GNU Lesser General Public License version 3 as
 *  published by the Free Software Foundation.
 *
 *  This program 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 this program; if not, write to the Free Software Foundation,
 *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 ****************************************************************************
 */
#ifndef FF_BITFLAGS_HPP
#define FF_BITFLAGS_HPP

#include <vector>
#include <string>
#include <cassert>
namespace ff {

/**
 * Flags used in the  \ref setInPtr and \ref setOutPtr methods for 
 * providing commands to the run-time concerning H2D and D2H data transfers 
 * and device memory allocation.
 *
 */
enum class CopyFlags    { DONTCOPY, COPY };
enum class ReuseFlags   { DONTREUSE,  REUSE };
enum class ReleaseFlags { DONTRELEASE, RELEASE };

struct MemoryFlags {  
    MemoryFlags():copy(CopyFlags::COPY),
                  reuse(ReuseFlags::DONTREUSE),
                  release(ReleaseFlags::DONTRELEASE) {}
    MemoryFlags(CopyFlags c, ReuseFlags r, ReleaseFlags f):copy(c),reuse(r),release(f) {}
    CopyFlags    copy;
    ReuseFlags   reuse;
    ReleaseFlags release;
};

using memoryflagsVector = std::vector<MemoryFlags>;

/**
 * cmd format:
 *  ...;$kernel_1;GPU:0; UF;SF;...;$kernel_2;CPU:0;.... ;$ .....
 *
 * S: send (COPYTO)
 * U: reUse (REUSE)
 * R: receive (COPYFROM)
 * F: free/remove (RELEASE)
 */
static inline const memoryflagsVector extractFlags(const std::string &cmd, const int kernel_id) {
    memoryflagsVector V;
    // no command in input, we just return a vector with one (default) entry
    if (cmd == "") { 
        V.resize(1);
        return V;
    }
    const std::string kid = "kernel_"+std::to_string(kernel_id);
    const char semicolon = ';';
    size_t n = cmd.rfind(kid);
    assert(n != std::string::npos);
    n = cmd.find_first_of(semicolon, n);   // first ';' after kernel_id
    assert(n != std::string::npos);
    n = cmd.find_first_of(semicolon, n+1); // first ';' after device_id
    assert(n != std::string::npos);

    size_t m = cmd.find_first_of('$', n+1);
    assert(m != std::string::npos);
    // gets just the sub-string of the command related to the memory flags 
    // starting and ending with ';' (i.e. ; UF;SF;...;)
    const std::string &flag_string = cmd.substr(n,m-n);
    assert(flag_string != "");

    n = 0;
    m = flag_string.find_first_of(semicolon,n+1);  
    assert(m != std::string::npos);
    V.reserve(10);    
    do {
        const std::string &flags = flag_string.substr(n+1, m-n-1);
        
        struct MemoryFlags mf;
        if (flags.find('S') != std::string::npos)      mf.copy = CopyFlags::COPY;
        else if (flags.find('R') != std::string::npos) mf.copy = CopyFlags::COPY;
        else                                           mf.copy = CopyFlags::DONTCOPY;
        mf.reuse   = (flags.find('U')!=std::string::npos ? ReuseFlags::REUSE     : ReuseFlags::DONTREUSE);
        mf.release = (flags.find('F')!=std::string::npos ? ReleaseFlags::RELEASE : ReleaseFlags::DONTRELEASE);
        V.push_back(mf);
        
        n = m;
        m = flag_string.find_first_of(semicolon, n+1);  
    } while(m != std::string::npos);
    
    return V;
}
    
static inline CopyFlags getCopy(int pos, const memoryflagsVector &V) {
    return V[pos].copy;
}
static inline ReuseFlags getReuse(int pos, const memoryflagsVector &V) {
    return V[pos].reuse;
}
static inline ReleaseFlags getRelease(int pos, const memoryflagsVector &V) {
    return V[pos].release;
}

// *******************************************************************


} // namespace


#endif /* FF_BITFLAGS_HPP */
