#main_gallery.py
"""Make a static web picture gallery with comments and links.
This module contains the main gallery transformation code.
"""

import os
import os.path
import shutil
import sys
import fnmatch

from gallery import *
import gallery_html

def make_gallery(src_dirs, p):
    """Create a picture gallery.
    Returns final gallery directory string

    [string] src_dirs: list of all pic dir parameters
    Gal_param p (see gallery_param)
    """

    # First set parameters and do legality checks --
    #   delay changes to file system until obvious error conditions checked

    # contrast these names, all absolute paths:
    #   new_gal_dir:   final name of destination gallery directory
    #   orig_src_dir:  initial location of first source dir
    #   new_src_dir:  orig_src_dir's final name (possibly renamed = new_gal_dir)
    # This complication comes from the fact that the source directory may or
    # may not be renamed, and as many checks as possible are done before any
    # file system changes are made.

    # Make sure source directories exist
    if not src_dirs:
        raise Exception, "No directory to process."
    src_dirs =[os.path.abspath(d) for d in src_dirs]
    for d in src_dirs:
        if not os.path.isdir(d):
            raise Exception,  "Not an existing directory: %s" % d
    orig_src_dir = src_dirs[0]

    remove_file_matches(src_dirs)
    galleries =[Gallery(d) for d in src_dirs]

    main_gal = galleries[0] # merge into this one
    
    # read or create pic docs
    for g in galleries:
        g.get_docs()

    # find actual conversion sizes desired
    if p.thumb_option:
        p.thumb_dim = main_gal.find_thumb_dim(p.thumb_dim)
    if p.medium_option:
        p.medium_dim = main_gal.find_medium_dim(p.medium_dim)

    # test for existence of source pic files 
    check_for_src_files(galleries, p)
    
    # Make sure there is a non-empty gallery title
    old_gallery_title = main_gal.title
    if p.gallery_title:
        main_gal.title = p.gallery_title
    elif not main_gal.title:
        main_gal.title = os.path.basename(orig_src_dir)

    # plan final pic dir, note new_src_dir -- the possibly renamed orig dir
    new_gal_dir=make_file_name(main_gal.title, p.remove_blanks)
    if p.new_gal_under:
        new_gal_dir = os.path.abspath(os.path.join(p.new_gal_under, new_gal_dir))
        new_src_dir = main_gal.gal_dir
    else:
        new_gal_dir = os.path.abspath(os.path.join(
                    os.path.dirname(main_gal.gal_dir), new_gal_dir ))
        new_src_dir = new_gal_dir

    # merge dirs may not match orig dest or renamed dest pic dir
    for i,g in enumerate(galleries):
        if i > 0 and (fnmatch.fnmatch(new_gal_dir, g.gal_dir) or
                      fnmatch.fnmatch(orig_src_dir, g.gal_dir)):
            raise Exception,  "The output directory may not be a merged directory.\n"+\
              "The output directory may be the first named source directory." 
            
    # Plan (but do not change in file system)
    # new name fields in docs: setting the name field
    plan_unique_names(galleries, p.use_titles, p.remove_blanks)

    # check if any names of pics change
    change_names = main_gal.check_names_change()
    doing_rotations = main_gal.check_rotations()

    # check for legal option combinations
    if new_src_dir == new_gal_dir and p.orig_pic_action != 'm' and change_names:
        raise Exception,  "When no new separate gallery directory is created, and \n"\
               "names are changed, choose to have original pictures \n"\
               "moved to new names, not copied or left alone." 
    if p.orig_pic_action == 'l' and p.big_pics_linked and \
                (new_src_dir != new_gal_dir or len(galleries) > 0):
        raise Exception,  "You may not leave the original images behind and \n"\
               "link them into the gallery.  Change one of these options."

    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    # Now no setup errors:  make the changes calculated:  
    #  (Errors after this leave a partly changed file system.)
    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    
    change_files(p, galleries, orig_src_dir, new_src_dir, new_gal_dir, 
                 old_gallery_title, change_names, doing_rotations)        
    return new_gal_dir
# end of make_gallery --------------------------------------------------


def check_for_src_files(galleries, p):
    n_orig_pics = 0
    missing = []
    for g in galleries:
        g.check_dims_and_files(p.force_conversion or p.big_pics_linked,
                               p.thumb_dim, p.medium_dim, missing)
        n_orig_pics += len(g.docs)
    if missing:
        missing.insert(0, "Missing a required size for these pics:")
        missing.append("Either find and copy the original picture")
        missing.append("  or remove the picture from its pic doc file.")
        raise Exception,  "\n".join(missing)
    if not n_orig_pics:
        raise Exception,  "No pictures to process."


def plan_unique_names(galleries, use_titles, remove_blanks):
    name_count = {}
    for g in galleries:
        g.new_unique_names(use_titles, name_count, remove_blanks)


def change_files(p, galleries, orig_src_dir, new_src_dir, new_gal_dir, 
                 old_gallery_title, change_names, doing_rotations):        

    main_gal = galleries[0]
    
    # set up new pic dir
    set_new_dir(main_gal.gal_dir, new_src_dir, new_gal_dir)
    main_gal.gal_dir = new_src_dir #source for now, all output to new_gal_dir

    # avoid name conflict if in same dir: first rename orig_name
    #   to unique temp names and keep file system in sync
    if change_names and fnmatch.fnmatch(new_gal_dir, new_src_dir):
        main_gal.make_temp_orig_names()

    # rename/move pics and create small versions if needed
    # after this orig_name is no longer used, and name + suffixes refer to
    # existing pics
    for g in galleries:
        g.new_name_sizes(new_gal_dir, p.orig_pic_action, p.thumb_dim, 
                         p.medium_dim, p.force_conversion, p.display)
    # move orig doc files
    old_doc_file = os.path.join(new_src_dir, PICDOCFILE)
    no_doc_file = not os.path.exists(old_doc_file)
    doc_file = os.path.join(new_gal_dir, PICDOCFILE)
    if not no_doc_file and not fnmatch.fnmatch(new_src_dir, new_gal_dir):
        if_can(shutil.move, old_doc_file, doc_file)

    # backup doc file as needed            
    # Rename original doc_file + .old + '_#' if needed --
    need_new_doc_file = change_names or len(galleries) > 1 or \
                        doing_rotations or old_gallery_title != main_gal.title
    if need_new_doc_file and os.path.exists(doc_file): # save renamed old
        old_docs = new_file_name(doc_file+".old")
        os.rename(doc_file, old_docs)

    # move merge dir's docfiles into gallery dir picdocs + _#.old (+_#)
    #   The first # is the merge dir seq number: 2, 3, ...
    #   The _# is added in case of name conflict
    for (i,g) in enumerate(galleries):
        if i > 0:
            old_doc_file = os.path.join(g.gal_dir, PICDOCFILE)
            dest = new_file_name(doc_file + '_p' + str(i+1) + ".old")
            if_can(shutil.move, old_doc_file, dest)

    # transform docs so new name is copied to orig name, with no rotations
    # If needed, create a doc_file with the new names to allow further changes.
    if need_new_doc_file or no_doc_file:
        for g in galleries:
            g.change_orig_names()
        if len(galleries) > 1: 
            # add to first intro all other titles and intros
            strs = [g.title_intro2str() for g in galleries[1:]]
            strs.insert(0, main_gal.intro)
            main_gal.intro = "\n".join(strs)
            
            #add docs from other galleries
            for g in galleries[1:]:
                main_gal.docs.extend(g.docs)
        str2file(str(main_gal), doc_file)

    # clean out old html files, dimension files generated
    # ? might want to keep some of the smaller ones?  Future change?
    for g in galleries:
        g.del_old_html()
        
    # done with sources, now associate main_gal with output dir
    main_gal.gal_dir = new_gal_dir
    
    # creates index.html and 1.html, 2.html, ... for sequence
    gallery_html.make_html(main_gal, p.lines_in_thumb_label, p.big_pics_linked)

    # make zip file in new_gal_dir's parent TODO

    # ? starts browser on index.html TODO

    if not fnmatch.fnmatch(orig_src_dir, new_gal_dir):
        p.display("The picture gallery in NOW in directory " + new_gal_dir)
    
    p.galDir = new_gal_dir  # only output field in p  

    css = os.path.join(os.path.dirname(new_gal_dir),"gallery.css")
    if not os.path.exists(css):
        defaultCSS = os.path.join(os.path.dirname(gallery_html.__file__), 
                                  "gallery.css")
        if not os.path.exists(defaultCSS):
            raise Exception,  "Missing a copy of the file gallery.css.\n" + \
                   "One copy should be with the gallery source files."
        shutil.copy2(defaultCSS, css)
        
        
def remove_file_matches(files):
    tot = len(files)
    for n in range(tot-1,1,-1):
        for m in range(0, n-1):
            if fnmatch.fnmatch(files[m], files[n]):
                files[n:n+1] = []
                break


def set_new_dir(orig_src_dir, new_src_dir, new_gal_dir):
    """set up new pic dir, given its earlier choice.
    """

    if not fnmatch.fnmatch(new_gal_dir, orig_src_dir):
        if os.path.exists(new_gal_dir):
            raise Exception,  "Cannot make gallery directory " + new_gal_dir + "\n" + \
                  "The directory already exists:  delete it, rename it\n" + \
                  " or use a different title for your gallery."
        if fnmatch.fnmatch(new_gal_dir,new_src_dir):
            # try to rename new_gal_dir
            try:
                os.rename(orig_src_dir, new_gal_dir)

                new_src_dir = new_gal_dir
            except OSError:
                raise Exception,  "Cannot rename pic directory:\n" +\
                  "   "+  orig_src_dir +"\n" +\
                  "to:\n" +\
                  "   " + new_gal_dir + "\n" +\
                  "Some file is probably in use in " + orig_src_dir+".\n" +\
                  "  Make sure this program is running from somewhere else!\n"+\
                  "Rename it later by hand or by rerunning this program.\n" 
        else: # create new dir
            try: # tested obvious problems, but be safe with try
                os.mkdir(new_gal_dir)
            except OSError:
                raise Exception, "Cannot make gallery directory "+ new_gal_dir 
