#!/usr/bin/env python

# --------------------------------------------------------------------------
# Sky Cube Wizard v0.2 by Sanne
# --------------------------------------------------------------------------
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
# ***** END GPL LICENCE BLOCK *****
# --------------------------------------------------------------------------

import os
import sys
import getopt
import random
import math
try:
    import Image, ImageDraw, ImageFilter, ImageChops
except:
    print "Can't import the Python Imaging Library (PIL), aborting.\nPlease make sure it is installed."
    sys.exit(2)

__version__ = "0.2"
__author__ = "Sanne"
__url__ = "blenderartists.org"
__progname__ = "Sky Cube Wizard"


class Skycube:
    def __init__(self):
        self.inputfile = None
        self.outputfile = "skycube.png"
        self.marginwidth = 4
        self.mode = "b"
    
    def handleOptions(self, argv):
        """ Process command line options """
        try:                                
            opts, args = getopt.getopt(argv, "e:hm:o:v")
        except getopt.GetoptError:
            printUsage()
            sys.exit(2)
            
        if len(args) > 0:
            self.inputfile = args[0]
            
        for opt, arg in opts:
            if opt == "-e":
                self.marginwidth = int(arg)
            elif opt == "-h":
                self.printUsage()
                sys.exit(2)
            elif opt == "-m":
                self.mode = arg
            elif opt == "-o":
                self.outputfile = arg
            elif opt == "-v":
                print self.versionInfo()
                sys.exit(2)
            elif len(args) == 0:
                self.printUsage()
                sys.exit(2)
    
    def printUsage(self):
        """ Print usage information. """
        print ", ".join([self.versionInfo(),  __author__, __url__])
        print
        print "Description:"
        print "   Create a square sky cubemap from a Blender environment map"
        print "   or CG Textures sky cubemap."
        print
        print "Usage:"
        print "   python skycubewizard.py [options] image"
        print
        print "Options:"
        print "  -e n: Extend the sides of the four north, east, south,"
        print "        west parts with a margin of n pixels (default: 4)."
        print "  -h:   print usage information and exit"
        print "  -m x: Mode of the input file's image parts arrangement."
        print "        b: Blender, c: CG Textutres Skymaps (default: b)"
        print "  -o f: output filename f (default: <imagename>-cube.png)"
        print "  -v:   print version information and exit"
        print
        
    def versionInfo(self):
        return "%s version %s" % (__progname__, __version__)
    
    def extendImage(self, img):
        mw = self.marginwidth
        imgx = img.size[0]
        imgy = img.size[1]
        
        l = []   # left rows
        r = []   # right rows
        # get 1px row at left and right edges of image
        # number of rows is equal to marginwidth
        # multiple rows form a staircase
        for i in range(mw):
            l.append(img.crop((0, mw-i-1, 1, imgy)))
            r.append(img.crop((imgx-1, i, imgx, imgy)))
        
        # make new image with extended margins and paste 
        # left and right rows and original image
        extended = Image.new("RGBA", (imgx + 2*mw, imgy))
        for i in range(mw):
            extended.paste(l[i], (i, mw-i-1, i+1, imgy))
            extended.paste(r[i], (imgx+mw+i, i, imgx+mw+i+1, imgy))
        extended.paste(img, (mw, 0, imgx+mw, imgy))
        
        return extended
    
    def envToCube(self, inputfile):
        mw = self.marginwidth
        mode = self.mode
        
        inputmap = Image.open(inputfile)
        envx = inputmap.size[0]
        envy = inputmap.size[1]
        
        # get sizes of cubemap parts according to inputmap size and layout
        # Blender
        if mode == "b":
            edgelen = envy/2   # length of cube edge
            cubexy = envy      # x/y-size of cubemap
        # CGT
        elif mode == "c":
            edgelen = envy
            cubexy = 2 * envy
        
        # coordinates of inputmap according to layout
        # fractions should be integers due to the nature of the input files
        if mode == "b":
            ex = (0, envx/3, 2*envx/3, envx)
            ey = (0, envy/4, envy/2, 3*envy/4, envy)
        elif mode == "c":
            ex = (0, envx/6, envx/3, envx/2, 2*envx/3, 5*envx/6, envx)
            ey = (0, envy/2, envy)
        
        # coordinates of cubemap parts according to inputmap size, x = y
        cxy = (0, cubexy/4, 3*cubexy/4, cubexy)
        
        # coordinates of cubemap parts to paste to
        cubecoords = {
            "west":(cxy[0], cxy[1]-mw, cxy[1], cxy[2]+mw),
            "north":(cxy[1]-mw, cxy[0], cxy[2]+mw, cxy[1]),
            "south":(cxy[2], cxy[1]-mw, cxy[3], cxy[2]+mw),
            "east":(cxy[1]-mw, cxy[2], cxy[2]+mw, cxy[3]),
            "zenith":(cxy[1], cxy[1], cxy[2], cxy[2])
        }
        
        # coordinates of map parts in inputmap [0], cubemap [1], and transform [2]
        if mode == "b":
            westrect = [(ex[0], ey[0], ex[1], ey[1]), cubecoords["west"], 270]
            northrect = [(ex[1], ey[0], ex[2], ey[1]), cubecoords["north"], 180]
            eastrect = [(ex[2], ey[0], ex[3], ey[1]), cubecoords["south"], 90]
            southrect = [(ex[2], ey[2], ex[3], ey[3]), cubecoords["east"], 0]
            zenithrect = [(ex[1], ey[2], ex[2], ey[4]), cubecoords["zenith"], 0]
        elif mode == "c":
            westrect = [(ex[1], ey[0], ex[2], ey[1]), cubecoords["west"], 270]
            northrect = [(ex[5], ey[0], ex[6], ey[1]), cubecoords["north"], 180]
            eastrect = [(ex[0], ey[0], ex[1], ey[1]), cubecoords["south"], 90]
            southrect = [(ex[4], ey[0], ex[5], ey[1]), cubecoords["east"], 0]
            zenithrect = [(ex[2], ey[0], ex[3], ey[2]), cubecoords["zenith"], 0]
        
        # make new image and paste cropped and rotated enmap parts 
        # to their respective locations
        cubemap = Image.new("RGBA", (cubexy, cubexy))
        
        for part in [westrect, northrect, eastrect, southrect, zenithrect]:
            cropped = inputmap.crop(part[0])
            if part is not zenithrect and mw > 0:
                cropped = self.extendImage(cropped).rotate(part[2])
            cubemap.paste(cropped.convert("RGB"), part[1], cropped.convert("RGBA"))
        
        #cubemap.show()
        cubemap.save(self.outputfile)
    
    def main(self, argv):
        """ Main function """
        if len(argv) == 0:
            self.printUsage()
            sys.exit(2)
        self.handleOptions(argv)
        if self.inputfile and os.path.exists(self.inputfile) and not os.path.isdir(self.inputfile):
            root, ext = os.path.splitext(self.inputfile)
            self.outputfile = "".join([root, "-cube.png"])
            self.envToCube(self.inputfile)


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

if __name__ == "__main__":
     app = Skycube()
     app.main(sys.argv[1:])
