/* ======================================================================== *\
!
! *
! * 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): Thomas Bretz  12/2000 <mailto:tbretz@uni-sw.gwdg.de>
!
!   Copyright: MAGIC Software Development, 2000-2001
!
!
\* ======================================================================== */


//////////////////////////////////////////////////////////////////////////////
//                                                                          //
// MLog                                                                     //
//                                                                          //
// This is what we call the logging-system.                                 //
//                                                                          //
// It is derived from the C++ streaming classes and can handle our          //
// logging. The log output can be redirected to stdout, stderr, any other   //
// stream or a root window.                                                 //
//                                                                          //
// There is a global log-instance which you can use like cout, id is gLog.  //
// A log-instance of your choice (gLog by default) is destributed to all    //
// Task which are used in an eventloop, so that you can redirect the output //
// of one eventloop to where you want..                                     //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////

#include "MLog.h"

#include <stdlib.h>     // mkstempe
#include <fstream.h>
#include <TGListBox.h>

#include "MLogManip.h"

ClassImp(MLog);

//
// This is the definition of the global log facility
//
MLog gLog;

// --------------------------------------------------------------------------
//
// this strange usage of an unbufferd buffer is a workaround
// to make it work on Alpha and Linux!
//
void MLog::Init()
{
    setp(&fBuffer, &fBuffer+1);
    *this << '\0';
}

// --------------------------------------------------------------------------
//
// default constructor which initializes the streamer and sets the device
// which is used for the output (i)
//
MLog::MLog(int i) : ostream(this), fPPtr(fBase), fEPtr(fBase+bsz), fOutputLevel(0), fDebugLevel((unsigned)-1), fDevice(i), fGuiLineId(0), fout(NULL), fOutAllocated(kFALSE), fgui(NULL)
{
    Init();
}

// --------------------------------------------------------------------------
//
// default constructor which initializes the streamer and sets the given
// ofstream as the default output device
//
MLog::MLog(ofstream &out) : ostream(this), fPPtr(fBase), fEPtr(fBase+bsz), fOutputLevel(0), fDebugLevel((unsigned)-1), fDevice(eFile), fGuiLineId(0),  fout(&out), fOutAllocated(kFALSE), fgui(NULL)
{
    Init();
}

// --------------------------------------------------------------------------
//
// default constructor which initializes the streamer and sets the given
// TGListBox as the default output device
//
MLog::MLog(TGListBox &out) : ostream(this), fPPtr(fBase), fEPtr(fBase+bsz), fOutputLevel(0), fDebugLevel((unsigned)-1), fDevice(eGui), fGuiLineId(0),  fout(NULL), fOutAllocated(kFALSE), fgui(&out)
{
    Init();
}

// --------------------------------------------------------------------------
//
// default constructor which initializes the streamer and opens a file with
// the given name. Dependend on the flag the file is set as output device
// or not.
//
MLog::MLog(const char *fname, int flag) : ostream(this), fPPtr(fBase), fEPtr(fBase+bsz), fOutputLevel(0), fDebugLevel((unsigned)-1), fDevice(eFile), fGuiLineId(0),  fgui(NULL)
{
    Init();

    AllocateFile(fname);
    CheckFlag(eFile, flag);
}

// --------------------------------------------------------------------------
//
// copyt constructor
//
MLog::MLog(MLog &log)
{
    fOutputLevel  = log.fOutputLevel;
    fDebugLevel   = log.fDebugLevel;
    fDevice       = log.fDevice;
}

// --------------------------------------------------------------------------
//
// This is the function which writes the stream physically to a device.
// If you want to add a new device this must be done here.
//
void MLog::WriteBuffer()
{
    const int len = fPPtr - fBase;

    if (fDevice&eStdout)
        cout.write(fBase, len);

    if (fDevice&eStderr)
        cerr.write(fBase, len);

    if (fDevice&eFile && fout)
        fout->write(fBase, len);

    if (fDevice&eGui && fgui)
    {
        char *dummy = new char[len+1];
        memcpy(dummy, fBase, len);
        *(dummy+len)='\0';
        fgui->AddEntry(dummy, fGuiLineId);
        fgui->SetTopEntry(fGuiLineId++);
        fgui->SetBit(kHasChanged);
        delete dummy;
    }

    //
    // restart writing to the buffer at its first char
    //
    fPPtr = fBase;
}

// --------------------------------------------------------------------------
//
// This is called to flush the buffer of the streaming devices
//
int MLog::sync()
{
    WriteBuffer();

    if (fDevice&eStdout)
        cout.flush();

    if (fDevice&eStderr)
        cerr.flush();

    if (fDevice&eFile && fout)
        fout->flush();

    return 0;
}

// --------------------------------------------------------------------------
//
// This function comes from streambuf and should
// output the buffer to the device (flush, endl)
// or handle a buffer overflow (too many chars)
// If a real overflow happens i contains the next
// chars which doesn't fit into the buffer anymore.
// If the buffer is not really filled i is EOF(-1).
//
int MLog::overflow(int i) // i=EOF means not a real overflow
{
    //
    // no output if
    //
    if (fOutputLevel > fDebugLevel)
        return 0;

    *fPPtr++ = (char)i;

    if (fPPtr == fEPtr)
        WriteBuffer();

    return 0;
}

// --------------------------------------------------------------------------
//
// Create a new instance of an file output stream
// an set the corresponding flag
//
void MLog::AllocateFile(const char *fname)
{
    fout = fname ? new ofstream(fname) : new ofstream(mkstemp("logXXXXXX"));
    fOutAllocated = kTRUE;
}

// --------------------------------------------------------------------------
//
// if fout was allocated by this instance of MLooging
// delete it.
//
void MLog::DeallocateFile()
{
    if (fOutAllocated)
        delete fout;
}

// --------------------------------------------------------------------------
//
// if necessary delete the old in stance of the file
// output stream and create a new one
//
void MLog::ReallocateFile(const char *fname)
{
    DeallocateFile();
    AllocateFile(fname);
}

// --------------------------------------------------------------------------
//
// This function checks if a device should get enabled or disabled.
//
void MLog::CheckFlag(Flags_t chk, int flag)
{
    if (flag==-1)
        return;

    flag ? EnableOutputDevice(chk) : DisableOutputDevice(chk);
}
