/*
 * Copyright (c) 2004-2010 Mellanox Technologies LTD. All rights reserved.
 *
 * This software is available to you under a choice of one of two
 * licenses.  You may choose to be licensed under the terms of the GNU
 * General Public License (GPL) Version 2, available from the file
 * COPYING in the main directory of this source tree, or the
 * OpenIB.org BSD license below:
 *
 *     Redistribution and use in source and binary forms, with or
 *     without modification, are permitted provided that the following
 *     conditions are met:
 *
 *      - Redistributions of source code must retain the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer.
 *
 *      - Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

/*
 * Fabric Utilities Project
 *
 * Data Model Header file
 *
 */

#ifndef IBDM_FABRIC_H
#define IBDM_FABRIC_H

#if HAVE_CONFIG_H
#  include "config.h"
#endif

#include <functional>
#include <map>
#include <set>
#include <list>
#include <vector>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/types.h>
#include <inttypes.h>

#define IB_LID_UNASSIGNED 0
#define IB_LFT_UNASSIGNED 255
#define IB_SLT_UNASSIGNED 255
#define IB_HOP_UNASSIGNED 255
#define IB_NUM_SL 16

#if __WORDSIZE == 64
#define PRIx64 "lx"
#else
#define PRIx64 "llx"
#endif
using namespace std;


///////////////////////////////////////////////////////////////////////////////
//
// STL TYPEDEFS
//
// This section defines typedefs for all the STL containers used by the package

// for comparing strings
struct strless : public binary_function <string, string, bool> {
	bool operator()(const string& x, const string& y) const {
		return (strcmp(x.c_str(), y.c_str()) < 0);
	}
};

typedef vector<int > vec_int;
typedef vector<vec_int > vec_vec_int;
typedef vector<uint8_t > vec_byte;
typedef vector<uint64_t > vec_uint64;
typedef vector<vec_byte > vec_vec_byte;
typedef vector<vec_vec_byte > vec3_byte;

typedef vector<class IBPort * > vec_pport;
typedef vector<class IBNode * > vec_pnode;
typedef vector<class VChannel * > vec_pvch;
typedef map< string, class IBSysPort *, strless > map_str_psysport;
typedef map< string, class IBNode *, strless > map_str_pnode;
typedef map< string, class IBSystem *, strless > map_str_psys;
typedef map< uint64_t, class IBPort *, less<uint64_t> > map_guid_pport;
typedef map< uint64_t, class IBNode *, less<uint64_t> > map_guid_pnode;
typedef map< uint64_t, class IBSystem *, less<uint64_t> > map_guid_psys;
typedef map< string, int, strless > map_str_int;
typedef list<class IBNode * > list_pnode;
typedef list<class IBSystem * > list_psystem;
typedef list<int > list_int;
typedef list<char * > list_charp;
typedef list<string > list_str;
typedef map< IBNode *, int, less< IBNode *> > map_pnode_int;
typedef map< IBNode *, vec_int, less< IBNode *> > map_pnode_vec_int;
typedef map< IBSystem *, int, less< IBSystem *> > map_psystem_int;
typedef set< uint16_t, less< uint16_t > > set_uint16;


///////////////////////////////////////////////////////////////////////////////
//
// CONSTANTS
//
#define FABU_LOG_NONE 0x0
#define FABU_LOG_ERROR 0x1
#define FABU_LOG_INFO 0x2
#define FABU_LOG_VERBOSE 0x4
#define IBNODE_UNASSIGNED_RANK 0xFF

// DFS constants type
typedef enum {Untouched,Open,Closed} dfs_t;

//
// GLOBALS
//
// Log level should be part of the "main" module
extern uint8_t FabricUtilsVerboseLevel;
int ibdmUseInternalLog();
int ibdmUseCoutLog();
char *ibdmGetAndClearInternalLog();


///////////////////////////////////////////////////////////////////////////////
// We only recognize CA or SW nodes
typedef enum {IB_UNKNOWN_NODE_TYPE, IB_CA_NODE, IB_SW_NODE} IBNodeType;

static inline IBNodeType char2nodetype(const char *w)
{
    if (!w || (*w == '\0')) return IB_UNKNOWN_NODE_TYPE;
    if (!strcmp(w,"SW")) return IB_SW_NODE;
    if (!strcmp(w,"CA")) return IB_CA_NODE;
    return IB_UNKNOWN_NODE_TYPE;
};

static inline const char * nodetype2char(const IBNodeType w)
{
    switch (w) {
    case IB_SW_NODE: return("SW"); break;
    case IB_CA_NODE: return("CA"); break;
    default: return("UNKNOWN");
    }
};


typedef enum {IB_UNKNOWN_LINK_WIDTH = 0,
              IB_LINK_WIDTH_1X = 1,
              IB_LINK_WIDTH_4X = 2,
              IB_LINK_WIDTH_8X = 4,
              IB_LINK_WIDTH_12X =8,
} IBLinkWidth;

static inline IBLinkWidth char2width(const char *w)
{
    if (!w || (*w == '\0')) return IB_UNKNOWN_LINK_WIDTH;
    if (!strcmp(w,"1x")) return IB_LINK_WIDTH_1X;
    if (!strcmp(w,"4x")) return IB_LINK_WIDTH_4X;
    if (!strcmp(w,"8x")) return IB_LINK_WIDTH_8X;
    if (!strcmp(w,"12x")) return IB_LINK_WIDTH_12X;
    return IB_UNKNOWN_LINK_WIDTH;
};

static inline const char * width2char(const IBLinkWidth w)
{
    switch (w) {
    case IB_LINK_WIDTH_1X: return("1x"); break;
    case IB_LINK_WIDTH_4X: return("4x"); break;
    case IB_LINK_WIDTH_8X: return("8x"); break;
    case IB_LINK_WIDTH_12X: return("12x"); break;
    default: return("UNKNOWN");
    }
};


typedef enum {IB_UNKNOWN_LINK_SPEED = 0,
              IB_LINK_SPEED_2_5 = 1,
              IB_LINK_SPEED_5   = 2,
              IB_LINK_SPEED_10  = 4,
} IBLinkSpeed;

static inline const char * speed2char(const IBLinkSpeed s)
{
    switch (s) {
    case IB_LINK_SPEED_2_5: return("2.5"); break;
    case IB_LINK_SPEED_5:   return("5"); break;
    case IB_LINK_SPEED_10:  return("10"); break;
    default: return("UNKNOWN");
    }
};

static inline IBLinkSpeed char2speed(const char *s)
{
    if (!s || (*s == '\0')) return IB_UNKNOWN_LINK_SPEED;
    if (!strcmp(s,"2.5")) return IB_LINK_SPEED_2_5;
    if (!strcmp(s,"5"))   return IB_LINK_SPEED_5;
    if (!strcmp(s,"10"))  return IB_LINK_SPEED_10;
    return IB_UNKNOWN_LINK_SPEED;
};


static inline string guid2str(uint64_t guid) {
    char buff[18];
    sprintf(buff, "0x%016" PRIx64 , guid);
    //string res(buff);
    return buff;
};


///////////////////////////////////////////////////////////////////////////////
//
// Virtual Channel class
// Used for credit loops verification
//

class VChannel {
    vec_pvch depend;    // Vector of dependencies
    dfs_t    flag;      // DFS state
public:
    IBPort  *pPort;
    int vl;

    // Constructor
    VChannel(IBPort * p, int v) {
        flag = Untouched;
        pPort = p;
        vl = v;
    };

    //Getters/Setters
    inline void setDependSize(int numDepend) {
        if (depend.size() != (unsigned)numDepend) {
            depend.resize(numDepend);
            for (int i=0;i<numDepend;i++) {
                depend[i] = NULL;
            }
        }
    };
    inline int getDependSize() {
        return depend.size();
    };

    inline dfs_t getFlag() {
        return flag;
    };
    inline void setFlag(dfs_t f) {
        flag = f;
    };

    inline void setDependency(int i,VChannel* p) {
        depend[i]=p;
    };
    inline VChannel* getDependency(int i) {
        return depend[i];
    };
};


///////////////////////////////////////////////////////////////////////////////
//
// IB Port class.
// This is not the "End Node" but the physical port of
// a node.
//
class IBPort {
    uint64_t        guid;           // The port GUID (on SW only on Port0)
public:
    class IBPort    *p_remotePort;  // Port connected on the other side of link
    class IBSysPort *p_sysPort;     // The system port (if any) connected to
    class IBNode    *p_node;        // The node the port is part of.
    vec_pvch        channels;       // Virtual channels associated with the port
    int             num;            // Physical ports are identified by number.
    unsigned int    base_lid;       // The base lid assigned to the port.
    IBLinkWidth      width;          // The link width of the port
    IBLinkSpeed     speed;          // The link speed of the port
    unsigned int    counter1;       // a generic value to be used by various algorithms
    unsigned int    counter2;       // a generic value to be used by various algorithms

    // constructor
    IBPort(IBNode *p_nodePtr, int number);

    // destructor:
    ~IBPort();

    // get the port name
    string getName();

    // connect the port to another node port
    void connect(IBPort *p_otherPort,
            IBLinkWidth w = IB_LINK_WIDTH_4X,
            IBLinkSpeed s = IB_LINK_SPEED_2_5);

    // disconnect the port. Return 0 if successful
    int disconnect(int duringSysPortDisconnect = 0);

    inline uint64_t guid_get() {return guid;};
    void guid_set(uint64_t g);
};


///////////////////////////////////////////////////////////////////////////////
//
// Private App Data
//
typedef union _PrivateAppData {
    void        *ptr;
    uint64_t    val;
} PrivateAppData;


//
// IB Node class
//
class IBNode {
    uint64_t guid;
 public:
    string          name;           // Name of the node (instance name of the chip)
    IBNodeType      type;           // Either a CA or SW
    uint32_t        devId;          // The device ID of the node
    uint32_t        revId;          // The device revision Id.
    uint32_t        vendId;         // The device Vendor ID.
    uint8_t         rank;           // The tree level the node is at 0 is root.
    class IBSystem  *p_system;      // What system we belong to
    class IBFabric  *p_fabric;      // What fabric we belong to.
    unsigned int    numPorts;       // Number of physical ports
    string          attributes;     // Comma-sep string of arbitrary attributes k=v
    vec_pport       Ports;          // Vector of all the ports
    vec_vec_byte    MinHopsTable;   // Table describing minimal hop count through
                                    // each port to each target lid
    vec_byte        LFT;            // The LFT of this node (for switches only)
    vec_byte        PSL;            // PSL table (CAxCA->SL mapping) of this node (for CAs only)
    vec3_byte       SLVL;           // SL2VL table of this node (for switches only)
    vec_uint64      MFT;            // The Multicast forwarding table
    PrivateAppData  appData1;       // Application Private Data #1
    PrivateAppData  appData2;       // Application Private Data #2

    // Constructor
    IBNode(string n, class IBFabric *p_fab, class IBSystem *p_sys,
            IBNodeType t, int np);

    // destructor:
    ~IBNode();

    // create a new port by name if required to
    inline IBPort *makePort(unsigned int num) {
        if ((num < 1) || (num > numPorts)) {
            cout << "-E- Given port number out of range: 1 < " << num
                    << " < " << numPorts << endl;
            return NULL;
        }
        if (! Ports[num - 1]) {
            Ports[num - 1] = new IBPort(this, num);
        }
        return Ports[num - 1];
    }

    // get a port by number num = 1..N:
    inline IBPort *getPort(unsigned int num) {
        if ((Ports.size() < num) || (num == 0))
            return NULL;
        else
            return Ports[num - 1];
    }

    // Set the min hop for the given port (* is all) lid pair
    void setHops(IBPort *p_port, unsigned int lid, int hops);

    // Get the min number of hops defined for the given port or all
    int getHops(IBPort *p_port, unsigned int lid);

    // Report Hop Table of the given node
    void repHopTable();

    // Scan the node ports and find the first port
    // with min hop to the lid
    IBPort *getFirstMinHopPort(unsigned int lid);

    // Set the Linear Forwarding Table:
    void setLFTPortForLid(unsigned int lid, unsigned int portNum);

    // Get the LFT for a given lid
    int getLFTPortForLid(unsigned int lid);

    // Set the PSL table
    void setPSLForLid(unsigned int lid, unsigned int maxLid, uint8_t sl);

    // Add entry to SL2VL table
    void setSLVL(unsigned int iport,unsigned int oport,uint8_t sl, uint8_t vl);

    // Get the PSL table for a given lid
    uint8_t getPSLForLid(unsigned int lid);

    // Get the SL2VL table entry
    uint8_t getSLVL(unsigned int iport, unsigned int oport, uint8_t sl);

    // Set the Multicast FDB table
    void setMFTPortForMLid(unsigned int lid, unsigned int portNum);

    // Get the list of ports for the givan MLID from the MFT
    list_int getMFTPortsForMLid(unsigned int lid);

    int getFirstPortLid();

    inline uint64_t guid_get() {return guid;};
    void guid_set(uint64_t g);
}; // Class IBNode


///////////////////////////////////////////////////////////////////////////////
//
// System Port Class
// The System Port is a front pannel entity.
//
class IBSysPort {
public:
    string          name;               // The front pannel name of the port
    class IBSysPort *p_remoteSysPort;   // If connected the other side sys port
    class IBSystem  *p_system;          // System it benongs to
    class IBPort    *p_nodePort;        // The node port it connects to.

    // Constructor
    IBSysPort(string n, class IBSystem *p_sys);

    // destructor:
    ~IBSysPort();

    // connect two SysPorts
    void connect(IBSysPort *p_otherSysPort,
            IBLinkWidth width = IB_LINK_WIDTH_4X,
            IBLinkSpeed speed = IB_LINK_SPEED_2_5);

    // disconnect the SysPort (and ports). Return 0 if successful
    int disconnect(int duringPortDisconnect = 0);
};


///////////////////////////////////////////////////////////////////////////////
//
// IB System Class
// This is normally derived into a system specific class
//
class IBSystem {
    uint64_t            guid;
public:
    string              name;       // the "host" name of the system
    string              type;       // what is the type i.e. Cougar, Buffalo etc
    class IBFabric      *p_fabric;  // fabric belongs to
    map_str_psysport    PortByName; // A map provising pointer to the SysPort by name
    map_str_pnode       NodeByName; // All the nodes belonging to this system.

    // Default Constractor - empty need to be overwritten
    IBSystem() {} ;

    // Destructor must be virtual
    virtual ~IBSystem();

    // Constractor
    IBSystem(string n, class IBFabric *p_fab, string t);

    // Get a string with all the System Port Names (even if not connected)
    virtual list_str getAllSysPortNames();

    // Get a Sys Port by name
    IBSysPort *getSysPort(string name);

    inline IBNode *getNode(string nName) {
        map_str_pnode::iterator nI = NodeByName.find(nName);
        if (nI != NodeByName.end()) {
            return (*nI).second;
        } else {
            return NULL;
        }
    };

    // make sure we got the port defined (so define it if not)
    virtual IBSysPort *makeSysPort(string pName);

    // get the node port for the given sys port by name
    virtual IBPort *getSysPortNodePortByName(string sysPortName);

    // Remove a sub module of the system
    int removeBoard(string boardName);

    // parse confinguration string into a vector
    void cfg2Vector(const string& cfg,
            vector<string>& boardCfgs,
            int numBoards);

    // Write system IBNL into the given directory and return IBNL name
    int dumpIBNL(const char *ibnlDir, string &sysType);

    inline uint64_t guid_get() {return guid;};
    void guid_set(uint64_t g);
};


///////////////////////////////////////////////////////////////////////////////
//
// IB Fabric Class
// The entire fabric
//
class IBFabric {
public:
    map_str_pnode   NodeByName;      // Provide the node pointer by its name
    map_guid_pnode  NodeByGuid;     // Provides the node by guid
    map_str_psys    SystemByName;   // Provide the system pointer by its name
    map_guid_psys   SystemByGuid;   // Provides the system by guid
    map_guid_pport  PortByGuid;     // Provides the port by guid
    vec_pport       PortByLid;      // Pointer to the Port by its lid
    unsigned int    minLid;         // Track min lid used.
    unsigned int    maxLid;         // Track max lid used.
    unsigned int    lmc;            // LMC value used
    uint8_t         defAllPorts;    // If not zero all ports (unconn) are declared
    uint8_t         subnCANames;    // The Subnet.lst has host names for CA's
    uint8_t         numSLs;         // Number of used SLs
    uint8_t         numVLs;         // Number of used VLs
    set_uint16      mcGroups;       // A set of all active multicast groups

    // Constructor
    IBFabric() {
        maxLid = 0;
        defAllPorts = 1;
        subnCANames = 1;
        numSLs = 1;
        numVLs = 1;
        lmc = 0;
        minLid = 0;
        PortByLid.push_back(NULL); // make sure we always have one for LID=0
    };

    // Destructor
    ~IBFabric();

    // get the node by its name (create one of does not exist)
    IBNode *makeNode(string n,
                IBSystem *p_sys,
                IBNodeType type,
                unsigned int numPorts);

    // get port by guid:
    IBPort *getPortByGuid(uint64_t guid);

    // get the node by its name
    IBNode *getNode(string name);
    IBNode *getNodeByGuid(uint64_t guid);

    // return the list of node pointers matching the required type
    list_pnode *getNodesByType(IBNodeType type);

    // crate a new generic system - basically an empty contaner for nodes...
    IBSystem *makeGenericSystem(string name);

    // crate a new system - the type must have a pre-red SysDef
    IBSystem *makeSystem(string name, string type, string cfg = "");

    // get a system by name
    IBSystem *getSystem(string name);

    // get a system by guid
    IBSystem *getSystemByGuid(uint64_t guid);

    // Add a cable connection
    int addCable(string t1, string n1, string p1,
            string t2, string n2, string p2,
            IBLinkWidth width, IBLinkSpeed speed);

    // Parse the cables file and build the fabric
    int parseCables(string fn);

    // Parse Topology File
    int parseTopology(string fn);

    // Add a link into the fabric - this will create system
    // and nodes as required.
    int addLink(string type1, int numPorts1, uint64_t sysGuid1,
            uint64_t nodeGuid1,  uint64_t portGuid1,
            int vend1, int devId1, int rev1, string desc1,
            int hcaIdx1, int lid1, int portNum1,
            string type2, int numPorts2, uint64_t sysGuid2,
            uint64_t nodeGuid2,  uint64_t portGuid2,
            int vend2, int devId2, int rev2, string desc2,
            int hcaIdx2, int lid2, int portNum2,
            IBLinkWidth width, IBLinkSpeed speed);

    // Parse the OpenSM subnet.lst file and build the fabric from it.
    int parseSubnetLinks (string fn);

    // Parse OpenSM FDB dump file
    int parseFdbFile(string fn);

    // Parse PSL mapping
    int parsePSLFile(string fn);

    // Parse SLVL mapping
    int parseSLVLFile(string fn);

    // Parse an OpenSM MCFDBs file and set the MFT table accordingly
    int parseMCFdbFile(string fn);

    // set a lid port
    inline void setLidPort(unsigned int lid, IBPort *p_port) {
        if ( PortByLid.empty() || (PortByLid.size() < lid + 1))
            for (unsigned int i = PortByLid.size(); i < lid + 1; i++)
                PortByLid.push_back(NULL);
        PortByLid[lid] = p_port;
        if (maxLid < lid)
            maxLid = lid;
    };

    // get a port by lid
    inline IBPort *getPortByLid(unsigned int lid) {
        if ( PortByLid.empty() || (PortByLid.size() < lid + 1))
            return NULL;
        return (PortByLid[lid]);
    };

    //set number of SLs
    inline void setNumSLs(uint8_t nSL) {
        numSLs=nSL;
    };

    //get number of SLs
    inline uint8_t getNumSLs() {
        return numSLs;
    };

    //set number of VLs
    inline void setNumVLs(uint8_t nVL) {
        numVLs=nVL;
    };

    //get number of VLs
    inline uint8_t getNumVLs() {
        return numVLs;
    };

    // dump out the contents of the entire fabric
    void dump(ostream &sout);

    // write out a topology file
    int dumpTopology(const char *fileName, const char *ibnlDir);

    // Write out the name to guid and LID map
    int dumpNameMap(const char *fileName);

    // Parse name map file and set GUIDs of the nodes by their name
    int setNodeGuidsByNameMapFile(const char *fileName);

private:
    int parseSubnetLine(char *line);
};


#endif /* IBDM_FABRIC_H */

