#!/usr/bin/env python
# -*- coding: utf-8 -*-

from gimpfu import * 
import ctypes
import os
from datetime import datetime

def mapload(image, drawable):

    # Settings
    reportfile = True   # generate a line report in the text file
    labels = True       # label map load visualisation grid (time-consuming)
    cellcount = 10      # number of cells for map load visualization on the shortest dimension of the map

    # Merging steps
    pdb.gimp_image_undo_group_start(image)
    
    # Image size recognition and layer creation
    filename = pdb.gimp_layer_get_name(image.layers[0])
    sizewidth = drawable.width
    sizeheight = drawable.height
    
    select = pdb.gimp_selection_save(image)    
    layer1 = pdb.gimp_layer_new_from_drawable(drawable, image)
    
    # Pixel count recognition for selection and image
    pixels_selection = pdb.gimp_drawable_histogram(drawable, 0, 0, 1)[3]
    pdb.gimp_selection_all(image)
    pixels_all = pdb.gimp_drawable_histogram(drawable, 0, 0, 1)[3]
    selected_all = pixels_selection == pixels_all
    is_transparent = pdb.gimp_drawable_has_alpha(drawable)
       
    # Edge detection using Sobel operator       
    pdb.plug_in_edge(image, drawable, 1, 0, 0)

    # Converting to greyscale according to the value parameter
    r_gain = 0.299
    g_gain = 0.587
    b_gain = 0.114
    
    pdb.plug_in_colors_channel_mixer(image, drawable, True, r_gain, r_gain, r_gain, g_gain, g_gain, g_gain, b_gain, b_gain, b_gain)

    # Converting to monochrome    
    pdb.gimp_image_convert_grayscale(image)

    # Bit depth recognition
    bytres = drawable.bpp
    if is_transparent:
        depth = 256 ** (bytres / 2) - 1
    else:
        depth = 256 ** bytres - 1
    
    resolution = (int(pdb.gimp_image_get_resolution(image)[0]) + int(pdb.gimp_image_get_resolution(image)[1])) / 2  
    
    # Map load calculation
    pdb.gimp_selection_load(select)        
    histogram = pdb.gimp_drawable_histogram(drawable, 0, 0, 1)
    
    if bytres == 1 or (bytres == 2 and is_transparent):
        ml = histogram[0] * 100 / depth
    else:
        ml = histogram[0] * 100

    # Grid creation
    pdb.gimp_selection_all(image)  
    if sizewidth > sizeheight:
        cellsize = sizeheight // cellcount + (sizeheight % cellcount > 0)
        gridheight = cellcount
        gridwidth = int(sizewidth * gridheight / sizeheight)
    else:
        cellsize = sizewidth // cellcount + (sizewidth % cellcount > 0)
        gridwidth = cellcount
        gridheight = int(sizeheight * gridwidth / sizewidth)
    pdb.gimp_image_scale_full(image, gridwidth, gridheight, 1)

    cellsize_width = sizewidth / gridwidth
    cellsize_height = sizeheight / gridheight
                                                                             
    pdb.plug_in_convmatrix(image, drawable, 25, [0,0,0,0,0, 0,1,2,1,0, 0,2,4,2,0, 0,1,2,1,0, 0,0,0,0,0], False, 16, 0, 5, [True, True, True, True, True], 0)

    # Map load calculation in grid
    if labels:
        mls = []  # map load values of cells
        trs = []  # transparencies of cells
        for grid_y in range(0, gridheight):
            for grid_x in range(0, gridwidth):
                pixelvalue = pdb.gimp_drawable_get_pixel(drawable, grid_x, grid_y)
                if bytres == 1:
                    mls.append(pixelvalue[1][0] * 100 / depth)
                elif bytres == 2 and is_transparent:
                    mls.append(pixelvalue[1][0] * 100 / depth)
                    trs.append(pixelvalue[1][1] * 100 / depth)
                else:
                    mls.append(pixelvalue[1][0] * 100)
        if len(mls) != 0:
            mean_ml = sum(mls) / len(mls)
        else:
            ctypes.windll.user32.MessageBoxW(0, unicode("Error in computing grid :-(", "utf-8"), u"ERROR", 0x10)
            exit()

        if is_transparent and (len(trs) != 0):
            mean_tr = sum(trs) / len(trs) / 100.0  # obtaining transparency ranging 0 (fully transparent) to 1 (fully covered)
        else:
            mean_tr = 1

    # SD recognition
    pdb.gimp_selection_load(select)
    if reportfile:
        histogram2 = pdb.gimp_drawable_histogram(drawable, 0, 0, 1)    
        if bytres == 1 or (bytres == 2 and is_transparent):
            sd = histogram2[1] / depth * 100
        else:
            sd = histogram2[1] * 100

    # Visualisation
    pdb.gimp_image_scale_full(image, sizewidth, sizeheight, 0)
    pdb.gimp_image_convert_rgb(image) 
        
    # if not selected_all:
    if not is_transparent:
        pdb.gimp_layer_add_alpha(drawable)
    
    pdb.gimp_image_insert_layer(image, layer1, None, -1)    
    pdb.gimp_layer_set_opacity(layer1, 5)

    # Value notification
    message = ""
    if resolution != 100:
        message = "Warning: Image resolution is " + str(resolution) + " DPI while 100 DPI is recommended.\n\n"
    if selected_all:
        message = message + "Graphic map load value: " + str(round(ml, 1)) + " %"
    else:
        message = message + "Graphic map load value of your selection: " + str(round(ml, 1)) + " %"
    ctypes.windll.user32.MessageBoxW(0, unicode(message, "utf-8"), u"MEASUREMENT RESULTS", 0x40)
        
    # Grid labels
    if labels:
        position_y = sizeheight / gridheight / 3
        arrayindex = 0
        row = 1
        while position_y < sizeheight:
            position_x = cellsize_width / 3
            col = 1
            while position_x < sizewidth:
                if mean_ml != 0:
                    mls[arrayindex] = mls[arrayindex] * ml / mean_ml * mean_tr
                if mls[arrayindex] >= 50:
                    green = int(255 - (mls[arrayindex] - 50) * 5.1)
                    pdb.gimp_context_set_foreground((255, green, 0))
                else:
                    blue = int(255 - mls[arrayindex] * 5.1)
                    pdb.gimp_context_set_foreground((255, 255, blue))
                text = pdb.gimp_text_fontname(image, drawable, position_x, position_y, int(mls[arrayindex]), -1, False, cellsize / 3, 0, "Calibri")
                position_x = cellsize_width / 3 + col * cellsize_width
                arrayindex += 1
                col += 1
            position_y = cellsize_height / 3 + row * cellsize_height
            row += 1 
    
        pdb.gimp_floating_sel_anchor(pdb.gimp_image_get_floating_sel(image))

        # Writing a report file
        if reportfile:
            now = datetime.now()
            current_time = now.strftime("%Y%m%d_%H%M%S")
            if not (os.path.isfile("gmlmt_report.txt") and os.path.getsize("gmlmt_report.txt") > 0):
                reportfile = open("gmlmt_report.txt", "a")
                reportfile.write("time_stamp\tfile_name\tmap_load_perc\tstd_dev_perc\tpixel_count\tresolution\n")
                reportfile.close()

            reportfile = open("gmlmt_report.txt", "a+")
            reportfile.write(current_time)
            reportfile.write("\t")
            reportfile.write(str(filename))
            reportfile.write("\t")
            reportfile.write(str(round(ml, 1)))
            reportfile.write("\t")
            reportfile.write(str(round(sd, 1)))
            reportfile.write("\t")
            reportfile.write(str(int(pixels_selection)))
            reportfile.write("\t")
            if resolution == 100:
                reportfile.write("ok")
            else:
                reportfile.write(str(resolution))
            reportfile.write("\n")
            reportfile.close()

    # Finishing merged steps
    pdb.gimp_image_undo_group_end(image)
    
register(
    "MAPLOAD_1-4",
    "Experimental tool for graphic map load measurement based on edge detection",
    "Experimental tool for graphic map load measurement based on edge detection using Sobel operator, developed at the Department of Geoinformatics, Palacký University Olomouc (RGB images with resolution 100 DPI prefered)",
    "Radek Barvíř", "CC BY-SA", "v 1.4, build 221120, 2022",
    "GMLMT 1.4 en",
    "RGB*", 
    [
        (PF_IMAGE, "image", "takes current image", None),
        (PF_DRAWABLE, "drawable", "Input layer", None)
    ],
    [],
    mapload, menu="<Image>/Filters/Edge-Detect")

main()