// Change History:
// 03/18/04 AV Development: Write APP_ExportFile only id AutoSave or no slit fields
// 01/12/04 AV Development: Check DRS_Application::GetStatus() before using Appl/process pointers. See bug #57
// 01/08/04 AV Development: Add call to MacroProcess to fix bug #55
// 12/09/03 AV Development: Set last env from dump
// 12/09/03 AV Development: Remove env param from Execute. Make LastEnv type of Env
// 12/09/03 AV Development: Run all processes sequentially. Add LastThreadId
// 11/13/03 AV Development: Add MacroInfo to keep track of reports and fill MacroID
// 10/22/03 AV Development: write LO_FieldType param
// 10/23/03 AV Development: write macro info in report viewer ini file
// 10/24/03 AV Development: return num of sub fields from SetMultiFile func

// DART Data Process

#include  <process.h>
#include <io.h>
#include <time.h>

#include "ddp/ddp_process.h"
#include "ddp/ddp_session.h"
#include "base/AT_GlobalTypes.h"
#include "base/AT_StringFunctions.h"

#include <map>

// **************************************************************************
// DDP_Process
// **************************************************************************
DDP_Process::DDP_Process(const byte * make, const byte * process, const DDP_Session * session) :
    Session(session), MakeType(make ? (char *)make : ""), Environment(NULL),
    ProcessName(process ? (char *)process : ""), ID(0), hEvent(NULL), Active(false)
{
    if(Session)
        ID = Session->GetNextID();
}

//----------------------------------------------------------------------------

DDP_Process::~DDP_Process()
{
    Cleanup(true);
    ReleaseEnv();
    CloseEvent();

    clear_scalar();
    clear_vector();
}
//----------------------------------------------------------------------------

const DDP_Params * DDP_Process::get_params()
{
    if(!Session)
        return NULL;

    DDP_Session * session = (DDP_Session *)Session;
    const DDP_Params * params = session->GetParams();
    if(!params)
        // make sure there is a parameter collection for a process
        session->OpenProcessParams();
        
return session->GetParams();
}
//----------------------------------------------------------------------------

const DDP_Params * DDP_Process::get_params() const
{
return Session ? Session->GetParams() : NULL;
}
//----------------------------------------------------------------------------

void DDP_Process::clear_scalar()
{
    // delete fields
    uint i, cnt = get_scalar_count();
    for(i = 0; i < cnt; ++i)
        {
        const DDP_Field * f = get_scalar(i);
        delete f;
        }
    ScalarFields.clear();
}
//----------------------------------------------------------------------------

const DDP_Field * DDP_Process::find_scalar_field(const DDP_Field * ff, uint & pos) const
{
    if(!ff)
        return NULL;

    if(ff->IsNull())
        return NULL;
        
    uint i, cnt = get_scalar_count();
    for(i = 0; i < cnt; ++i)
        {
        const DDP_Field * f = get_scalar(i);

        if(f->GetActionType() == DARTAGG_EXCLUDE)
            continue;
        if(f->IsNull())
            continue;
                    
        if(!stricmp((const char *)f->GetCaption(), (const char *)ff->GetCaption() ))
            {
            pos = i;
            return f;
            }
        }

return NULL;        
}
//----------------------------------------------------------------------------

void DDP_Process::clear_vector()
{
    uint i, cnt = get_vector_count();
    for(i = 0; i < cnt; ++i)
        {
        const DDP_VectorDef * v = get_vector(i);
        delete v;
        }
    VectorFields.clear();
}
//----------------------------------------------------------------------------

const DDP_VectorDef * DDP_Process::find_vector_field(const DDP_VectorDef * ff, uint & pos) const
{
    if(!ff)
        return NULL;

    if(ff->IsNull())
        return NULL;
        
    byte bff[DDP_PrintFile::LINE_SIZE];
    byte bf[DDP_PrintFile::LINE_SIZE];

    ff->GetDescr(bff, sizeof(bff) );

    uint i, cnt = get_vector_count();
    for(i = 0; i < cnt; ++i)
        {
        const DDP_VectorDef * f = get_vector(i);

        if(f->GetActionType() == DARTAGG_EXCLUDE)
            continue;
        if(f->IsNull())
            continue;
            
        f->GetDescr(bf, sizeof(bf) );

        if(!stricmp((const char *)bff, (const char *)bf ))
            {
            pos = i;
            return f;
            }
        }

return NULL;        
}
//----------------------------------------------------------------------------

const byte * DDP_Process::get_session_file() const
{
    return Session ? Session->GetINIFile() :
        NULL;
}
//----------------------------------------------------------------------------

const byte * DDP_Process::get_session_id() const
{
    return Session ? Session->GetID() :
        NULL;
}
//----------------------------------------------------------------------------

bool DDP_Process::new_env()
{
    if(!Session)
        return false;
    uint i;
    Environment = new Env;
    Env * p = get_env();

    p->SetNum(ID);

    // add params
    std::string sWork;
    byte buf[500];

    bool res = p->PutEnvVar(GetApplId(), get_session_id());
    res &= p->PutEnvVar(GetMakeType(), PORT_c_str(MakeType));
    res &= p->PutEnvVar(GetEnvNum(), (const byte *)PORT_itoa(GetID(), buf));

    sWork = (char *)get_session_file();
    uint pos = sWork.rfind("\\");
    if(pos != std::string::npos)
        sWork.erase(pos + 1);
    GetPrivateProfileStringA( "Application", (const char *)GetWorkPath(), sWork.c_str(),
        (char *)buf, sizeof( buf), (const char *)get_session_file());
    sWork = (char *)buf;
    UTL_String::CheckPath(sWork);
    // environment settings
    res &= p->PutEnvVar(GetWorkPath(), PORT_c_str(sWork));

    // Launch time
    TimeStamp(buf, sizeof(buf));
    res &= p->PutEnvVar(GetLaunchTime(), buf);
    FilePrefix(buf, sizeof(buf), p);
    // param file
    sWork = (char *)buf;
    sWork += (char *)GetParamFileName(buf, sizeof(buf));
    res &= p->PutEnvVar(GetParamFile(), PORT_c_str(sWork));
    if(!res)
        {
        ReleaseEnv();
        return false;
        }

return true;
}

//----------------------------------------------------------------------------

void DDP_Process::ReleaseEnv()
{
    delete Environment;
    Environment = NULL;
}

//----------------------------------------------------------------------------

bool DDP_Process::InitEnv()
{
    if(!Session)
        return false;
return new_env();
}

//----------------------------------------------------------------------------

uint DDP_Process::TimeStamp(byte * buf, uint size)
{
    if(!buf || !size)
        return 0;
    time_t tt;
    time(&tt);
    struct tm * t = localtime(&tt);
    if(!t)
        { *buf = 0; return false; }
    uint n = PORT_snprintf_1((char *)buf, size, "%2.2u_%2.2u_%2.2u-%2.2u.%2.2u.%2.2u",
        t->tm_mon+1, t->tm_mday, t->tm_year%100,
        t->tm_hour, t->tm_min, t->tm_sec);
return n;
}

//----------------------------------------------------------------------------

uint DDP_Process::FilePrefix(byte * buf, uint sz, bool Temp)
{
    const Env * env = get_env();
    if(!buf || !sz || !env)
        return 0;
    uint n;
    const byte * time = env->GetEnvVar(GetLaunchTime());
    uint env_num = env->GetNum();
    const byte * id = get_session_id();

    if(Temp)
      n = PORT_snprintf_1((char *)buf, sz, "%s%s_%u_%s", GetPrefix(), time, env_num, id);
    else
      n = PORT_snprintf_1((char *)buf, sz, "%s_%u_%s", time, env_num, id);
return n;
}
//----------------------------------------------------------------------------

bool DDP_Process::print_database_info()
{
    if(!Session)
        return false;
        
    uint i, cnt = Session->GetDatabaseCount();
    uint scalar_sz = 0, vector_sz = 0;
    for(i = 0; i < cnt; ++i)
        {
        if(Session->IsScalarDatabase(i))
            ++scalar_sz;
        else
            ++vector_sz;
        }
    // dump database info
    if(!ParamFile.Print((const byte *)"DB_ScalarCount = %u\r\n", scalar_sz))
        return false;
    for(i = 0; i < cnt; ++i)
        {
        if(Session->IsScalarDatabase(i))
            {
            if(!ParamFile.Print((const byte *)"DB_ScalarDatabase = Scalar%s\r\n", Session->GetDatabaseID(i)))
                return false;
            if(!ParamFile.Print((const byte *)"DB_ScalarID = %s\r\n", Session->GetDatabaseID(i)))
                return false;
            }
        }
    if(!ParamFile.Print((const byte *)"DB_VectorCount = %u\r\n", vector_sz))
        return false;
    for(i = 0; i < cnt; ++i)
        {
        if(!Session->IsScalarDatabase(i))
            {
            if(!ParamFile.Print((const byte *)"DB_VectorDatabase = Vector%s\r\n", Session->GetDatabaseID(i)))
                return false;
            if(!ParamFile.Print((const byte *)"DB_VectorID = %s\r\n", Session->GetDatabaseID(i)))
                return false;
            }
        }
        
return true;
}

//----------------------------------------------------------------------------

uint DDP_Process::print_layout_info()
{
    if(!ParamFile.Print((const byte *)";----------Layout------------------------------------------\r\n"))
        return 0;
    uint Cnt = get_scalar_count();
    ParamFile.Print((const byte *)"LO_FieldCount = %u\r\n", Cnt);
    byte buf[DDP_PrintFile::LINE_SIZE];
    std::map<std::string, uint> MaxGroupApportionLevel;

    for(uint i = 0; i < Cnt; ++i)
        {
        DDP_Field * field = (DDP_Field *)get_scalar(i);
        if(!field->Dump(&ParamFile) )
            return 0;
        }
        
return Cnt;
}

//----------------------------------------------------------------------------

bool DDP_Process::write_vector(uint i)
{
    DDP_VectorDef * v = (DDP_VectorDef *)get_vector(i);
    if(v && !v->Dump(&ParamFile))
        return false;

return true;        
}
//----------------------------------------------------------------------------

uint DDP_Process::print_vect_info()
{
    if(!ParamFile.Print((const byte *)";----------Vector Def--------------------------------------\r\n"))
        return 0;

    uint Cnt = get_vector_count();
    uint i;
    for(i = 0; i < Cnt; ++i)
        {
        if(!write_vector(i))
            return 0;
        }

    if(!ParamFile.Print((const byte *)";----------Vector periods----------------------------------\r\n"))
        return 0;
    if(!ParamFile.Print((const byte *)"LO_PeriodCount = %u\r\n", Session->GetPeriodCount()))
        return 0;
        
return Cnt;
}
//----------------------------------------------------------------------------

bool DDP_Process::print_app_info()
{
    if(!Session)
        return false;

    ParamFile.Print((const byte *)"APP_Description = %s\r\n", Session->GetDescription());
    ParamFile.Print((const byte *)"APP_FileName = %s\r\n", Session->GetINIFile());
    ParamFile.Print((const byte *)"APP_Process = %s\r\n", PORT_c_str(ProcessName));
    ParamFile.Print((const byte *)"APP_RecordCount = %u\r\n", GetRecordCount());

return true;
}
//----------------------------------------------------------------------------

bool DDP_Process::print_ui_params()
{
    const DDP_Params * params = get_params();
    if(!params)
        return false;

    // print ui parameters with DDP_ prefix.
    uint i, cnt = params->GetParamCount();
    if(cnt)
        ParamFile.Print((const byte *)";--------------- DDP UI Parameters ---------------------\r\n");

    for(i = 0; i < cnt; ++i)
        {
        if(params->GetParamType(i) == DDP_Params::PARAM_INT ||
            params->GetParamType(i) == DDP_Params::PARAM_TEXT)
            {
            const byte * pName = params->GetParamName(i);
            std::string pVal   = params->GetText(i);

            const byte * tokVal = (const byte *)std::strtok((char *)pVal.c_str(), "\r\n");
            while(tokVal)
                {
                std::string sFormat("DDP_%s = %s\r\n");
                uint len = PORT_strlen(tokVal) + 
						   PORT_strlen(pName) + 
						   sFormat.length();
                if(!ParamFile.NPrint(len, PORT_c_str(sFormat), pName, tokVal))
                    return false;
                tokVal = (const byte *)std::strtok(NULL, "\r\n");
                }
            }
        }
        
return true;
}
//----------------------------------------------------------------------------

bool DDP_Process::AddScalar(const DDP_Field * Field)
{
    if(!Field)
        ScalarFields.push_back(new DDP_Field);
    else
        // make copy
        ScalarFields.push_back(new DDP_Field(*Field));

return true;
}
//----------------------------------------------------------------------------

bool DDP_Process::AddVector(const DDP_VectorDef * Vector)
{
    if(!Vector)
        VectorFields.push_back(new DDP_VectorDef);    
    else
        VectorFields.push_back(new DDP_VectorDef(*Vector));

return true;
}
//----------------------------------------------------------------------------

bool DDP_Process::SetScalarFields(const DDP_ExportDef** Fields)
{
    if(!Fields)
        {
        clear_scalar();
        return true;
        }
    uint i = 0;
    while(Fields[i])
        {
        if(!Fields[i]->IsVector())
            {
            const DDP_Field * f = (const DDP_Field *)Fields[i];
            if(!AddScalar(f))
                return false;
            }
        else
            if(!AddScalar(NULL))
                return false;
        ++i;
        }

return true;
}
//----------------------------------------------------------------------------

bool DDP_Process::SetVectorFields(const DDP_ExportDef** Vectors)
{
    if(!Vectors)
        {
        clear_vector();
        return true;
        }

    uint i = 0;
    while(Vectors[i])
        {
        if(Vectors[i]->IsVector())
            {
            const DDP_VectorDef * v = (const DDP_VectorDef *)Vectors[i];
            if(!AddVector(v))
                return false;
            }
        else
            if(!AddVector(NULL))
                return false;
        ++i;
        }
        
return true;
}
//----------------------------------------------------------------------------

bool DDP_Process::DumpParams(const DDP_ExportDef** Fields,
  const DDP_ExportDef** vectors)
{
    if(!SetScalarFields(Fields))
        return false;
    if(!SetVectorFields(Fields))
        return false;
    if(!SetVectorFields(vectors))
        return false;

return Dump();
}
//----------------------------------------------------------------------------

bool DDP_Process::Dump()
{
    std::string temp;

    if(!InitEnv())
        return false;
    Env * e = get_env();
    if(!e)
        return false;

    temp = (char *)e->GetEnvVar(GetWorkPath());
    temp += (char *)e->GetEnvVar(GetParamFile());

    if(!ParamFile.Open(PORT_c_str(temp)))
        return false;

    byte buf[DDP_PrintFile::LINE_SIZE];
    temp = (char *)e->GetEnvVar(GetWorkPath());
    FilePrefix(buf, sizeof(buf), false);
    ParamFile.Print((const byte *)"[Params]\r\n");

    if(!print_app_info())
        return false;

    uint i;
    // dump database info
    if(!print_database_info())
        return false;

    // dump layout info
    uint res = print_layout_info();
    res += print_vect_info();

    if(res)
        res = ParamFile.Print((const byte *)"LO_NumSize = 4\r\n");

    // dump all ui parameters
    if(res)
        res = print_ui_params();
        
    ParamFile.Close();

return res;
}
//----------------------------------------------------------------------------

uint DDP_Process::_spawn(bool wait, byte ** exe_args, byte ** env, DDP_Process * process)
{
    STARTUPINFOA info;
    PROCESS_INFORMATION process_info;
    memset(&info, 0, sizeof(info));
    info.cb = sizeof(info);

    std::string com_line;
    uint i;
    for(i = 0; exe_args[i]; ++i)
        {
        com_line += "\"";
        com_line += (char *)exe_args[i];
        com_line += "\"";
        if(exe_args[i + 1])
            com_line += " ";
        }
    std::vector<byte> env_block;

    for(i = 0; env[i]; ++i)
        {
        uint sz = PORT_strlen(env[i]) + 1;
        for(uint k = 0; k < sz; ++k)
            env_block.push_back(env[i][k]);
        }

    env_block.push_back('\0');
    uint32 ret = 0;
    DDP_Session * session = process ? (DDP_Session *)process->GetSession() : NULL;
    
    if(!CreateProcessA(NULL, (char *)com_line.c_str(), NULL, NULL, FALSE,
        NORMAL_PRIORITY_CLASS, (char *)&env_block[0], NULL, &info, &process_info))
        {
        if(session && DDP_Session::FindSession(session))
            session->PushError("CreateProcess failed. Command line: %s", PORT_c_str(com_line));
        return MAXUINT;
        }
    if(wait && (WaitForSingleObject(process_info.hProcess, INFINITE) == WAIT_FAILED))
        {
        if(session && DDP_Session::FindSession(session))
            session->PushError("WaitForSingleObject failed. Command line: %s", PORT_c_str(com_line));
        return MAXUINT;
        }
    if(wait && !GetExitCodeProcess(process_info.hProcess, (LPDWORD)&ret))
        {
        if(session && DDP_Session::FindSession(session))
            session->PushError("GetExitCodeProcess failed. Command line: %s. Return code: %u", PORT_c_str(com_line), ret);
        return MAXUINT;
        }

return ret;
}

//----------------------------------------------------------------------------

byte ** DDP_Process::GetArgs(uint & cnt) const
{
    byte buf [400];
    const Env * e = get_env();
    if(!e)
        return NULL;

    cnt = ARG_CNT;
    byte ** args = new byte * [cnt];
    memset(args, 0, sizeof(byte *) * cnt);

    // read engine exe file
    uint sz = GetPrivateProfileStringA( "Application", "DDProcess",
        (const char *)GetDefaultEng(), (char *)buf, sizeof(buf), (const char *)get_session_file());
    sz += (PORT_strlen(e->GetEnvVar(GetWorkPath())) + 2);
    args[0] = new byte [sz];
    std::sprintf((char *)args[0], "%s%s", e->GetEnvVar(GetWorkPath()), buf);
    // read engine make file
    sz = GetPrivateProfileStringA( "Application", "DDProcessMaker",
        (const char *)GetDefaultMake(), (char *)buf, sizeof(buf), (const char *)get_session_file());
    sz += (PORT_strlen(e->GetEnvVar(GetWorkPath())) + 2);
    args[1] = new byte [sz];
    std::sprintf((char *)args[1], "%s%s", e->GetEnvVar(GetWorkPath()), buf);
return args;
}

//----------------------------------------------------------------------------

bool DDP_Process::ReloadEnv()
{
    Env * e = (Env *)get_env();
    if(!e)
        return false;

    byte buf[2 * MAX_PATH + 1];
    std::string tempEnv((char *)GetCleanupFile(buf, sizeof(buf), true));
    uint pos = tempEnv.rfind("*");
    if(pos != std::string::npos)
        tempEnv.erase(pos);

    tempEnv += "DDP_";
    tempEnv += (char *)e->GetEnvVar(GetMakeType());    
    tempEnv += ".env";

return e->Reload(PORT_c_str(tempEnv));
}
//----------------------------------------------------------------------------

bool DDP_Process::Cleanup(bool tempOnly)
{
    if(IsActive())
        return true;

    if(!Environment)
        return false;

    byte buf[2 * MAX_PATH + 1];
    std::string CleanupTemp((char *)GetCleanupFile(buf, sizeof(buf), true));
    DoCleanup(PORT_c_str(CleanupTemp));

    if(!tempOnly)
        {
        std::string CleanupData((char *)GetCleanupFile(buf, sizeof(buf), false));
        DoCleanup(PORT_c_str(CleanupData));
        }
    
return true;
}
//----------------------------------------------------------------------------

void DDP_Process::Run(void * arg)
{
    DDP_Process * process = (DDP_Process *)arg;
    if(!process)
      throw AT_Corrupt_Err(NULL, "Cannot run process. Process is NULL");

    DDP_Session * session = (DDP_Session *)process->GetSession();
    if(!session)
      throw AT_Corrupt_Err(NULL, "Cannot run process. Session is NULL");

    DDP_Process::Env *environment = process->GetEnv();
    if(!environment)
      throw AT_Corrupt_Err(NULL, "Cannot run process. Environment is NULL");

    // enter waiting for the current process
    if(!session->Start(process))
        return;

    // exit. check session
    if(!DDP_Session::FindSession(session))
        return;

    std::string ApplId((char *)session->GetID());

    byte buf[2 * MAX_PATH + 1];
    std::string msg;
    std::string title;
    std::string ProcessName((char *)process->GetProcess());
    std::sprintf((char *)buf, "DART %s Errors", ProcessName.c_str());
    title = (char *)buf;
    try
        {
        uint cnt = 0;
        std::string time_stamp((char *)environment->GetEnvVar(DDP_Process::GetLaunchTime()));

        byte ** args = process->GetArgs(cnt);
        if(!args || cnt == 0)
            {
            std::sprintf((char *)buf, "Cannot start report. No parameters found.");
            throw AT_Corrupt_Err(NULL, buf);
            }
        uint res = DDP_Process::_spawn(true, args, environment->GetEnv(), process);
        // memory cleanup------------------
        // delete args
        for(uint i = 0; i < cnt; ++i)
            delete [] args[i];
        delete [] args;

        //----------------------------------
        if(res == MAXUINT)
            {
            uint err = GetLastError();
            msg = "Error ";
            // get system message
            LPVOID lpMsgBuf;
            FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                          NULL,
                          err,
                          MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
                          (LPTSTR) &lpMsgBuf,
                          0,
                          NULL);
            std::sprintf((char *)buf, "System Code: %u\r\n", err);
            msg += (char *)buf;
            msg += (char*)lpMsgBuf;
            LocalFree(lpMsgBuf);
            }
        else if(res != 0)
            {
            std::sprintf((char *)buf, "Failed to launch process: %s. Error code: %u. Start time: %s",
                ProcessName.c_str(), res, time_stamp.c_str());
            msg = (char *)buf;
            }
        }
    catch (AT_Error &err)
        {
        msg = "Error running process: ";
        msg += (char *)ProcessName.c_str();
        msg += "\n";
        msg += err.getObjectName();
        AT_Error * e = err.getCause();
        while(e)
            {
            msg += "\n";
            msg += e->getObjectName();
            e = e->getCause();
            }
        }

    if(msg.length())
        {
    // load all session errors
        while(DDP_Session::FindSession(session) &&
            session->GetErrorCount())
            {
            uint len = session->PopError(buf, sizeof(buf));
            if(len)
                {
                msg += "\r\n";
                msg.append((char *)buf, len);
                }
            }

        MessageBoxA(NULL, msg.c_str(), title.c_str(), MB_OK | MB_ICONERROR);
        if(DDP_Session::FindSession(session))
            {
            // deactivate
            process->SetActive(false);
            DDP_Session::LogProcessTime(process, DDP_Session::LOG_WRITE, (const byte *)"Error: %s", PORT_c_str(msg));
            }
        }

    if(DDP_Session::FindSession(session))
        session->Stop();
}
//----------------------------------------------------------------------------

bool DDP_Process::Execute()
{
  uint res = _beginthread(Run, 0, (void *)this);
  std::string msg("Failed to start process: ");
  msg += (char *)GetProcess();
  AT_Assert(res != MAXUINT, this, msg.c_str());

return true;
}
//----------------------------------------------------------------------------

bool DDP_Process::RaiseEvent()
{
    hEvent = ::CreateEvent(NULL, TRUE, TRUE, NULL);
    if(!hEvent)
        return false;
    // raise new process
    ResetEvent(hEvent);
return true;
}
//----------------------------------------------------------------------------

bool DDP_Process::CloseEvent()
{
    if(!hEvent)
        return true;

    SetEvent(hEvent);

    CloseHandle(hEvent);
    hEvent = NULL;
    
return true;
}
//----------------------------------------------------------------------------

void DDP_Process::_cleanup(const byte * fn)
{
  struct _finddata_t ffblk;
  intptr_t fh;
  std::string path((char *)fn);
  uint pos = path.rfind('\\');
  if(pos != std::string::npos)
    path.erase(pos + 1);
  else
    path = "";
  fh = _findfirst((const char *)fn, &ffblk);
  if(fh == -1L)
	  return ;

  std::string file;
  int res = 0;
  while (!res)
    {
    file = path;
    file += ffblk.name;
    DeleteFileA(file.c_str());
    res = _findnext(fh, &ffblk);
    }// while cleanup

  _findclose( fh );
}

//----------------------------------------------------------------------------

byte * DDP_Process::GetCleanupFile(byte * fn, uint sz, bool CleanTemp)
{
    const Env * e = get_env();
    if(!e)
        return NULL;
    // cleanup
    const byte *wpath = e->GetEnvVar(GetWorkPath());
    uint s = std::sprintf((char *)fn, "%s", (const char *)wpath);
    FilePrefix(&fn[s], sz - s, CleanTemp);
    std::strcat((char *)fn, "*");
return fn;    
}

//----------------------------------------------------------------------------

void DDP_Process::DoCleanup(const byte * fn)
{
    if(!fn)
        return;
    _cleanup(fn);
}

//----------------------------------------------------------------------------

DDP_Process::Env::Env() : _Env(NULL), VarCount(0), Size(0)
{
    // count env vars
    while(_environ[VarCount])
        ++VarCount;
    Size = (VarCount + ENV_MAXPARAMS + 1);
    _Env = new byte * [Size];
    memset(_Env, 0, Size * sizeof(byte*));
    // load environment
    uint i = 0;
    while(_environ[i])
        {
        uint sz = PORT_strlen(_environ[i]) + 1;
        _Env[i] = new byte [sz];
        memcpy(_Env[i], _environ[i], sz);
        ++i;
        }
}

//----------------------------------------------------------------------------

DDP_Process::Env::~Env()
{
    Clear();
}

//----------------------------------------------------------------------------

void DDP_Process::Env::Clear()
{
    if(!_Env)
        return;
        
    for(uint i = 0; i < VarCount; ++i)
        delete [] _Env[i];
    delete [] _Env;

    _Env = NULL;
    VarCount = 0;
    Size = 0;
}

//----------------------------------------------------------------------------

bool DDP_Process::Env::Reload(const byte * fn)
{
    FILE * fp = std::fopen((const char *)fn, "rt");
    if(!fp)
        return false;

    Clear();
    // count env vars
    while(_environ[VarCount])
        ++VarCount;
    Size = (VarCount + ENV_MAXPARAMS + 1);
    _Env = new byte * [Size];
    memset(_Env, 0, Size * sizeof(byte*));
    // load environment
    uint i = 0;
    while(_environ[i])
        {
        uint sz = PORT_strlen(_environ[i]) + 1;
        _Env[i] = new byte [sz];
        memcpy(_Env[i], _environ[i], sz);
        ++i;
        }

    byte * se;
    byte buf[DDP_PrintFile::LINE_SIZE];
    
    while ((se = (byte *)std::fgets((char *)buf, sizeof(buf), fp)) != NULL)
        {
        byte * key = NULL;
        byte * val = NULL;
        if(UTL_String::ParseEnvLine(se, key, val))
            {
            if(key)
                PutEnvVar(key, val);
            }
        }

    fclose(fp);

return true;    
}

//----------------------------------------------------------------------------

bool DDP_Process::Env::PutEnvVar(const byte * key, const byte * val)
{
    uint pos = UTL_String::FindKey((const byte **)_Env, VarCount, key);
    uint sz = PORT_strlen(key) + (val ? PORT_strlen(val) : 0) + 2;

    if(pos == MAXUINT)
        {
        if(VarCount >= Size)
            return false;

        pos = VarCount;
        _Env[++VarCount] = NULL;
        }

    if(_Env[pos])
        delete [] _Env[pos];

    _Env[pos] = new byte [sz];

    if(val)
        PORT_snprintf_2(_Env[pos], sz, "%s=%s", key, val);
    else
        PORT_snprintf_1(_Env[pos], sz, "%s=", key);

return true;
}

//----------------------------------------------------------------------------

const byte * DDP_Process::Env::GetEnvVar(const byte * key) const
{
  if(!key)
    return NULL;
  uint keylen = PORT_strlen(key);
  if(!keylen)
    return NULL;

  uint sz;
  for(uint i = 0; i < VarCount; ++i)
    {
    const byte * l = _Env[i];
    if(l)
      {
      const byte * eq = (const byte *)std::strchr((const char *)l, '=');
      sz = 0;
      if(eq)
        sz = min(keylen, (uint)(eq - l));
      if(sz && !memicmp(l, key, sz))
        return ++eq;
      }
    }
return NULL;
}

//----------------------------------------------------------------------------

bool DDP_RecListProcess::print_app_info()
{
    if(!DDP_Process::print_app_info())
        return false;

    Env * e = get_env();
    if(!e)
        return false;
        
    byte buf[500];
    FilePrefix(buf, sizeof(buf), e);
    // rec list file
    std::string reclist_file((char *)e->GetEnvVar(GetWorkPath()));
    reclist_file += (char *)buf;
    reclist_file += (char *)GetRecListFileName(buf, sizeof(buf));
    ParamFile.Print((const byte *)"APP_RecListFile = %s\r\n", PORT_c_str(reclist_file));

return DumpRecList(PORT_c_str(reclist_file));
}
//----------------------------------------------------------------------------

void DDP_RecListProcess::SetRecList(const AT_Record_List * rl)
{
    RecList = rl;
    if(!RecList)
        return;

    // set record count
    DDP_Params * params = (DDP_Params *)get_params();
    if(!params)
        return;
    uint cnt = params->GetInt((const byte *)"RecordCount");
    if(!cnt)
        params->SetParam((const byte *)"RecordCount", RecList->count());
}
//----------------------------------------------------------------------------

bool DDP_RecListProcess::DumpRecList(const byte * file)
{
return Session ? ((DDP_Session *)Session)->DumpRecList(RecList, file, IsKeepOrder()) : false;
}
//----------------------------------------------------------------------------

bool DDP_Report::InitEnv()
{
    if(!DDP_RecListProcess::InitEnv())
        return false;
    Env * e = get_env();
    if(!e)
        return false;

    std::string sWork;
    byte buf[500];
    
    // report file
    FilePrefix(buf, sizeof(buf), false);
    ReportFile = (char *)e->GetEnvVar(GetWorkPath());
    ReportFile += (char *)buf;
    ReportFile += (char *)GetReportFileName();
    // report ini file
    FilePrefix(buf, sizeof(buf), false);
    ReportINI = (char *)e->GetEnvVar(GetWorkPath());
    ReportINI += (char *)buf;
    ReportINI += (char *)GetRptViewFileName();
    LogFileName = ReportINI;
    
    if(!RptViewerINI.Open(PORT_c_str(ReportINI)))
        return false;

    if(!RptViewerINI.Print((const byte *)"[Main]\r\n"))
        return false;
    if(!RptViewerINI.Print((const byte *)"ReportFile = %s\r\n", PORT_c_str(ReportFile)))
        return false;

    uint ReportCount;
    const DDP_MacroDef & Macro = ((DDP_Session *)Session)->MacroProcess(GetProcess(), &ReportCount);

    if(ReportCount > 0)
        {
        if(!RptViewerINI.Print((const byte *)"MacroFile = %s\r\n", Macro.GetFileName()))
            return false;
        if(!RptViewerINI.Print((const byte *)"MacroID = %u\r\n", ReportCount - 1))
            return false;
        }
    switch(Macro.GetStatus())
        {
        case DDP_MacroDef::MS_RECORDING:
            if(!RptViewerINI.Print((const byte *)"MacroRecording = yes\r\n"))
                return false;
            break;
        case DDP_MacroDef::MS_RUNNING:
            if(!RptViewerINI.Print((const byte *)"MacroRecording = no\r\n"))
                return false;
            break;
        }
return true;
}

//----------------------------------------------------------------------------

bool DDP_Report::print_database_info()
{
    if(!Session)
        return false;
        
    if(!ParamFile.Print((const byte *)"APP_ReportFile = %s\r\n", PORT_c_str(ReportFile)))
        return false;
    if(!ParamFile.Print((const byte *)"APP_ReportINIFile = %s\r\n", PORT_c_str(ReportINI)))
        return false;

    uint i, cnt = Session->GetDatabaseCount();
    for(uint i = 0; i < cnt; ++i)
        {
        // dump VV file
        if(!Session->IsScalarDatabase(i))
            if(!RptViewerINI.Print((const byte *)"VVPath = %s\r\n", Session->GetDatabaseConfig(i)))
                return false;
    }

return DDP_RecListProcess::print_database_info();
}

//----------------------------------------------------------------------------

uint DDP_Report::print_layout_info()
{
    // report viewer info
    byte num_buf[50];
    byte buf[DDP_PrintFile::LINE_SIZE];
    uint Cnt = DDP_RecListProcess::print_layout_info();
    uint sz;
    uint i, v = 0;
    for(i = 0; ; ++i)
        {
        const DDP_Field * field = get_scalar(i);
        const DDP_VectorDef * vector = get_vector(v);

        if(!field && !vector)
            break;
            
        const byte * caption = field ? field->GetUserDefinedName() : NULL;
        sz = 0;
        if(!caption)
            {
            while(vector && !vector->HasFormula())
                {
                ++v;
                vector = get_vector(v);
                }
            if(vector)
                {
                vector->GetDescr(buf, sizeof(buf), (const byte *)" | ", true);
                caption = buf;
                sz = vector->GetTextSize();
                }
            ++v;
            }
        else if(caption)
            sz = field ? field->get_size() : 0;

        if(caption && sz)
            {
            if(SubReportFields.length())
                {
                SubReportFields += ",";
                SubReportFieldWidths += ",";
                }

            SubReportFields += (char *)caption;
            SubReportFieldWidths += PORT_itoa(sz, num_buf);
            }
        else
            break;
        }

return Cnt;
}
//----------------------------------------------------------------------------

bool DDP_Report::write_vector(uint i)
{
    DDP_VectorDef * v = (DDP_VectorDef *)get_vector(i);
    if(v->HasFormula())
        return true;
    if(AddressCount == Session->GetVectorCount())
        return true;
        
return DDP_RecListProcess::write_vector(i);
}
//----------------------------------------------------------------------------

uint DDP_Report::print_vect_info()
{
    std::string temp;
    byte buf[50];
    uint i, cnt = get_vector_count();

    AddressCount = 0;
    for(i = 0; i < cnt; ++i)
        {
        const DDP_VectorDef * vector = get_vector(i);
        if(!vector)
            continue;
            
        if(vector->HasFormula())
            {
            if(!vector->Dump(&ParamFile))
                return 0;
            }
        else
            {
            if(!vector->IsNull())
                {
                temp += PORT_itoa(vector->GetVectorId(), buf);
                if(i < cnt - 1)
                    temp += ",";
                ++AddressCount;
                }
            }
        }

    // report viewer info
    if(!RptViewerINI.Print((const byte *)"SubReportFields = %s\r\n", PORT_c_str(SubReportFields)))
        return 0;
    if(!RptViewerINI.Print((const byte *)"SubReportFieldWidths = %s\r\n", PORT_c_str(SubReportFieldWidths)))
        return 0;

    // dump vector list to report viewer
    if(!RptViewerINI.Print((const byte *)"VectorList = %s\r\n", PORT_c_str(temp)))
        return false;
    // hardcoded report bucket size
    if(!RptViewerINI.Print((const byte *)"BucketCompBytes = 8\r\n"))
        return false;
    if(!RptViewerINI.Print((const byte *)"NewReport = yes\r\n"))
        return false;

return DDP_RecListProcess::print_vect_info();
}

//----------------------------------------------------------------------------

bool DDP_Report::AddScalar(const DDP_Field * Field)
{
    if(!DDP_RecListProcess::AddScalar(Field))
        return false;

    DDP_Field * f = (DDP_Field *)get_scalar(get_scalar_count() - 1);
    if(!f)
        return false;
    f->SetActionType(DARTAGG_GROUPBY);
    f->SetSortVal(SORTVALUE_SORTASC);

return true;
}
//----------------------------------------------------------------------------

bool DDP_Report::AddVector(const DDP_VectorDef * Vector)
{
    if(!DDP_RecListProcess::AddVector(Vector))
        return false;

    DDP_VectorDef * v = (DDP_VectorDef *)get_vector(get_vector_count() - 1);
    if(!v)
        return false;
    v->SetActionType(DARTAGG_GROUPBY);
    v->SetSortVal(SORTVALUE_SORTASC);
            
return true;
}
//----------------------------------------------------------------------------

bool DDP_Report::Dump()
{
    bool res = DDP_RecListProcess::Dump();

    RptViewerINI.Print((const byte *)"RecordCount = %u\r\n", GetRecordCount());

    // print search description
    
    const DDP_Params * params = get_params();
    if(!params)
        return false;
    std::string val = params->GetText((const byte *)DDP_PARAM_ATTACHDESCR);

    if(val.length())
        {
        if(!RptViewerINI.Print(PORT_cbyte("[Search Description]\r\n") ))
            return false;
        if(!RptViewerINI.NPrint(val.length() + 1, PORT_cbyte("%s\r\n"), PORT_c_str(val) ))
            return false;
        }
    
    RptViewerINI.Close();

return res;
}
//----------------------------------------------------------------------------

const byte * DDP_Export::get_caption(uint num, byte * buf, uint sz) const
{
    const byte * desc = NULL;
    if(stricmp(ExportType.c_str(), "drs"))
        desc = DDP_RecListProcess::get_caption(num, buf, sz);
    else
        {
        const DDP_Field * field = get_scalar(num);
        if(field && buf)
            {
            PORT_strncpy(buf, field->GetID(), sz);
            desc = buf;
            }
        }
    if(!desc)
        PORT_strncpy(buf, "NULL", sz);
        
return buf;
}
//----------------------------------------------------------------------------

uint DDP_Export::get_vector_count() const
{
    if(!PORT_stricmp(ExportType.c_str(), "drs"))
        return 0;
return DDP_RecListProcess::get_vector_count();
}
//----------------------------------------------------------------------------

uint DDP_Export::print_layout_info()
{
    uint i, cnt = DDP_Process::print_layout_info();

    if(!ParamFile.Print((const byte *)"APP_ExportType = %s\r\n", PORT_c_str(ExportType)))
        return 0;

    if(!ParamFile.Print((const byte *)"APP_ExportFile = %s\r\n", PORT_c_str(ExportFile)))
        return 0;
        
return cnt;
}
//----------------------------------------------------------------------------

uint DDP_Export::print_vect_info()
{
    uint i, Cnt = get_vector_count();
    for(i = 0; i < Cnt; ++i)
        {
        bool isLong = !stricmp(ExportType.c_str(), "csv");
        DDP_VectorDef * vector = (DDP_VectorDef *)get_vector(i);

        vector->SetLongName(isLong);
        }
        
    Cnt = DDP_Process::print_vect_info();
    if(!Cnt)
        return 0;

return Cnt;
}
//----------------------------------------------------------------------------

DDP_DataExport::~DDP_DataExport()
{
    uint i, cnt = SplitFileFields.size();
    for(i = 0; i < cnt; ++i)
        delete SplitFileFields[i];
    SplitFileFields.clear();

    cnt = SplitTableFields.size();
    for(i = 0; i < cnt; ++i)
        delete SplitTableFields[i];
    SplitTableFields.clear();

    cnt = SortFields.size();
    for(i = 0; i < cnt; ++i)
        delete SortFields[i];
    SortFields.clear();

    cnt = SortVectorFields.size();
    for(i = 0; i < cnt; ++i)
        delete SortVectorFields[i];
    SortVectorFields.clear();
}
//----------------------------------------------------------------------------

uint DDP_DataExport::SetMultiFile(const DDP_Field** fields)
{
    SplitFileFields.clear();
    uint i = 0;
    while(fields && fields[i])
        {
        SplitFileFields.push_back(new DDP_Field(*fields[i]));
        ++i;
        }

    SplitTableFields.clear();
    const DDP_Params * params = get_params();
    if(!params)
        return i;

    uint f, fcnt = 0;
    const DDP_Field ** pfields = (const DDP_Field ** )params->GetObject(PORT_cbyte("SplitTableFields"), &fcnt);
    if(pfields && fcnt)
        {
        for(f = 0; f < fcnt; ++f)
            SplitTableFields.push_back(new DDP_Field(*pfields[f]));
        }

    // load sort fields
    SortFields.clear();
    SortVectorFields.clear();
    const DDP_ExportDef ** pexp = (const DDP_ExportDef ** )params->GetObject(PORT_cbyte("SortFields"), &fcnt);
    if(pexp && fcnt)
        {
        for(f = 0; f < fcnt; ++f)
            {
            if(pexp[f]->IsVector())
                {
                SortVectorFields.push_back((DDP_VectorDef *)pexp[f]->NewCopy(pexp[f]) );
                SortFields.push_back(new DDP_Field);
                }
            else
                {
                SortFields.push_back((DDP_Field *)pexp[f]->NewCopy(pexp[f]) );
                SortVectorFields.push_back(new DDP_VectorDef);
                }
            }
        }

return i;
}
//----------------------------------------------------------------------------

bool DDP_DataExport::print_field(DDP_Field * field, const byte * skey)
{
    if(!field)
        return false;

    if(skey)
        {
        uint idx;
        const DDP_Field * sf = find_scalar_field(field, idx);
        if(sf)
            {
            field->SetActionType(sf->GetActionType());

            byte cb[DDP_PrintFile::LINE_SIZE];
            PORT_snprintf_1(cb, sizeof(cb), "%u", idx);
            field->SetUserData(cb);
            }
        }
        
return field->Dump(&ParamFile, skey);
}
//----------------------------------------------------------------------------

uint DDP_DataExport::print_layout_info()
{
    uint i, n = SplitFileFields.size();
    if(n && !AutoSave)
        SetExportFile(NULL);
        
    if(!ParamFile.Print((const byte *)";----------Split Fields--------------------------------------\r\n"))
        return 0;
    if(!ParamFile.Print((const byte *)"SplitFieldCount = %u\r\n", n))
        return 0;

    DDP_Field * field;

    for(i = 0; i < n; ++i)
        {
        field = (DDP_Field *)SplitFileFields[i];
        if(!field)
            continue;

        if(!print_field(field, PORT_cbyte("Split") ))
            return 0;
        }

    n = SplitTableFields.size();
    if(n)
        {
        if(!ParamFile.Print((const byte *)";----------Split Table Fields--------------------------------------\r\n"))
            return 0;
        if(!ParamFile.Print((const byte *)"SplitTableFieldCount = %u\r\n", n))
            return 0;

        for(i = 0; i < n; ++i)
            {
            field = (DDP_Field *)SplitTableFields[i];
            if(!field)
                continue;

            if(!print_field(field, PORT_cbyte("SplitTable") ))
                return 0;
            }
        }

    n = SortFields.size();
    if(n)
        {
        if(!ParamFile.Print((const byte *)";----------Sort Fields---------------------------------------------\r\n"))
            return 0;
        if(!ParamFile.Print(PORT_cbyte("SortFieldCount = %u\r\n"), n))
            return 0;

        for(i = 0; i < n; ++i)
            {
            field = (DDP_Field *)SortFields[i];
            if(!field)
                continue;

            if(!print_field(field, PORT_cbyte("Sort") ))
                return 0;
            }
        }

return DDP_Export::print_layout_info();
}
//----------------------------------------------------------------------------

uint DDP_DataExport::print_vect_info()
{
    uint i, n = SortVectorFields.size();
    if(n)
        {
        if(!ParamFile.Print((const byte *)";----------Sort Vector Fields---------------------------------------------\r\n"))
            return 0;
        if(!ParamFile.Print(PORT_cbyte("SortVectorCount = %u\r\n"), n))
            return 0;

        for(i = 0; i < n; ++i)
            {
            DDP_VectorDef * vector = (DDP_VectorDef *)SortVectorFields[i];

            uint idx = 0;
            const DDP_VectorDef * vv = find_vector_field(vector, idx);
            if(vv)
                {
                vector->SetActionType(vv->GetActionType());

                byte bv[DDP_PrintFile::LINE_SIZE];
                PORT_snprintf_1(bv, DDP_PrintFile::LINE_SIZE, "%u", idx);
                vector->SetUserData(bv);
                }

            if(!vector->Dump(&ParamFile, PORT_cbyte("Sort") ))
                return false;
            }
        }

return DDP_Export::print_vect_info();
}
//----------------------------------------------------------------------------

bool DDP_DataExport::MakeLogFileName()
{
    LogFileName = ExportFile;
    uint n = SplitFileFields.size();
    uint ExpCount;
    bool IsRunningMacro = false;
    const DDP_MacroDef & Macro = ((DDP_Session *)Session)->MacroProcess(GetProcess(), &ExpCount);

    uint pos;
    if(ExpCount > 0)
        {
        switch(Macro.GetStatus())
            {
            case DDP_MacroDef::MS_RUNNING:
                IsRunningMacro = true;
                break;
            }
        }

    const DDP_Params * params = get_params();
    if(!params)
        return false;

    std::string prefix = params->GetText((const byte *)DDP_PARAM_AUTONAMEPREFIX);
    if(n)
        {
        UTL_String::CheckPath(LogFileName);
        if(prefix.length())
            LogFileName += prefix;

        uint i;
        for(i = 0; i < n; ++i)
            {
            const DDP_Field * field = SplitFileFields[i];
            if(i)
                LogFileName += "_";
            LogFileName += (char *)field->GetUserDefinedName();
            }
        LogFileName += "_SplitExport_";
        LogFileName += ExportType;
        }
    else
        {
        pos = LogFileName.rfind(".");
        if(pos != std::string::npos)
            {
            LogFileName.replace(pos, 1, 1, '_');
            }
        }
    LogFileName += ".log";

    std::string fname;
    pos = UTL_String::RGetSubStrRight(LogFileName, PORT_cbyte("\\"), fname);
    if(pos != std::string::npos)
        {
        if(UTL_String::RemoveFromList(fname, PORT_cbyte(ILLEGAL_WINFILE_CHARS) ))
            {
            ++pos;
            LogFileName.replace(pos, LogFileName.length() - pos, fname);
            }
        }
    else
        UTL_String::RemoveFromList(LogFileName, PORT_cbyte(ILLEGAL_WINFILE_CHARS) );

    std::string val = params->GetText((const byte *)DDP_PARAM_TIMESTAMP);
    if(val.length())
        {
        const Env * env = get_env();
        const byte * time = env->GetEnvVar(GetLaunchTime());
        UTL_String::InsertBefore(LogFileName, PORT_cbyte(".log"), time, PORT_cbyte("-") );
        }

    if(IsRunningMacro)
        {
        if(!ParamFile.Print((const byte *)"APP_IsRunningMacro = Yes\r\n"))
            return false;

        if(!ParamFile.Print((const byte *)"APP_IsLogFileExist = Yes\r\n"))
            return false;
        }
    else
        {
        // print search description
        val = params->GetText((const byte *)DDP_PARAM_APPENDTABLES);
        if(!UTL_String::GetBool(PORT_c_str(val)))
            ::DeleteFileA(LogFileName.c_str());
        }

    Env * p = get_env();
return p->PutEnvVar(GetLogFile(), PORT_c_str(LogFileName));
}
//----------------------------------------------------------------------------

bool DDP_DataExport::print_app_info()
{
    const DDP_Params * params = get_params();
    if(!params)
        return false;

    std::string val = params->GetText((const byte *)DDP_PARAM_ATTACHDESCR);
    if(val.length())
        {
        if(!MakeLogFileName())
            return false;

        if(!ParamFile.Print((const byte *)"APP_LogFileName = %s\r\n", PORT_c_str(LogFileName) ))
            return false;
        }
        
return DDP_Export::print_app_info();
}
//----------------------------------------------------------------------------

bool DDP_DataExport::Dump()
{
    bool res = DDP_Export::Dump();

    if(LogFileName.length())
        {
        DDP_PrintFile Log;
        if(!Log.Open(PORT_c_str(LogFileName)))
            return false;

        // print search description
        const DDP_Params * params = get_params();
        if(!params)
            return false;
        std::string val = params->GetText((const byte *)DDP_PARAM_ATTACHDESCR);

        if(val.length())
            {
            if(!Log.Print((const byte *)"[Search Description]\r\n"))
                return false;
            if(!Log.NPrint(val.length() + 1, PORT_cbyte("%s\r\n"), PORT_c_str(val)))
                return false;
            }

        Log.Close();
        }
    
return res;
}
//----------------------------------------------------------------------------

void DDP_BrowseExport::SetIndex(const AT_Index * index)
{
    Index = index;
    if(!Index)
        return ;
    DDP_Params * params = (DDP_Params *)get_params();
    uint cnt = params->GetInt((const byte *)"RecordCount");

    if(!cnt)
        // set term count
        params->SetParam((const byte *)"RecordCount", Index->getTermCount());
}
//----------------------------------------------------------------------------

/****************************************************************************
 NOTES
 ****************************************************************************/

