#!/usr/bin/python
import sys
import os
import popen2
import getopt
import tempfile

verbose = False

length_list = []

def process_focal_length(fl):
    length_list.append(fl)

def process_file(filename):
    ft = popen2.popen2("exif \"%s\" | grep \"^Focal Length        |\""
                       % filename)
    ft[1].close()
    s = ft[0].readline()
    fls = s[21:-1]
    if verbose:
        print ("%s: %s" % (filename, fls)).strip()
    pi = fls.find(".")
    fls = fls[:pi]
    try:
        fl = int(fls)
    except:
        sys.stderr.write("Could not extract focal length from file %s\n"
                         % filename)
        return
    process_focal_length(fl)


def process_directory(directory, mask):
    ft = popen2.popen2("find \"%s\" -name \"%s\" -print" % (directory, mask))
    ft[1].close()
    for filename in ft[0]:
        process_file(filename[:-1])
    ft[0].close()


# ============================== Actions ==============================

def guess_good_xtics(min, max):
    res =  ((((max - min) + 20) / 15) / 5) * 5
    return res

def draw_bar_graph(name, tmp_name, xtics):
    outf, inf = popen2.popen2("gnuplot")
    inf.write("""
 set terminal gif
 set style fill solid border -1
 set ylabel \"Number of uses\"
 set xtics %d
 set out \"%s.gif\"
 plot \"%s\" with boxes
    """ % (xtics, name, tmp_name))
    inf.close()
    outf.close()
    

def produce_all_bars(name):
    if verbose:
        print "Creating a bar graph of all focal lengths: ", name

    min = length_list[0]
    max = length_list[-1]

    h = {}
    for fl in length_list:
        if h.has_key(fl):
            h[fl] = h[fl] + 1
        else:
            h[fl] = 1

    fd, tmp_name = tempfile.mkstemp()
    f = os.fdopen(fd, 'w')
    for fl in range(min, max):
        if h.has_key(fl):
            num = h[fl]
        else:
            num = 0
        f.write("%i    %i\n" % (fl, num))
        
    f.close()
    draw_bar_graph(name, tmp_name, guess_good_xtics(min,max))
    

def produce_aggreg_bars(name, factor):
    if verbose:
        print ("Creating a bar graph while aggregating focal lengths by %d: %s"
               % (factor, name))

    min = length_list[0]
    max = length_list[-1]

    h = {}
    m1 = 0
    m2 = 65535
    for fl in length_list:
        fl = (fl / factor) * factor
        if h.has_key(fl):
            h[fl] = h[fl] + 1
        else:
            h[fl] = 1

    fl = (min / factor) * factor
    fd, tmp_name = tempfile.mkstemp()
    f = os.fdopen(fd, 'w')
    while fl < max + factor:
        if h.has_key(fl):
            num = h[fl]
        else:
            num = 0
        f.write("%i    %i\n" % (fl, num))
        fl = fl + factor

    f.close()
    draw_bar_graph(name, tmp_name, guess_good_xtics(min, max))

    
def produce_distribution(name):
    if verbose:
        "Creating a line graph of the distribution function"

    prev = 0
    count = 0

    min = length_list[0]
    max = length_list[-1]
    
    fd, tmp_name = tempfile.mkstemp()
    f = os.fdopen(fd, 'w')
    for fl in length_list:
        if fl != prev and prev > 0:
            f.write("%i    %i\n" % (prev, count))
        count = count + 1
        prev = fl
            
    f.write("%i    %i\n" % (prev, count))
    f.close()

    outf, inf = popen2.popen2("gnuplot")
    inf.write("""
set terminal gif
set ylabel \"Number of uses\"
set xtics %d
set out \"%s.gif\"
plot \"%s\" with boxes
    """ % (guess_good_xtics(min, max), name, tmp_name))
    inf.close()
    outf.close()
        


def print_usage():
    print """    count.py [-hvbf] [-m mask] [-n name] [-a factor] dir1 dir2...

count.py recursively traverses all directories that have been provided
on the command line looking for files with names that match the given
mask (default *.jpg).  The exif information of all such files is
examined for the used focal length.  All focal lengths are gathered
and various statistics are performed according to the given flags.

The  command line  parameters  can  be divided  into  four types:  the
directories, switches  that change the  way data is  gathered, various
action switches that  specify how the data is  processed and presented
and various other switches.

The other switches are:

  -h or --help

    Makes the script print this information and terminate.

  -v or --verbose

    Makes the script report verbose info about its current actions.


The switches affecting data gathering are:

  -m mask  or --mask-mask

    Specifies an alternative mask instead of the default *.jpg



The actions switches are:

  -b or --bars

    Produces a gnuplot bargraph showing how many times individual
    focal lengths were used.

  -a factor or --aggregate=factor

    Like the above but focal lengths are divided into groups of the
    given size and the frequencies (nuber of uses) are given for the
    whole group.

  -f or --five

    Like the above whith the value of factor equal to five.

  -d or --distribution

    Produces a gnuplot graph of the distribution function.
    


"""

def main():
    global verbose
    
    if (len(sys.argv) < 2):
        print_usage()
        sys.exit(1)

    try:
        opts, args = getopt.gnu_getopt(sys.argv[1:],
                                       "hm:n:vbfa:d",
                                       ["help", "verbose", "mask=", "name=",
                                        "bars", "five", "aggregate=",
                                        "distribution"])
    except getopt.GetoptError, err:
        # print help information and exit:
        sys.stderr.write(str(err))
        print_usage()
        sys.exit(1)

    mask = "*.jpg"
    name_base = "graph"
    verbose = False

    g_bars = False
    g_aggregate = 0
    g_five = False
    g_dist = False
    for o, a in opts:
        if o in ("-v", "--verbose"):
            verbose = True
        elif o in ("-h", "--help"):
            print_usage()
            sys.exit()
        elif o in ("-m", "--mask"):
            mask = a
        elif o in ("-n", "--name"):
            name_base = a

        elif o in ("-b", "--bars"):
            g_bars = True
        elif o in ("-f", "--five"):
            g_five = True
        elif o in ("-a", "--aggregate"):
            try:
                g_aggregate = int(a)
            except:
                sys.stderr.write("Error processing aggregate count\n")
                sys.exit(1)

        elif o in ("-d", "--distribution"):
            g_dist = True
        else:
            sys.stderr.write("Error: unhandled option\n")
            print_usage()
            sys.exit(1)

    if not g_bars and g_aggregate == 0 and not g_five and not g_dist:
        sys.stderr.write("Error: no action specified.\n")
        print_usage()
        sys.exit(1)

    for directory in args:
        process_directory(directory, mask)

    if len(length_list) == 0:
        sys.stderr.write("No focal lengths have been gathered\n")
        sys.exit(1)

    length_list.sort()

    if g_bars:
        produce_all_bars(name_base + "-bars")

    if g_five:
        produce_aggreg_bars(name_base + "-five", 5)

    if g_aggregate > 0:
        produce_aggreg_bars(("%s-aggr-%d" % (name_base, g_aggregate)),
                            g_aggregate)
        
    if g_dist:
        produce_distribution(name_base + "-distr")



        
    
main()

