#!/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)
    
    # Recognising if selection was made
    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

    # Adding alpha channel
    if not pdb.gimp_drawable_has_alpha(drawable):
        pdb.gimp_layer_add_alpha(drawable)

    # Cropping the background
    if not selected_all:
        pdb.gimp_selection_load(select)
        pdb.gimp_selection_invert(image)
        pdb.gimp_edit_clear(drawable)
        pdb.gimp_selection_all(image)
       
    # 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
    depth = 256 ** (bytres / 2) - 1
    resolution = (int(pdb.gimp_image_get_resolution(image)[0]) + int(pdb.gimp_image_get_resolution(image)[1])) / 2  
    
    # Average map load calculation
    pdb.gimp_selection_load(select)        
    histogram = pdb.gimp_drawable_histogram(drawable, 0, 0, 1)
    
    if bytres == 2:
        ml = histogram[0] * 100 / depth
    elif bytres > 2:
        ml = histogram[0] * 100
    else:
        ctypes.windll.user32.MessageBoxW(0, unicode("Too low bit resolution of the input file :-(", "utf-8"), u"ERROR", 0x10)
        exit()

    # Distribution 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

    # Considering the cells' surroundings                                                                         
    #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
        if bytres == 2:
            for grid_y in range(0, gridheight):
                for grid_x in range(0, gridwidth):
                    pixel_value = pdb.gimp_drawable_get_pixel(drawable, grid_x, grid_y)
                    mls.append(pixel_value[1][0] * 100 / depth)
                    trs.append(pixel_value[1][1] * 100 / depth) 
        else:
            for grid_y in range(0, gridheight):
                for grid_x in range(0, gridwidth):
                    pixel_value = pdb.gimp_drawable_get_pixel(drawable, grid_x, grid_y)
                    mls.append(pixel_value[1][0] * 100)
                    trs.append(pixel_value[1][1] * 100)

    # SD recognition
    pdb.gimp_selection_load(select)
    if reportfile:
        histogram2 = pdb.gimp_drawable_histogram(drawable, 0, 0, 1)    
        if bytres == 2:
            sd = histogram2[1] * 100 / depth
        else:
            sd = histogram2[1] * 100

    pdb.gimp_selection_all(image)

    # Visualisation
    pdb.gimp_context_set_background((0, 0, 0))
    pdb.gimp_layer_flatten(drawable)
    pdb.gimp_image_scale_full(image, sizewidth, sizeheight, 0)
    pdb.gimp_image_convert_rgb(image)    
    pdb.gimp_image_insert_layer(image, layer1, None, -1)    
    pdb.gimp_layer_set_opacity(layer1, 5)

    # Value notification
    message = ""
    if not(99 <= resolution <= 101):
        message = "Warning: Image resolution is " + str(resolution) + " DPI while 100 DPI is recommended.\n\n"

    if selected_all:
        intro = "Graphic map load value: "
    else:
        intro = "Graphic map load value of your selection: "

    message = message + intro + 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
        array_index = 0
        row = 1
        while position_y < sizeheight:
            position_x = cellsize_width / 3
            col = 1
            while position_x < sizewidth:
                # Defining label colours
                if mls[array_index] >= 50:
                    green = int(255 - (mls[array_index] - 50) * 5.1)
                    pdb.gimp_context_set_foreground((255, green, 0))
                else:
                    blue = int(255 - mls[array_index] * 5.1)
                    pdb.gimp_context_set_foreground((255, 255, blue))
                
                # Converting numbers to labels
                if trs[array_index]:
                    mls[array_index] = "!!" if mls[array_index] > 100 else int(mls[array_index])
                else:
                    mls[array_index] = ""

                text = pdb.gimp_text_fontname(image, drawable, position_x, position_y, str(mls[array_index]), -1, False, cellsize / 3, 0, "Calibri")
                position_x = cellsize_width / 3 + col * cellsize_width
                array_index += 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-5",
    "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.5, build 240529, 2024",
    "GMLMT 1.5 en",
    "RGB*", 
    [
        (PF_IMAGE, "image", "takes current image", None),
        (PF_DRAWABLE, "drawable", "Input layer", None)
    ],
    [],
    mapload, menu="<Image>/Filters/Edge-Detect")

main()