// Change History:
// DART Data Transfer

#ifndef DDP_SESSION_H
#define DDP_SESSION_H

#include  <vector>
#include  "ddp_vector.h"
#include  "ddp_field.h"
#include  "ddp_macrodef.h"
#include  "ddp_process.h"
#include  "ddp_params.h"

////////////////////////////////////////////////////////
// DDP_Session class
////////////////////////////////////////////////////////
class DDP_Session
{
public:
    enum { ERROR_SIZE = 500 };
    enum { LOG_OPEN = 1, LOG_WRITE = 2, LOG_CLOSE = 3 };
    
private:
    struct Database
        {
        std::string ID;
        std::string ConfigFile;
        bool IsScalar;
        };

protected:
    struct Job
        {
        Job() : Process(NULL), Params(NULL) {}
        DDP_Process * Process;
        DDP_Params * Params;
        };

private:
    std::vector<Job> Jobs;
    uint NextID;
    HANDLE hPush;
    
    std::vector<Database> Databases;
    std::vector<std::string> Errors;

    // Application Attributes
    const DRS_Application * App;
    std::string sID;
    std::string sDescription;
    std::string sINIFile;
    
    uint VectorCount;
    uint PeriodCount;
    
    DDP_VectorTree AppVectorTree;
    DDP_Layout AppLayout;
    DDP_MacroDef MacroDef;

protected:
    static std::vector<DDP_Session *> SessionPool;
    virtual bool push_err(const byte * err);
    virtual bool dump_export(DDP_Export * exp, const void** Fields,
        const byte * FieldTypes, const byte * FileName, const byte * ExpType);
    uint push_params(DDP_Params * params);
    Job * push_job();
    const Job * get_job(uint num) const { return num < GetJobCount() ? &Jobs[num] : NULL; }
    static uint64 GetFileSize(const byte * fname);

protected:
    DDP_Session();
    virtual ~DDP_Session();

public:
    bool Load(const DRS_Application * appl);
    
    bool Start(const DDP_Process * process);
    bool Stop();

    // application properties:
    const DRS_Application * GetApplication() const { return App; }
    const byte * GetID() const { return PORT_c_str(sID); }
    const byte * GetDescription() const { return PORT_c_str(sDescription); }
    const byte * GetDataPath(const byte * fn, byte * buf, uint sz) const
        { return DRS_Application::DataFilePathGet(fn, buf, sz, GetINIFile() ); }

    const byte * GetINIFile() const { return PORT_c_str(sINIFile); }
    uint GetVectorCount() const { return VectorCount; }
    uint GetPeriodCount() const { return PeriodCount; }

    const DDP_Layout * GetFieldLayout() const { return &AppLayout; }
    const DDP_VectorTree::RootNode * GetVectorRoot(uint vector) const {
        return AppVectorTree.GetRoot(vector); }

    // Macro Interface:
    void RecordMacro(const byte * file) { MacroDef.Record(file); }
    void RunMacro(const byte * file) { MacroDef.Run(file); }
    void StopMacro() { MacroDef.Stop(); }
    const DDP_MacroDef & MacroProcess(const byte * process, uint * cnt = NULL)
      { uint n = MacroDef.AddProcess(process); if(cnt) *cnt = n;
          return MacroDef; }

    // Dump Interface:
    bool DumpRecList(const AT_Record_List *pRecordList, const byte * file,
        bool keepOrder = false);
    bool DumpReportParams(AT_Record_List *pRecordList, const DDP_ExportDef** Fields,
      const DDP_ExportDef** Vectors);
    bool DumpExportParams(AT_Record_List *pRecordList, const void** Fields,
        const byte * FieldTypes, const DDP_Field** SplitFields,
            const byte * FileName, const byte * ExpType, bool AutoSave);
    bool DumpBrowseParams(const AT_Index *pIndex, const void** Fields,
        const byte * FieldTypes, const byte * FileName, const byte * ExpType);

    // Parameteter Interface:
    void OpenProcessParams() { push_params(new DDP_Params); }
    const DDP_Params * GetParams() const;
    bool SetParam(const byte * key, const byte * val);
    bool SetParam(const byte * key, const void * val, uint size)
        { DDP_Params * params = (DDP_Params *)GetParams();
        if(!params) return false;
        params->SetParam(key, val, size); return true; }

    // Process Interface:
    uint PushProcess(DDP_Process * process);
    DDP_Process * PopProcess();
    DDP_Process * FrontProcess() { return GetJobCount() ? Jobs.front().Process :
        NULL; }
    DDP_Process * BackProcess() { return GetJobCount() ? Jobs.back().Process :
        NULL; }
    const DDP_Process * WaitForProcess(const DDP_Process * process) const;

    bool Execute();
    uint GetJobCount() const { return Jobs.size(); }
    uint GetNextID() const { return NextID; }

    void Clear();
    uint GetCachedProcessNames(byte * buf, uint sz) const;

    // Session Pool
    static DDP_Session * PushSession();
    static bool PopSession(DRS_Application * a);
    static DDP_Session * GetSession(DRS_Application * a);
    static bool FindSession(DDP_Session * session);
    static bool LogProcessTime(const DDP_Process * process, uint open, const byte * fmt, ...);
    
    // Database Properties:
    uint GetDatabaseCount() const { return Databases.size(); }
    bool IsScalarDatabase(uint num) const { return num < GetDatabaseCount() ?
        Databases[num].IsScalar : false; }
    const byte * GetDatabaseID(uint num) const { return num < GetDatabaseCount() ?
        PORT_c_str(Databases[num].ID) : NULL; }
    const byte * GetDatabaseConfig(uint num) const { return num < GetDatabaseCount() ?
        PORT_c_str(Databases[num].ConfigFile) : NULL; }

    // Error Interface:
    bool PushError(AT_Error & err);
    bool PushError(const char * fmt, ... );
    uint GetErrorCount() const { return Errors.size(); }
    const byte * GetError(uint num) { return num < GetErrorCount() ?
        PORT_c_str(Errors[num]) : NULL; }
    uint PopError(byte * buf, uint sz);
};

#endif

