#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Author:
#   Marc-Olivier Buob <marcolivier.buob@orange.fr>
#
# Usage:
#   ./gnuplot2_summaries.py gnuplot_dir
#
# Example:
#   ./gnuplot2_summaries.py ~/git/ibgp2/results/article/rr/gnuplot
#
# Outputs:
#   $gnuplot_dir/*summaries.{dat,gnu,eps}

from __future__     import print_function
from __future__     import with_statement # Required in 2.5

import os, subprocess, sys

#------------------------------------------------------------
# Constants
#------------------------------------------------------------

GNU_COMMON = """
# terminal
set term postscript color eps 18

# Palette (curves color)
set palette model HSV
set palette defined ( 0 0 1 1, 1 1 1 1 )
set palette defined ( 0 0 1 0, 1 0 1 1, 6 0.8333 1 1, 7 0.8333 0 1)
unset colorbox

# Caption
set key on inside bottom right
"""

GNU_SETTINGS = """
# Title
set title "%(title)s"

# X-axis
set xlabel "%(xlabel)s"
set xrange %(xrange)s
set xtics %(xtics)s
set xtics out

# Y-axis
set ylabel "%(ylabel)s"
set yrange %(yrange)s
set ytics %(ytics)s

# Curves
set border linewidth 1.5
set style line 1  lt 1 lw 2 pt 1 pi 0 ps 1
set pointintervalbox 0
set style data boxes
plot for [n=1:num_files] data_dir."%(prefix)s".n.".dat" using 2:1 with linespoints ls 1 lc palette frac n/(1.0+num_files) title "|S| = ".n
"""

SETTINGS = {
    "convergence_time_sec_" : {
        "prefix" : "convergence_time_sec_",
        "title"  : "iBGP convergence.",
        "xlabel" : "Convergence time (in s)",
        "xrange" : "[0:]",
        "xtics"  : 5,
        "ylabel" : "CDF(%routers)",
        "yrange" : "[0:]",
        "ytics"  : 10
    },
    "diversity_"            : {
        "prefix" : "diversity_",
        "title"  : "BGP route diversity.",
        "xlabel" : "#Routes",
        "xrange" : "[0:]",
        "xtics"  : 1,
        "ylabel" : "CDF(%routers)",
        "yrange" : "[0:]",
        "ytics"  : 10
    },
    "mem_usage_kib_"        : {
        "prefix" : "mem_usage_kib_",
        "title"  : "Memory usage.",
        "xlabel" : "|S|",
        "xrange" : "[0:]",
        "xtics"  : 1,
        "ylabel" : "RAM used (RIB+peers) (in kb)",
        "yrange" : "[0:]",
        "ytics"  : 10
    },
    "ram_all_kib_"        : {
        "prefix" : "ram_all_kib_",
        "title"  : "Memory usage.",
        "xlabel" : "|S|",
        "xrange" : "[0:]",
        "xtics"  : 1,
        "ylabel" : "RAM used (in kb)",
        "yrange" : "[0:]",
        "ytics"  : 10
    },
    "num_recv_message_"     : {
        "prefix" : "num_recv_message_",
        "title"  : "CPU usage.",
        "xlabel" : "#BGP updates received",
        "xrange" : "[0:]",
        "xtics"  : 1,
        "ylabel" : "CDF(\%routers)",
        "yrange" : "[0:]",
        "ytics"  : 10
    },
    "diversity_"            : {
        "prefix" : "diversity_",
        "title"  : "BGP route diversity.",
        "xlabel" : "#BGP diversity",
        "xrange" : "[0:]",
        "xtics"  : 1,
        "ylabel" : "CDF(\%routers)",
        "yrange" : "[0:]",
        "ytics"  : 10
    }
}

GNU_FS = """
# settings
data_dir="%(gnuplot_dir)s"
num_files=%(num_files)s
set out data_dir."%(filename_eps)s"
"""

INF_USAGE = """
usage: %(prog_name)s gnuplot_dir
    Args:
        gnuplot_dir: the directory containing the output generated by gnuplot1_mode.py.
    Example:
        %(prog_name)s ~/git/ibgp2/results/article/ibgp2/gnuplot
    Outputs:
        \$gnuplot_dir/convergence_time_sec_all.{gnu, eps}
        \$gnuplot_dir/diversity_all.{gnu, eps}
        \$gnuplot_dir/mem_usage_kib_all.{gnu, eps}
        \$gnuplot_dir/num_recv_message_all.{gnu, eps}
"""

SUFFIX_SUMMARY = "summary"

#------------------------------------------------------------
# Log
#------------------------------------------------------------

def info(*objs):
    """
    Print an info.
    Args:
        objs: The objects to print.
    """
    print("INFO:", *objs, file = sys.stdout)

def error(*objs):
    """
    Print an error.
    Args:
        objs: The objects to print.
    """
    print("ERROR:", *objs, file = sys.stderr)

#------------------------------------------------------------
# Gnuplot
#
# Each curves (eps file) is produced according to a
# (.dat, .gnu) pair of input files.
#
# The following function generates the .gnu files. This
# work is split in several print_* functions.
#------------------------------------------------------------

def print_common_def(ofs_gnu):
    """
    Print gnuplot settings shared by all the file written
    by this script.
    Args:
        ofs_gnu: Output stream to the gnuplot file.
    """
    print(GNU_COMMON, file = ofs_gnu)

def print_fs(ofs_gnu, gnuplot_dir, num_files, filename_eps):
    """
    Write in a gnuplot file the information related to the
    filesystem (location of the *.dat files containing the
    points, path of the *.eps file, etc.).
    Args:
        ofs_gnu: Output stream to the gnuplot file.
        gnuplot_dir: Directory containing the *.dat files.
        num_files: Number of files related to this curves.
        filename_eps: Path of the resulting eps file.
    """
    print(GNU_FS % {
        "gnuplot_dir"  : os.path.abspath(gnuplot_dir) + os.path.sep,
        "num_files"    : num_files,
        "filename_eps" : os.path.basename(filename_eps)
    }, file = ofs_gnu)

def print_settings(ofs_gnu, settings):
    """
    Prints gnuplot settings.
    Args:
        ofs_gnu: Output stream to the gnuplot file.
        settings: A dict instance. Example: setting["diversity_"]
    """
    print(GNU_SETTINGS % settings, file = ofs_gnu)

def make_gnu_all(ofs_gnu, gnuplot_dir, num_files, filename_eps, settings):
    """
    Write the gnuplot file related to a curve.
    Args:
        ofs_gnu: Output stream to the gnuplot file.
        gnuplot_dir: Directory containing the *.dat files.
        num_files: Number of files related to this curves.
        filename_eps: Path of the resulting eps file.
        settings: Settings related to this curve.
    """
    print_common_def(ofs_gnu)
    print_fs(ofs_gnu, gnuplot_dir, num_files, filename_eps)
    print_settings(ofs_gnu, settings)

def call_gnuplot(filename_gnu):
    """
    Process a gnu file with gnuplot.
    Args:
        filename_gnu: The absolute path of the gnuplot file.
    """
    with open(os.devnull, "w") as fnull:
        subprocess.Popen(["gnuplot", filename_gnu], stderr = fnull)

#------------------------------------------------------------
# Main program
#------------------------------------------------------------

def print_usage(output_file = sys.stderr):
    """
    Print the usage of this program.
    Args:
        output_file: The output file.
    """
    print(INF_USAGE % {"prog_name" : sys.argv[0]}, file = output_file)

def main():
    """
    Main program.
    """
    # Check arguments
    argc = len(sys.argv)
    if argc != 2:
        print_usage()
        return 1

    gnuplot_dir = sys.argv[1]
    for prefix in SETTINGS.keys():
        # DEBUG
        if prefix != "convergence_time_sec_":
            print("g3: DEBUG: skip")

        # Count number of file matches the current prefixes
        num_files = 0
        for filename in os.listdir(gnuplot_dir):
            basename = os.path.basename(filename)
            if basename.startswith(prefix) and basename.endswith(".dat") and not basename.endswith(SUFFIX_SUMMARY + ".dat"):
                num_files += 1

        filename_wext = os.path.abspath(os.path.join(gnuplot_dir, "%s%s" % (prefix, SUFFIX_SUMMARY)))
        filename_gnu = "%s.gnu" % filename_wext
        filename_eps = "%s.eps" % filename_wext

        if num_files > 0:
            with open(filename_gnu, "w") as ofs_gnu:
                info("Writting %s" % filename_gnu)
                make_gnu_all(ofs_gnu, gnuplot_dir, num_files, filename_eps, SETTINGS[prefix])
                info("Writting %s" % filename_eps)
                call_gnuplot(filename_gnu)
        else:
            error("No file found in [%s] matching [%s]" % (gnuplot_dir, prefix))
    return 0

if __name__ == '__main__':
    sys.exit(main())
