//# Plotter.cc: Highest level plotting object that holds one or more canvases.
//# 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: $
#include <graphics/GenericPlotter/Plotter.h>

#include <casacore/casa/OS/Time.h>

#include <ctype.h>
#include <iomanip>
#include <stdint.h>

using namespace std;

using namespace casacore;
namespace casa {

/////////////////////////
// PLOTTER DEFINITIONS //
/////////////////////////

// Static //

const String Plotter::DEFAULT_DATE_FORMAT = "%y/%m/%d\n%h:%n:%s";
const String Plotter::DEFAULT_RELATIVE_DATE_FORMAT = "%h:%n:%s";

String Plotter::formattedDateString(const String& format, double value,
            PlotAxisScale scale, bool isRelative, int secPrecision) {
    // Values to be used in formatted String.
    Time t;
    int64_t hours = 0;
    uint64_t minutes = 0;
    double seconds = 0;
    stringstream ss;
    
    // Calculate relative values, if needed.
    if(isRelative) {
        // Put - in front for negative value.
        if(scale == DATE_MJ_DAY) {
            double temp = value * 24;
            hours = int64_t(temp);
            temp -= hours;
            temp *= 60;
            minutes = uint64_t(std::abs(temp));
            temp *= 60;
            seconds = std::abs(temp);
        } else if(scale == DATE_MJ_SEC) {
            hours = (int64_t)(value / 3600);
            double temp = value - (hours * 3600);
            minutes = (uint64_t)(std::abs(temp / 60));
            temp = std::abs(temp) - minutes * 60;
            seconds = temp;
        }
        
    // Calculate absolute values, if needed.
    } else {
        if(scale == DATE_MJ_SEC) {
            t = Time((value / 86400) + 2400000.5);
            seconds = modf(value, &seconds);
        } else if(scale == DATE_MJ_DAY) {
            t = Time(value + 2400000.5);
            seconds = modf(value * 86400, &seconds);
        }
    }
    
    int defPrec = ss.precision(); // change and restore for seconds
    char c;
    for(unsigned int i = 0; i < format.length(); i++) {
        c = format[i];
        if(c == '%' && i < format.length() - 1) {
            c = format[++i];
            switch(c) {
            case 'y': case 'Y':
                if(!isRelative) ss << t.year();
                break;
                
            case 'm': case 'M':
                if(!isRelative) {
                    if(t.month() < 10) ss << '0';
                    ss << t.month();
                }
                break;
                
            case 'd': case 'D':
                if(!isRelative) {
                    if(t.dayOfMonth() < 10) ss << '0';
                    ss << t.dayOfMonth();
                }
                break;
                
            case 'h': case 'H':
                if(!isRelative) {
                    if(t.hours() < 10) ss << '0';
                    ss << t.hours();
                } else {
                    if (hours < 10) ss << '0';
                    ss << hours;
                }
                break;
                
            case 'n': case 'N':
                if(!isRelative) {
                    if(t.minutes() < 10) ss << '0';
                    ss << t.minutes();
                } else {
                    if(minutes < 10) ss << '0';
                    ss << minutes;
                }
                break;
                
            case 's': case 'S':
				if (secPrecision >=0)
					ss.precision(secPrecision);
				else
					ss.precision(4);
                if(!isRelative) {
                    if(t.seconds() < 10) ss << '0';
                    ss << fixed << (t.seconds() + seconds);
                } else {
                    if(seconds < 10) ss << '0';
                    ss << fixed << seconds;
                }
                ss.precision(defPrec);
                break;
                
            case 'p': case 'P': {
                if(i >= format.length() - 1 || (!isdigit(format[i + 1]) &&
                   format[i + 1] != '-' && format[i + 1] != '+')) break;

                i++;
                unsigned int j = i + 1;
                for(; j < format.length() && isdigit(format[j]); j++);
                    
                String sprec = format.substr(i, j - i);
                int prec;
                sscanf(sprec.c_str(), "%d", &prec);
                if(prec < 0) prec = defPrec;
                ss.precision(prec);
                
                if(prec >= 0) ss << setprecision(prec);
                break; }
                
            default: ss << format[i - 1] << c; break;
            }
        } else ss << c;
    }

    return ss.str();
}


// Non-Static //

Plotter::Plotter() {
	commonAxisX = false;
	commonAxisY = false;
}

Plotter::~Plotter() { }


PlotCanvasPtr Plotter::canvasAt(const PlotLayoutCoordinate& coord) {
    PlotCanvasLayoutPtr layout = canvasLayout();
    if(!layout.null()) return layout->canvasAt(coord);
    else               return PlotCanvasPtr();
}

PlotCanvasPtr Plotter::canvas() {
    PlotCanvasLayoutPtr layout = canvasLayout();
    if(!layout.null()) return layout->canvas();
    else               return PlotCanvasPtr();
}

void Plotter::setCanvasCachedAxesStackImageSize( int width, int height ){
	PlotCanvasLayoutPtr layout = canvasLayout();
	if ( !layout.null() ) {
		vector<PlotCanvasPtr> canv = layout->allCanvases();
	    for(unsigned int i = 0; i < canv.size(); i++){
	    	if(!canv[i].null()){
	    		canv[i]->setCachedAxesStackImageSize(width,height);
	    	}
	    }
	}
}

void Plotter::setCanvas(PlotCanvasPtr canvas) {
    if(!canvas.null())
        setCanvasLayout(PlotCanvasLayoutPtr(new PlotLayoutSingle(canvas)));
}

void Plotter::setCommonAxisX(Bool commonAxis ){
	commonAxisX = commonAxis;
}

void Plotter::setCommonAxisY(Bool commonAxis ){
	commonAxisY = commonAxis;
}


bool Plotter::isCommonAxisX() const {
	return commonAxisX;
}

bool Plotter::isCommonAxisY() const {
	return commonAxisY;
}

void Plotter::setAxisLocation( PlotAxis xLocation, PlotAxis yLocation ){
	axisLocationX = xLocation;
	axisLocationY = yLocation;
}

PlotAxis Plotter::getAxisLocationX() const {
	return axisLocationX;
}

PlotAxis Plotter::getAxisLocationY() const {
	return axisLocationY;
}

LogMessage::Priority Plotter::logFilterMinPriority() const {
    return logger()->filterMinPriority(); }
void Plotter::setLogFilterMinPriority(PlotLogMessage::Priority minPriority) {
    logger()->setFilterMinPriority(minPriority); }

bool Plotter::logFilterEventFlag(int flag) const {
    return logger()->filterEventFlag(flag); }
void Plotter::setLogFilterEventFlag(int flag, bool on) {
    logger()->setFilterEventFlag(flag, on); }

int Plotter::logFilterEventFlags() const{ return logger()->filterEventFlags();}
void Plotter::setLogFilterEventFlags(int flags) {
    logger()->setFilterEventFlags(flags); }

PlotLoggerPtr Plotter::logger() const {
    if(m_logger.null())
        const_cast<PlotLoggerPtr&>(m_logger) = new PlotLogger(
                const_cast<Plotter*>(this));
    return m_logger;
}

bool Plotter::isVisible(PlotCanvasPtr& canvas ){
	vector<PlotCanvasPtr> currentPlots = canvasLayout()->allCanvases();
	int plotCount = currentPlots.size();
	bool visible = false;
	for ( int i = 0; i < plotCount; i++  ){
		if ( currentPlots[i] == canvas ){
			visible = true;
			break;
		}
	}
	return visible;
}

}