//# PlotLogger.h: Classes to perform various logging actions for the plotter.
//# Copyright (C) 2009
//# Associated Universities, Inc. Washington DC, USA.
//#
//# This library is free software; you can redistribute it and/or modify it
//# under the terms of the GNU Library General Public License as published by
//# the Free Software Foundation; either version 2 of the License, or (at your
//# option) any later version.
//#
//# This library is distributed in the hope that it will be useful, but WITHOUT
//# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
//# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
//# License for more details.
//#
//# You should have received a copy of the GNU Library General Public License
//# along with this library; if not, write to the Free Software Foundation,
//# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA.
//#
//# Correspondence concerning AIPS++ should be addressed as follows:
//#        Internet email: aips2-request@nrao.edu.
//#        Postal address: AIPS++ Project Office
//#                        National Radio Astronomy Observatory
//#                        520 Edgemont Road
//#                        Charlottesville, VA 22903-2475 USA
//#
//# $Id: $
#ifndef PLOTLOGGER_H_
#define PLOTLOGGER_H_

#include <graphics/GenericPlotter/PlotOptions.h>

#include <casacore/casa/Logging/LogSinkInterface.h>
#include <casacore/casa/Utilities/CountedPtr.h>

#include <map>
#include <time.h>
#include <vector>

namespace casa {

//# Forward declarations
class PlotCanvas;
class Plotter;


// Superclass for all messages going through the PlotLogger.  This class has
// been refactored to just be a thin layer on top of LogMessage.
class PlotLogMessage : public casacore::LogMessage {
public:
    // Static //
    
    // Default event type.
    static const int DEFAULT_EVENT_TYPE;
    
    
    // Non-Static //
    
    // Constructor which takes an optional priority.
    PlotLogMessage(int eventType = DEFAULT_EVENT_TYPE);
    
    // Constructor which takes the origin(s) and an optional priority.
    PlotLogMessage(const casacore::String& origin1, const casacore::String& origin2,
            int eventType = DEFAULT_EVENT_TYPE);
    
    // Constructor which takes the origin(s), the message, and an optional
    // priority.
    PlotLogMessage(const casacore::String& origin1, const casacore::String& origin2,
            const casacore::String& message, int eventType = DEFAULT_EVENT_TYPE);
    
    // Copy constructor.
    PlotLogMessage(const PlotLogMessage& copy);
    
    // Destructor.
    virtual ~PlotLogMessage();
    
    
    // Returns the event type of this message.
    virtual int eventType() const;
    
protected:
    // Event type, either a value from PlotLogger::Event, or a custom
    // user-defined value.
    int m_eventType;
};


// Used to report time and memory measurements.  This functionality can be
// accessed either directly with a PlotLogMeasurement object or indirectly
// through the PlotLogger class.  Message is:
// END.\tTime: [time] [timeUnits].  casacore::Memory: [memory] [memoryUnits].
// If the measurement has not been ended, calls stopMeasurement() first.
class PlotLogMeasurement : public PlotLogMessage {
public:
    // Static //
    
    // Available time units.  Currently only seconds because that's all that
    // C++ natively supports.
    enum TimeUnit {
        SECOND
    };
    
    // Available memory units.
    enum MemoryUnit {
        BYTE, KILOBYTE, MEGABYTE
    };
    
    // Default units.
    // <group>
    static const TimeUnit DEFAULT_TIME_UNIT;
    static const MemoryUnit DEFAULT_MEMORY_UNIT;
    // </group>
    
    // Get a string representation of the given time/memory unit.
    // <group>
    static casacore::String timeUnits(TimeUnit t);   
    static casacore::String memoryUnits(MemoryUnit m);
    // </group>
    
    
    // Non-Static //
    
    // Constructor which takes the origin(s), optional time and memory
    // units, and an optional priority.  Also calls startMeasurement().
    PlotLogMeasurement(const casacore::String& origin1, const casacore::String& origin2,
                       TimeUnit timeUnit = DEFAULT_TIME_UNIT,
                       MemoryUnit memoryUnit = DEFAULT_MEMORY_UNIT,
                       int eventType = DEFAULT_EVENT_TYPE);
    
    // Copy constructor.
    PlotLogMeasurement(const PlotLogMeasurement& copy);
    
    // Destructor.
    ~PlotLogMeasurement();
    
    
    // Returns the time/memory when the measurement started.
    // <group>
    time_t startTime() const;
    unsigned int startMemory() const;
    // </group>
    
    // Returns the time/memory difference between when the measurement started
    // and when the measurement ended.  Invalid if the measurement was never
    // started and ended.
    // <group>
    double time() const;    
    double memory() const;
    // </group>
    
    // Returns the time/memory units for this measurement.
    // <group>
    TimeUnit timeUnit() const;
    MemoryUnit memoryUnit() const;
    // </group>
   
    // Starts the measurement by setting the start time and memory.
    // Measurement automatically begins when the object is constructed, but
    // can be restarted as desired.
    void startMeasurement();
    
    // Calculates the measurements from the last starting point to this point,
    // and generates the log message.
    void stopMeasurement();
    
private:
    // Start time
    time_t m_startTime;
    
    // Start memory
    unsigned int m_startMemory;
    
    // casacore::Time and memory differences
    double m_time, m_memory;
    
    // casacore::Time unit
    TimeUnit m_timeUnit;
    
    // casacore::Memory unit
    MemoryUnit m_memoryUnit;
};


// Used to report located indices.  Basically just a container for the results
// of a PlotCanvas::locate.
class PlotLogLocate : public PlotLogMessage {
public:
    // Static //
    
    // Convenience access to PlotCanvas::locate() to return a PlotLogLocate
    // message.
    static PlotLogLocate canvasLocate(PlotCanvas* canvas,
            const PlotRegion& region);
    
    
    // Non-Static //
    
    // Constructor which takes the two origins, the region that was located,
    // and the located indices (see PlotCanvas::locate()).  If
    // deleteIndicesOnDestruction is true, the given indices vector will be
    // deleted when this message is destroyed.  This should be used with care
    // if multiple PlotLogLocates using the same located indices are being
    // used.
    PlotLogLocate(const casacore::String& origin1, const casacore::String& origin2,
            const PlotRegion& locateRegion,
            std::vector<std::vector<std::pair<unsigned int,unsigned int> > >* locatedIndices,
            int eventType = DEFAULT_EVENT_TYPE,
            bool deleteIndicesOnDestruction = true);
    
    // Copy constructor.  NOTE: will set the deleteIndicesOnDestruction flag on
    // the original to false so that the indices won't be deleted from out
    // under the copy.
    PlotLogLocate(const PlotLogLocate& copy);
    
    // Destructor.
    ~PlotLogLocate();
    
    
    // Returns the region that was located.
    const PlotRegion& locateRegion() const;
    
    // Returns the total number of points that were located.
    unsigned int numLocatedIndices() const;
    
    // Returns the number of plots that were searched.
    unsigned int numSearchedPlots() const;
    
    // Returns the located indices.
    std::vector<std::vector<std::pair<unsigned int, unsigned int> > >* indices() const;
    
    // Returns the located indices for the plot at the given index, or NULL for
    // invalid.
    std::vector<std::pair<unsigned int, unsigned int> >* plotIndices(
            unsigned int index) const;
    
    // Returns whether or not this message will delete the indices vector on
    // destruction or not.
    bool willDeleteIndices() const;
    
private:
    // Region.
    PlotRegion m_region;
    
    // Indices.
    std::vector<std::vector<std::pair<unsigned int, unsigned int> > >* m_indices;
    
    // Should delete indices.
    bool m_shouldDelete;
};


// Subclass of PlotLogMessage to unify messages for method entering/exiting.
class PlotLogMethod : public PlotLogMessage {
public:
    // Constructor which takes the class and method names, a flag for whether
    // the method is entering or exiting, and an optional additional message
    // and priority.
    PlotLogMethod(const casacore::String& className, const casacore::String& methodName,
            bool entering, const casacore::String& message = casacore::String(),
            int eventType = DEFAULT_EVENT_TYPE);
    
    // Destructor.
    ~PlotLogMethod();
};


// Subclass of PlotLogMessage to unify messages for object creation/deletion.
class PlotLogObject : public PlotLogMessage {
public:
    // Constructor which takes the class name and object address, a flag for
    // whether the object is being created or destroyed, and an optional
    // additional message and priority.
    PlotLogObject(const casacore::String& className, void* address, bool creation,
            const casacore::String& message = casacore::String(),
            int eventType = DEFAULT_EVENT_TYPE);
    
    // Destructor.
    ~PlotLogObject();
};


// Subclass of casacore::LogFilterInterface to filter on both event flags and minimum
// priority.
class PlotLoggerFilter : public casacore::LogFilterInterface {
public:
    // Constructor which takes optional event flags and minimum priority.
    PlotLoggerFilter(int eventFlags, casacore::LogMessage::Priority minPriority);
    
    // Destructor.
    ~PlotLoggerFilter();
    
    // Implements casacore::LogFilterInterface::clone().
    casacore::LogFilterInterface* clone() const;
    
    // Implements casacore::LogFilterInterface::pass().
    casacore::Bool pass(const casacore::LogMessage& message) const;
    
    // Gets/Sets the event flags.
    // <group>
    int eventFlags() const;
    void setEventFlags(int flags);
    // </group>
    
    // Gets/Sets the minimum priority.
    // <group>
    casacore::LogMessage::Priority minimumPriority() const;
    void setMinimumPriority(casacore::LogMessage::Priority minPriority);
    // </group>
    
private:
    // Event flags.
    int m_eventFlags;
    
    // Minimum priority.
    casacore::LogMessage::Priority m_minPriority;
};


// A PlotLogger is used to log messages to an underlying CASA log object, as
// well as provide access to different logging functionality like measurements.
// PlotLogger is associated with a single Plotter object and should be used by
// all children of that Plotter (canvases, plot items, etc.) to report their
// behavior if the proper flag is turned on.  The logger can also filter out
// messages by priority.  Therefore a message must BOTH have its event type
// flag turned on, AND meet the priority minimum in order to be posted to the
// log.  The exception to this is for the MSG_* event types, which are logged
// as long as they meet the filtered priority requirement.  The actual log can
// either be the global sink, or can be set to a file.
class PlotLogger {
public:
    // Static //
    
    // Event types //
    
    // Event types that are always allowed.
    // <group>
    static const int MSG_INFO  = -1;
    static const int MSG_WARN  = -2;
    static const int MSG_ERROR = -3;
    // </group>
    
    // Miscellaneous debugging messages.
    static const int MSG_DEBUG       = 1;
    
    // Replotting/redrawing the whole GUI.
    static const int DRAW_TOTAL      = 2;
    
    // Replotting/redrawing each plot item.
    static const int DRAW_INDIVIDUAL = 4;
    
    // Entering/exiting major methods.
    static const int METHODS_MAJOR   = 8;
    
    // Creation/deletion of major objects.
    static const int OBJECTS_MAJOR   = 16;
    
    // Exporting canvases to file.
    static const int EXPORT_TOTAL    = 32;
    
    
    // No events.
    static const int NO_EVENTS = 0;
    
    // All events as a flag.
    static int ALL_EVENTS_FLAG();
    
    // All events as a vector.
    static std::vector<int> ALL_EVENTS();
    
    
    // Registers an extended event type with the given name and optional
    // priority and returns its value.
    static int REGISTER_EVENT_TYPE(const casacore::String& name,
            casacore::LogMessage::Priority priority = casacore::LogMessage::NORMAL);
    
    // Unregisters the given extended event type.  If a priority has been set,
    // it is NOT removed.
    // <group>
    static void UNREGISTER_EVENT_TYPE(int event);
    static void UNREGISTER_EVENT_TYPE(const casacore::String& name);
    // </group>
    
    // Returns all the event names.
    static std::vector<casacore::String> EVENT_NAMES();
    
    // Converts between an event type and its name.
    // <group>
    static casacore::String EVENT(int type);
    static int EVENT(const casacore::String& name);
    // </group>
    
    // Returns an event flag from the given vector.
    // <group>
    static int FLAG_FROM_EVENTS(const std::vector<int>& events);
    static int FLAG_FROM_EVENTS(const std::vector<casacore::String>& names);
    // </group>
    
    // Returns an event flag for all events that meet the given minimum
    // priority.
    static int FLAG_FROM_PRIORITY(casacore::LogMessage::Priority minPriority);
    
    
    // Gets/Sets the message priority for the given log event.  Uses a default
    // if the event has not been set.
    // <group>
    static casacore::LogMessage::Priority EVENT_PRIORITY(int event);
    static void SET_EVENT_PRIORITY(int event, casacore::LogMessage::Priority priority);
    // </group>
    
    
    // Disables the global sink until enableGlobalSink() is called.  Can be
    // used when something posts undesirably to both the local and global logs.
    static void disableGlobalSink();
    
    // Re-enables the global sink.  See disableGlobalSink().
    static void enableGlobalSink();
    
    
    // Non-Static //
    
    // Constructor which takes the Plotter this logger is associated with.  The
    // global log sink is used, and the minimum priority filter is set.
    PlotLogger(Plotter* plotter, int filterEventFlags = NO_EVENTS,
            casacore::LogMessage::Priority filterMinPriority = casacore::LogMessage::DEBUGGING);
    
    // Destructor.
    virtual ~PlotLogger();
    
    
    // Log IO Methods //
    
    // Gets the log sink interface.
    // <group>
    casacore::CountedPtr<casacore::LogSinkInterface> sink();
    const casacore::CountedPtr<casacore::LogSinkInterface> sink() const;
    // </group>
    
    // Gets a copy of the log sink interface, IF it is not the global.
    casacore::LogSinkInterface* localSinkCopy() const;
    
    // Gets/Sets the log sink file location.  If the filename is empty, it
    // means the global sink.
    // <group>
    const casacore::String& sinkLocation() const;
    void setSinkLocation(const casacore::String& logFile);
    // </group>
    
    // Returns true if the logger is currently using the global sink, false
    // otherwise.
    bool usingGlobalSink() const { return sinkLocation().empty(); }
    
    
    // Filtering Methods //
    
    // Gets/Sets the log filter priority level.
    // <group>
    casacore::LogMessage::Priority filterMinPriority() const;
    void setFilterMinPriority(PlotLogMessage::Priority minPriority);
    // </group>
    
    // Gets/Sets the log filter event flag for a single event type.
    // <group>
    bool filterEventFlag(int flag) const;
    void setFilterEventFlag(int flag, bool on);
    // </group>
    
    // Gets/Sets the log filter event flag(s).  The flag should be a bitwise-or
    // of one or more values in PlotLogger events and any extended event types.
    // <group>
    int filterEventFlags() const;
    void setFilterEventFlags(int flags);
    // <group>
    
    
    // Message Methods //
    
    // Posts the given message to the underlying log sink.
    // <group>
    void postMessage(const PlotLogMessage& message);
    void postMessage(const casacore::String& origin1, const casacore::String& origin2,
                     const casacore::String& message,
                     int eventType = PlotLogMessage::DEFAULT_EVENT_TYPE);
    // </group>
    
    
    // Measurement Methods //
    
    // Marks the logger to begin a time/memory measurement.  Measurement marks
    // can be recursive.  Returns a generic message saying that measurement has
    // begun, which will be also posted to the log if postStartMessage is true.
    PlotLogMessage markMeasurement(const casacore::String& origin1,const casacore::String& origin2,
            int eventType = PlotLogMessage::DEFAULT_EVENT_TYPE,
            bool postStartMessage = true);
    
    // Gets the measurement since the last mark.  The message will also be
    // posted to the log if postReleaseMessage is true.
    PlotLogMeasurement releaseMeasurement(bool postReleaseMessage = true);
    
    
    // Locate Methods //
    
    // Calls locate on the given canvas and returns the result as a message
    // that will also be posted if postLocateMessage is true.
    PlotLogLocate locate(PlotCanvas* canvas, const PlotRegion& region,
            int eventType = MSG_INFO, bool postLocateMessage = true);
    
private:
    // Log sink.
    casacore::CountedPtr<casacore::LogSinkInterface> m_logger;
    
    // Log filter.
    PlotLoggerFilter m_filter;
    
    // Log sink location.
    casacore::String m_loggerLocation;
    
    // Current measurement marks.
    std::vector<PlotLogMeasurement> m_measurements;

    
    // Static //
    
    // Registered extended types.
    static std::vector<int> EXTENDED_TYPES;
    
    // Registered extended type names.
    static std::vector<casacore::String> EXTENDED_NAMES;
    
    // casacore::Map from log event to priority.
    static std::map<int, casacore::LogMessage::Priority> EVENT_PRIORITIES;
    
    // Disabled old global filter, or null.
    static casacore::LogFilterInterface* DISABLED_GLOBAL_FILTER;
};
typedef casacore::CountedPtr<PlotLogger> PlotLoggerPtr;

}

#endif /* PLOTLOGGER_H_ */