/* ======================================================================== *\
!
! *
! * This file is part of MARS, the MAGIC Analysis and Reconstruction
! * Software. It is distributed to you in the hope that it can be a useful
! * and timesaving tool in analysing Data of imaging Cerenkov telescopes.
! * It is distributed WITHOUT ANY WARRANTY.
! *
! * Permission to use, copy, modify and distribute this software and its
! * documentation for any purpose is hereby granted without fee,
! * provided that the above copyright notice appear in all copies and
! * that both that copyright notice and this permission notice appear
! * in supporting documentation. It is provided "as is" without express
! * or implied warranty.
! *
!
!
!   Author(s): Daniela Dorner, 01/2005 <mailto:dorner@astro.uni-wuerzburg.de>
!
!   Copyright: MAGIC Software Development, 2000-2006
!
!
\* ======================================================================== */

/////////////////////////////////////////////////////////////////////////////
//
// setstatus.C
// ===========
//
// This macro is a key part of the automation concept.
// It sets the status for dates/runs/sequences/datasets in the database, if a
// certain step has been done.
//
// Usage:
//  .x setstatus.C+("primary","table","column","statustime","returncode","failedcode","failedcodeadd","starttime","failedtime",kTRUE)
// The first argument is the primary (date/run/sequence/dataset), the second
// and third argument give the table and column of the step. The fourth
// column is giving the statustime, to which the column has to be set. The
// fifth argument is giving the error code (in case of failure), the sixth
// an error comment, the seventh the starttime and the eigth the stoptime
// (only in case of failure).
// The last argument is indicating if the columns of all influenced steps
// shall be reset to NULL. The default is kTRUE, which means, that the
// columns are reset.
//
// Make sure, that database and password are corretly set in a resource
// file called sql.rc and the resource file is found.
//
// Returns 0 in case of failure and 1 in case of success.
//
/////////////////////////////////////////////////////////////////////////////

#include <iostream>
#include <iomanip>
#include <fstream>

#include <TEnv.h>

#include <MTime.h>

#include <MSQLServer.h>
#include <TSQLRow.h>
#include <TSQLResult.h>

using namespace std;


Bool_t CheckReset(TEnv &rc, TString table, TString column, TString statustime)
{
    if (statustime!="NULL")
    {
        cout << "everything ok - no reset needed" << endl;
        return kTRUE;
    }

    //some columns can't be reseted, this is given in steps.rc
    TString reset=rc.GetValue(table+"."+column+".Reset", "no");
    if (reset.Contains("no"))
    {
        cout << "you can't reset " << column << "." << endl;
        return kFALSE;
    }

    cout << "reset is possible" << endl;
    return kTRUE;
}

TString CheckDefault(MSQLServer &serv, TEnv &rc, TString primary, TString table, TString influence)
{
    //check if the value in the database is the default
    TString query(Form("SELECT %s FROM %s WHERE %s=%s",
                       influence.Data(), table.Data(),
                       rc.GetValue(table+".Primary", ""), primary.Data()));

    TSQLResult *res = serv.Query(query);
    if (!res)
        cout << "Error - no run to check" << endl;

    MTime t, t0;
    //set time t to 'no-version-value',
    //which is in this case equivalent to NULL
    t.Set(1970,1,1,0,0,0); 
    t0.Set(1970,1,1,0,0,0);

    TSQLRow *row=0;
    while ((row = res->Next()))
    {
        if (row && (*row)[0])
            t.SetSqlDateTime((*row)[0]);

        cout << "t : " << t  << endl;
        cout << "t0: " << t0 << endl;

        return t()-t0()>0 ? "no" : "yes";
    }
}

Int_t SetInfluences(MSQLServer &serv, TEnv &rc, TString primary, TString table, TString column, TString statustime, TString returncode, TString failedcode, TString failedcodeadd, TString starttime, TString failedtime, Bool_t resetall)
{
    cout << "set influences for " << table << "." << column << endl;

    //build query and set step and influences for the step
    TString influences  = rc.GetValue(table+"."+column+".Influences", "");

    TString query(Form("UPDATE %s SET %s=%s, fReturnCode=%s, fFailedCode=%s, fFailedCodeAdd=%s, fFailedTime=%s ",
                       table.Data(), column.Data(), statustime.Data(), returncode.Data(), failedcode.Data(),
                       failedcodeadd.Data(), failedtime.Data()));
    if (starttime.CompareTo("noreset"))
        query += Form(", fStartTime=%s", starttime.Data());

    while (!influences.IsNull() && resetall)
    {
        influences = influences.Strip(TString::kBoth);

        Int_t idx = influences.First(' ');
        if (idx<0)
            idx = influences.Length();

        TString influence = influences(0, idx);
        influences.Remove(0, idx);

        TString deflt  = rc.GetValue(influence+".Default", "");

        //for some columns there may be a different default
        // in the file steps.rc they are marked with Default: check
        // in this case, the value in the database has to be checked
        if (deflt=="check")
            deflt=CheckDefault(serv, rc, primary, table, influence);
        //if it is the default value (1970-01-01 00:00:00), nothing is inserted into the database
        if (deflt=="yes")
            continue;

        query+=Form(", %s=NULL", influence.Data());
    }

    query+=Form(" WHERE %s='%s'", rc.GetValue(table+".Primary", ""), primary.Data());

    TSQLResult *res = serv.Query(query);
    if (!res)
        return 0;

    return 1;
}

int setstatus(TString primary, TString table, TString column, TString statustime, TString returncode, TString failedcode, TString failedcodeadd, TString starttime, TString failedtime, Bool_t resetall=kTRUE)
{
    TEnv env("sql.rc");

    MSQLServer serv(env);
    if (!serv.IsConnected())
    {
        cout << "ERROR - Connection to database failed." << endl;
        return 0;
    }
    cout << "setstatus" << endl;
    cout << "---------" << endl;
    cout << endl;
    cout << "Connected to " << serv.GetName() << endl;
    cout << endl;

    TEnv rc("steps.rc");

    //if reset is needed and/or can be done, set new values
    return !CheckReset(rc, table, column, statustime) ? 0 : SetInfluences(serv, rc, primary, table, column, statustime, returncode, failedcode, failedcodeadd, starttime, failedtime, resetall);

}

