#!/usr/bin/env python

#   Gimp-Python - allows the writing of Gimp plugins in Python.
#   Copyright (C) 1997  James Henstridge <james@daa.com.au>
#
#   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.

# Copyright (C) 2008  Paul Taney
# bug reports or suggestions to paultaney@yahoo.com
# my examples were
#    retinex.py plugin -- Copyright (C) 2007, John Fremlin
#    clothify.py plugin -- Copyright (C) 1997, James Henstridge <james@daa.com.au>
#    foggify.py plugin -- Copyright (C) 1997, James Henstridge <james@daa.com.au>
#    average_layer.py plugin -- Copyright (C) 2008, Elmar Hoefner

TESTING = 0
if not TESTING:
    from gimpfu import *
import sys, os.path
import numpy
#import scipy # BREAKS on my installation

# in the future I hopw to use scipy clustering
# from scipy.cluster.vq import *  # has kmeans2 (http://hackmap.blogspot.com/2007/09/k-means-clustering-in-scipy.html)


if not TESTING:
    gettext.install("gimp20-python", gimp.locale_directory, unicode=True)


line_template ="""
<line x1=%s y1=%s x2=%s y2=%s
style=stroke:rgb(99,99,99);stroke-width:2/>
"""

def getSVG(x1, y1, x2, y2):
    d = {"x1":str(x1), "y1":str(y1), "x2":str(x2), "y2":str(y2)}
    x1=str(x1); y1=str(y1); x2=str(x2); y2=str(y2)
    #if 1: print ">>>%r<<<" % d
    return line_template % (x1, y1, x2, y2)
  
header1 = """<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg width="100%" height="100%" version="1.1"
xmlns="http://www.w3.org/2000/svg">

<!-- This SVG was generated by gimpfu plug-in at <Images>/Filters/Render/Stroke to SVG... -->
"""
  
header2 = """<!-- This is an SVG snippet representing a line. -->
<!-- This should not be considered a complete SVG file. -->
<!-- For instance, we dont handle line color (much). -->
"""

footer = """</svg>
"""
def writeSVG(filename, line, sx, sy, density):

    # line = flip_Y(line)
    # line = interpolate(line)
    # line = uniqify_x(line)

    stride = int((len(line)-1)/density/2)  # divide by 1.5 because... unzip() doubles the length
    if 1: print "writing %s, stride=%r" % (filename, stride)
    if stride < 1: stride = 1

    count = 1 
    acc = []
    for x1, y1, x2, y2 in unzip(line):
        if count % stride == 0:
            x1, y1, x2, y2 = x1*sx, y1*sy, x2*sx, y2*sy 
            acc.append(getSVG(x1, y1, x2, y2))
        count += 1

    """bug:  if linelength was an odd number it lost the last point"""
    if len(line)%2 != 0:
        acc.append(getSVG(line[-2][0]*sx, line[-2][1]*sy, line[-1][0]*sx, line[-1][1]*sy)) 

    FH = open(filename, 'w')
    FH.write(header1)
    #FH.write(header2)
    FH.write(" ".join(acc))
    FH.write(footer)
    FH.close()
    #pdb.gimp_message("wrote %s" % filename)

def writeCSV(filename, line, sx, sy, density):
    stride = int((len(line)-1)/density) 
    if 1: print "writing %s, stride=%r" % (filename, stride)
    if stride < 1: stride = 1

    count = 1 
    acc = []
    for x1, y1 in line:
        if count % stride == 0:
            acc.append("%.2f, %.2f," % (x1*sx, y1*sy))
        count += 1

    if len(line)%2 != 0:
        s = "%.2f, %.2f," % (line[-1][0]*sx, line[-1][1]*sy)
        acc.append(s)  # dont miss one

    FH = open(filename, 'w')
    FH.write("# CSV file generated by stroke_to_vector.py\n")
    FH.write("# a gimpfu plug-in at <Images>/Filters/Render/Stroke to SVG\n")
    FH.write("\n".join(acc))
    FH.write("\n")
    FH.close()
    #pdb.gimp_message("wrote %s" % filename)

def write_tuple(filename, line, sx, sy, density):

    # line = flip_Y(line)
    # line = interpolate(line)
    # line = uniqify_x(line)

    stride = int((len(line)-1)/density) 
    if 1: print "writing %s, stride=%r" % (filename, stride)
    if stride < 1: stride = 1

    count = 1 
    acc = []
    for x1, y1 in line:
        if count % stride == 0:
            x1, y1 = x1*sx, y1*sy
            acc.append("    (%.2f, %.2f)" % (x1, y1))
        count += 1

    """bug:  if linelength was an odd number it lost the last point"""
    if len(line)%2 != 0:
        s = "    (%.2f, %.2f)," % (line[-1][0]*sx, line[-1][1]*sy)
        acc.append(s)  # dont miss one

    FH = open(filename, 'w')
    FH.write("# file generated by stroke_to_vector.py\n")
    FH.write("# a gimpfu plug-in at <Images>/Filters/Render/Stroke to SVG...\n")
    FH.write("# scalefactors  %r, %r\n" % (sx, sy))
    FH.write("line = (\n")
    FH.write("\n".join(acc))
    FH.write("\n    )\n")
    FH.close()
    #pdb.gimp_message("wrote %s" % filename)

def unzip(L):
    """change 2-tuples to 4-tuple s for linedrawing""" 
    M = []
    while len(L)>=2:
        M.append((L[0][0], L[0][1], L[1][0], L[1][1])) 
        L = L[2:]
    return M

def read_in(drawable):
    width = drawable.width
    height = drawable.height
    bpp = drawable.bpp
    pr = drawable.get_pixel_rgn(0, 0, width, height, False)
    a = numpy.fromstring(pr[:,:],"B")
    assert(a.size == width * height * bpp)
    image = numpy.array(a.reshape(height,width,bpp),"d")[:,:,0:min(bpp,3)] # Travis O. page 133
    return image/256.0


def vanderwalt(image, f):
    """thanks to Stefan van der Walt"""
    RED, GRN, BLU = 0, 1, 2
    bluemask = (image[...,BLU] > f*image[...,GRN]) & \
               (image[...,BLU] > f*image[...,RED])

    return bluemask

def write_out(drawable, image):
    """by John Fremlin"""
    byte_image = numpy.array((image*256).round(0),"B")  # B is a ubyte
    width = drawable.width
    height = drawable.height
    bpp = drawable.bpp
    sys.stderr.write("write_out::drawable.width=%i drawable.height=%i drawable.bpp=%i\n" % (width, height, bpp))
    assert(width)
    assert(height)

    pr = drawable.get_pixel_rgn(0, 0, width, height, True)
    assert(byte_image.size == width * height * bpp)
    pr[:,:] = byte_image.tostring()
    # he doesnt return anything, so pr must be real estate.  I think its one colorchannel...

#     for y in range(height):
#         for x in range(width):
#             pr[x,y] = struct.pack('BBB',*byte_image[y,x,:])
           
#         progress_update("write-out", (y / float(height)))

def progress_update(stage, proportion_done):
    gimp.progress_update(proportion_done*0.10)

#def retinex_plugin(img, drawable, new_layer_name, opacity, logscale, flatten):
def stroke_to_vector_plugin(img, drawable, new_layer_name, factor, flatten, scalefactorx, scalefactory, density, outputformat, filename):

    #assert not pdb.gimp_selection.is_empty  # perhaps they must select an area for me to get the "drawable"?
    # what is a drawable, anyways?

    img.undo_group_start()

    try:
        image = read_in(drawable)

        bluemask = vanderwalt(image, factor)

        opacity = 100
        new_layer = gimp.Layer(img, new_layer_name, drawable.width, drawable.height, 
                               RGB_IMAGE, opacity, NORMAL_MODE)

        # write_out(new_layer, bluemask)  # all black    # nonzero() doc: Travis O., page 75
        # write_out(new_layer, bluemask.nonzero())       # failed with:  AttributeError:
        height, width = drawable.width, drawable.height
        assert(width)
        assert(height)

        OUT = """
        this section fails in write_out()

        #b = numpy.array((bluemask.nonzero()*3).reshape(height, width, 3),"b") # 'tuple' object has no attribute 'reshape'
        a = numpy.array(bluemask.nonzero())  # *3 so the bpp is normalized? ...also fails
        write_out(new_layer, a)    
        img.add_layer(new_layer, 0)
"""
        if flatten:
            img.flatten()

        if 0: print "outputformat= >>>%s<<<" %  outputformat
        if outputformat == "SVG":
            line = numpy.array(bluemask.nonzero()).swapaxes(0,1).tolist()
            writeSVG(filename, line, scalefactorx, scalefactory, density)
        elif outputformat == "python":
            line = numpy.array(bluemask.nonzero()).swapaxes(0,1).tolist()
            write_tuple(filename, line, scalefactorx, scalefactory, density)
        elif outputformat == "CSV":
            line = numpy.array(bluemask.nonzero()).swapaxes(0,1).tolist()
            writeCSV(filename, line, scalefactorx, scalefactory, density)
        else:
            sys.stdout.write("No output format selected.\n")

    finally:
        img.undo_group_end()


if not TESTING:
        register(
        "python-fu-stroke_to_vector",  # name
	"bluest line to layer and optional vector file v0.1",  # blurb
        N_("""Retain the bluest blues in a new layer and save as a vector
where blue sub-pixels are greater than red and green by some factor 
(default 40%).  Why just blue?  because I am handling stripcharts with
blue ink on them.  If you have another application, I could add RADIOs."""),  # help
        "Paul Taney",  # author
        "Paul Taney",  # copyright
        "2008",  # date
        N_("Stroke To SVG..."),  # gimp menu
        "RGB*",  # image arguments?
        [
            (PF_IMAGE, "image", "Input image", None),
            (PF_DRAWABLE, "drawable", "Input drawable", None),
            (PF_STRING, "name", _("New _layer name"), _("blueline")),
            #(PF_SLIDER, "opacity", _("Op_acity"),    25, (0, 100, 1)),
            (PF_SLIDER, "factor", _("How blue?"),    1.4, (1, 2, .1)),
            (PF_BOOL,   "flatten", _("_Flatten layers together after processing"), False),
            (PF_FLOAT,  "scalefactorx", "scale factor x",    "1"),    	
            (PF_FLOAT,  "scalefactory", "scale factor y",    "1"),    	
            (PF_SLIDER, "density", _("Line _density"),    50, (20, 200, 10)),
	    (PF_RADIO,  "outputformat", "Output format:",  "None", (
		("None", "None"),
		("SVG",  "SVG"),
		("CSV",  "CSV"),
		("python tuple", "python"))), 	
            #(PF_FILENAME, "filename", "Output file:", os.path.expanduser("~/tmp.svg")),  # Beazley p326, fails trying to convert to Unicode...
            (PF_FILENAME, "filename", "Output file:", "tmp.svg"), 
        ],
        [],
        stroke_to_vector_plugin,
        menu="<Image>/Filters/Render/",
        domain=("gimp20-python", gimp.locale_directory)
        )

if not TESTING:
    main() # gimpfu
else:
    L = [(25, 26), 
         (50, 51), 
         (100, 101), 
         (150, 151), 
         (200, 201), 
         (250, 251), 
         (300, 301), 
         (350, 351), 
         (400, 401),
         (450, 451),
         (500, 501), 
         (550, 551), 
         (600, 601),
         (650, 651),
         (700, 701), 
         (750, 751), 
         (800, 801),
         (850, 851),
         (900, 901), 
         (950, 951), 
         (1050, 1051),
         ]

    #getSVG(25, 26, 50, 51)
    density = 1
    #writeSVG("tmp1.svg", [(25, 26), (50, 51), (100, 101)], 1, 1, density)
    density = 3
    writeSVG("tmp3.svg", L, 1, 1, density)
    density = 4
    writeSVG("tmp4.svg", L, 1, 1, density)
    density = 5
    writeSVG("tmp5.svg", L, 1, 1, density)
    density = 6
    writeSVG("tmp6.svg", L, .2, .4, density)

    density = 3
    write_tuple("tmp3.dat", L, 1, 1, density)
    density = 4
    write_tuple("tmp4.dat", L, 1, 1, density)
    density = 5
    write_tuple("tmp5.dat", L, 1, 1, density)
    density = 6
    write_tuple("tmp6.dat", L, .2, .4, density)

    density = 6
    writeCSV("tmp7.csv", L, .2, .4, density)
