#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Convert an pixel image into a mosaic constructed by polygons
=======================================================================
Author: Johannes Beetz
Based on algorithm described in "Artificial Mosaics (2005)" by Di Blasi
"""
import os
from pathlib import Path
import time
import random
import edges, guides, tiles, convex, coloring, plotting

# Select filename of input image
fname = r'mosaic_input.png' # let empty for test image

# Parameters

half_tile = 12 # 4...30 => half size of mosaic tile
FileAExist = Path("gimp_samj_mosaic_1.txt")
if FileAExist.is_file():
    FileAr = open("gimp_samj_mosaic_1.txt", "r")
    FileAVar = (FileAr.read())
    half_tile = int(FileAVar)

GAUSS = 3 # 0...8 => blurs image before edge detection (check "edges" image for a good value)
FileBExist = Path("gimp_samj_mosaic_2.txt")
if FileBExist.is_file():
    FileBr = open("gimp_samj_mosaic_2.txt", "r")
    FileBVar = (FileBr.read())
    GAUSS = int(FileBVar)

EDGE_DETECTION = 'HED' # HED or DiBlasi
FileCExist = Path("gimp_samj_mosaic_3.txt")
if FileCExist.is_file():
    FileCr = open("gimp_samj_mosaic_3.txt", "r")
    FileCVar = (FileCr.read())
    Val_GAUSS = int(FileCVar)
    if Val_GAUSS == 0:
        EDGE_DETECTION = 'HED'
    elif Val_GAUSS == 1:
        EDGE_DETECTION = 'DiBlasi'

WITH_FRAME = True # default is True => control about guidelines along image borders

RAND_SIZE = 0.3 # portion of tile size which is added or removed randomly during construction
FileDExist = Path("gimp_samj_mosaic_4.txt")
if FileDExist.is_file():
    FileDr = open("gimp_samj_mosaic_4.txt", "r")
    FileDVar = (FileDr.read())
    RAND_SIZE = float(FileDVar)

MAX_ANGLE = 40 # 30...75 => max construction angle for tiles along roundings
FileEExist = Path("gimp_samj_mosaic_5.txt")
if FileEExist.is_file():
    FileEr = open("gimp_samj_mosaic_5.txt", "r")
    FileEVar = (FileEr.read())
    MAX_ANGLE = int(FileEVar)

GAP_CHAIN_SPACING = 0.5 # 0.4 to 1.0 => spacing of gap filler chains
FileFExist = Path("gimp_samj_mosaic_6.txt")
if FileFExist.is_file():
    FileFr = open("gimp_samj_mosaic_6.txt", "r")
    FileFVar = (FileFr.read())
    GAP_CHAIN_SPACING = float(FileFVar)

MAKE_CONVEX = True # default is True => break concave into more realistic polygons
FileGExist = Path("gimp_samj_mosaic_7.txt")
if FileGExist.is_file():
    FileGr = open("gimp_samj_mosaic_7.txt", "r")
    FileGVar = (FileGr.read())
    Val_MAKE_CONVEX = int(FileGVar)
    if Val_MAKE_CONVEX == 0:
        MAKE_CONVEX = True
    elif Val_MAKE_CONVEX == 1:
        MAKE_CONVEX = False

COLOR_SCHEMA = ['nilotic',] # leave empty to plot all available or choose from 'wise_men',
                  #  'fish', 'cave_canem', 'nilotic', 'rooster', 'carpe_diem', 'Hyena'

# choose which image to plot
plot_list = [
    'original',
    #'edges', # CAN BE HELPFUL FOR ADJUSTING GAUSS PARAMETER
    #'distances',
    #'guidelines',
    #'gradient',
    #'angles_0to180',
    #'polygons_chains',
    #'used_up_space',
    #'distance_to_tile',
    #'filler_guidelines',
    #'polygons_filler',
    #'polygons_cut',
    'final', # <== MOST IMPORTANT
    #'final_recolored', # <== OR THIS
    #'statistics',
    ]


# Load image
t_start = time.time()
random.seed(0)
img0 = edges.load_image(fname, plot=plot_list)
h,w = img0.shape[0],img0.shape[1]
A0 = (2*half_tile)**2 # area of tile when placed along straight guideline
print (f'Estimated number of tiles: {2*w*h/A0:.0f}') # factor 2 since tiles can be smaller than default size

# Find edges of image objects
if EDGE_DETECTION == 'HED':
    img_edges = edges.edges_hed(img0, gauss=GAUSS, plot=plot_list)
elif EDGE_DETECTION == 'DiBlasi':
    img_edges = edges.edges_diblasi(img0, gauss=GAUSS, details=4, plot=plot_list)
else:
    raise ValueError('Parameter for edge detection mode not understood.')


if WITH_FRAME: 
    img_edges[0,:]=1; img_edges[-1,:]=1; img_edges[:,0]=1; img_edges[:,-1]=1


# place tiles along chains
chains, angles_0to180 = guides.chains_and_angles(img_edges, half_tile=half_tile, plot=plot_list)
polygons_chains = tiles.place_tiles_along_chains(chains, angles_0to180, half_tile, RAND_SIZE, MAX_ANGLE, A0, plot=plot_list)

# find gaps and put more tiles inside
filler_chains = guides.chains_into_gaps(polygons_chains, h, w, half_tile, GAP_CHAIN_SPACING, plot=plot_list)
polygons_all = tiles.place_tiles_into_gaps(polygons_chains, filler_chains, half_tile, A0, plot=plot_list)

# remove parts of tiles which reach outside of image frame
polygons_all = tiles.cut_tiles_outside_frame(polygons_all, half_tile, img0.shape[0],img0.shape[1], plot=plot_list)

# convert concave polygons to convex (i.e. more realistic) ones
polygons_convex = convex.make_convex(polygons_all, half_tile, A0) if MAKE_CONVEX else polygons_all

# make polygons smaller, remove or correct strange polygons, simplify and drop very small polygons
polygons_post = tiles.irregular_shrink(polygons_convex, half_tile)
polygons_post = tiles.repair_tiles(polygons_post) 
polygons_post = tiles.reduce_edge_count(polygons_post, half_tile)
polygons_post = tiles.drop_small_tiles(polygons_post, A0)

if 'final' in plot_list:
    # copy colors from original image
    colors = coloring.colors_from_original(polygons_post, img0, method='average')
    
    t0 = time.time()
    svg = plotting.draw_tiles(polygons_post, colors, h,w, background_brightness=0.2, return_svg=True, chains=None)
    if svg:
        with open("mosaic_output.svg", "w") as fn:
            fn.write(svg)
    # print ('Final plot / saving:', f'{time.time()-t0:.1f}s') 

if 'final_recolored' in plot_list:
    color_dict = coloring.load_colors()
    keys = color_dict.keys() if not COLOR_SCHEMA else COLOR_SCHEMA
    for key in keys:
        new_colors = coloring.modify_colors(colors, 'source', color_dict[key])
        title = key if not COLOR_SCHEMA else ''
        plotting.draw_tiles(polygons_post, new_colors, h, w, background_brightness=0.2,
                            return_svg=None, chains=None, title=title)

if 'statistics' in plot_list:
    plotting.statistics(polygons_post)

print (f'Total calculation time: {time.strftime("%M min %S s", time.gmtime((time.time()-t_start)))}' ) # sek->min:sek
print ('Final number of tiles:', len(polygons_post))

