import tkinter as tk
from tkinter import colorchooser, filedialog
from PIL import Image, ImageDraw, ImageTk
from tkinter import filedialog
import math
import os
import zipfile
import imageio
import shutil
import subprocess

class ImagePreviewGenerator:
    def __init__(self, root):
        self.root = root
        self.root.title("MOE creator")

        # Controls for stripes
        self.stripe_height = tk.IntVar(value=260)
        self.stripe_width = tk.IntVar(value=40)
        self.stripe_color = "#FF0000"
        self.stripe_border_color = "#000000"
        self.stripe_border_size = tk.IntVar(value=3)

        # Controls for shapes
        self.shape_type = tk.StringVar(value="No Shape")
        self.shape_size = tk.IntVar(value=35)
        self.shape_color = "#0000FF"
        self.shape_border_color = "#000000"
        self.shape_border_size = tk.IntVar(value=2)

        # Background Color (allows transparency)
        self.bg_color = "#FFFFFF"
        self.use_transparent_bg = tk.BooleanVar(value=False)
        self.bg_image_path = None
        self.use_bg_image = tk.BooleanVar(value=False)
        
        # dirt Effect
        self.dirt_intensity = tk.IntVar(value=0)
        self.texture_image_path = None
        self.apply_dirt_effect = tk.BooleanVar(value=False)
        
        # Wear Intensity Variable
        self.wear_intensity = tk.IntVar(value=0)
        self.wear_texture_path = None
        self.apply_wear_effect_var = tk.BooleanVar(value=False)  # New line
        
        # Shape image path
        self.shape_image_path = None
        self.retain_aspect_ratio = tk.BooleanVar(value=True)
        self.enable_image = tk.BooleanVar(value=False)  # New variable for enabling/disabling the image

        # Shape options including "No Shape"
        shape_options = ["No Shape", "Circle", "Square", "Ring", "4-Point Star", "5-Point Star", 
                         "6-Point Star", "7-Point Star", "8-Point Star", "Diamond"]

        #Folder path for mod
        output_dir = os.path.join("res", "gui", "maps", "vehicles", "decals", "insignia")
        os.makedirs(output_dir, exist_ok=True)

        # Create frames for better organization
        controls_frame = tk.Frame(root)
        controls_frame.grid(row=0, column=0, padx=10, pady=10, sticky="ew")

        stripe_frame = tk.LabelFrame(controls_frame, text="Stripe Settings", padx=10, pady=10)
        stripe_frame.grid(row=0, column=0, padx=10, pady=10, sticky="n")

        shape_frame = tk.LabelFrame(controls_frame, text="Shape Settings", padx=10, pady=10)
        shape_frame.grid(row=0, column=1, padx=10, pady=10)

        bg_frame = tk.LabelFrame(controls_frame, text="Background Settings", padx=10, pady=10)
        bg_frame.grid(row=0, column=2, padx=10, pady=10, sticky="n")

        dirt_frame = tk.LabelFrame(root, text="Dirt Settings", padx=0, pady=0)
        dirt_frame.grid(row=1, column=0, padx=10, pady=0, sticky="e") 
 
        wear_frame = tk.LabelFrame(root, text="Wear Settings", padx=0, pady=0)
        wear_frame.grid(row=1, column=1, padx=10, pady=0, sticky="ew")
               
        run_frame = tk.LabelFrame(root, text="Run Insignia Script", padx=10, pady=10)
        run_frame.grid(row=1, column=3, padx=10, pady=10, sticky="n")

        create_mod_frame = tk.LabelFrame(root, text="Create Mod", padx=0, pady=0)
        create_mod_frame.grid(row=1, column=2, padx=10, pady=0, sticky="n")
        
        # Stripe Controls
        tk.Label(stripe_frame, text="Stripe Height:").grid(row=0, column=0, sticky="w")
        tk.Scale(stripe_frame, from_=0, to=260, orient=tk.HORIZONTAL, variable=self.stripe_height, command=self.generate_previews).grid(row=0, column=1, sticky="e")

        tk.Label(stripe_frame, text="Stripe Width:").grid(row=1, column=0, sticky="w")
        tk.Scale(stripe_frame, from_=0, to=86, orient=tk.HORIZONTAL, variable=self.stripe_width, command=self.generate_previews).grid(row=1, column=1, sticky="e")

        tk.Button(stripe_frame, text="Stripe Color", command=self.choose_stripe_color).grid(row=2, column=0, sticky="w")
        tk.Button(stripe_frame, text="Stripe Border Color", command=self.choose_stripe_border_color).grid(row=2, column=1, sticky="e")

        tk.Label(stripe_frame, text="Stripe Border Size:").grid(row=3, column=0, sticky="w")
        tk.Scale(stripe_frame, from_=0, to=10, orient=tk.HORIZONTAL, variable=self.stripe_border_size, command=self.generate_previews).grid(row=3, column=1, sticky="e")

        # Shape Controls
        tk.Label(shape_frame, text="Shape:").grid(row=0, column=0, sticky="w")
        tk.OptionMenu(shape_frame, self.shape_type, *shape_options, command=self.generate_previews).grid(row=0, column=1, sticky="e")

        tk.Label(shape_frame, text="Shape Size:").grid(row=1, column=0, sticky="w")
        tk.Scale(shape_frame, from_=10, to=55, orient=tk.HORIZONTAL, variable=self.shape_size, command=self.generate_previews).grid(row=1, column=1, sticky="e")

        tk.Button(shape_frame, text="Shape Color", command=self.choose_shape_color).grid(row=2, column=0, sticky="w")
        tk.Button(shape_frame, text="Shape Border Color", command=self.choose_shape_border_color).grid(row=2, column=1, sticky="e")

        tk.Label(shape_frame, text="Shape Border Size:").grid(row=3, column=0, sticky="w")
        tk.Scale(shape_frame, from_=0, to=10, orient=tk.HORIZONTAL, variable=self.shape_border_size, command=self.generate_previews).grid(row=3, column=1, sticky="e")

        # New Button for Choosing Shape Image
        tk.Button(shape_frame, text="Choose Shape Image", command=self.choose_shape_image).grid(row=4, column=0, sticky="w")

        # Checkbox for Retaining Aspect Ratio
        tk.Checkbutton(shape_frame, text="Retain Aspect Ratio", variable=self.retain_aspect_ratio, command=self.generate_previews).grid(row=4, column=1, sticky="e")

        # Checkbox for Enabling Image
        tk.Checkbutton(shape_frame, text="Use Image as Shape", variable=self.enable_image, command=self.generate_previews).grid(row=5, column=0, columnspan=2, sticky="w")

        # Background Settings
        tk.Button(bg_frame, text="Background Color", command=self.choose_bg_color).grid(row=0, column=0, sticky="w")
        tk.Checkbutton(bg_frame, text="Use Transparent Background", variable=self.use_transparent_bg, command=self.generate_previews).grid(row=1, column=0, sticky="e")
        tk.Button(bg_frame, text="Background Image", command=self.choose_bg_image).grid(row=2, column=0, sticky="w")
        tk.Checkbutton(bg_frame, text="Use Background Image", variable=self.use_bg_image, command=self.generate_previews).grid(row=3, column=0, sticky="w")

        # dirt Settings
        tk.Label(dirt_frame, text="Dirt Intensity:").grid(row=0, column=0, sticky="w", padx=5, pady=5)
        tk.Scale(dirt_frame, from_=0, to=100, orient=tk.HORIZONTAL, variable=self.dirt_intensity, command=self.generate_previews).grid(row=0, column=1, sticky="e", padx=5, pady=5)
        tk.Button(dirt_frame, text="Dirt Texture", command=self.choose_texture_image).grid(row=1, column=0, sticky="w", padx=5, pady=5)
        tk.Checkbutton(dirt_frame, text="Apply Dirt Effect", variable=self.apply_dirt_effect, command=self.generate_previews).grid(row=2, column=0, sticky="w", padx=5, pady=5)

        # Wear controls positioned on the right side, keeping the same layout
        tk.Label(wear_frame, text="Wear Intensity:").grid(row=0, column=2, sticky="w", padx=5, pady=5)
        tk.Scale(wear_frame, from_=0, to=100, orient=tk.HORIZONTAL, variable=self.wear_intensity, command=self.generate_previews).grid(row=0, column=3, sticky="e", padx=5, pady=5)
        tk.Button(wear_frame, text="Wear Texture", command=self.choose_wear_texture).grid(row=1, column=2, sticky="w", padx=5, pady=5)
        tk.Checkbutton(wear_frame, text="Apply Wear Effect", variable=self.apply_wear_effect_var, command=self.generate_previews).grid(row=2, column=2, sticky="w", padx=5, pady=5)

        #Insignia run
        tk.Button(run_frame, text="Run Insignia", command=self.run_insignia_script).grid(row=0, column=0, padx=5, pady=5)

        # Dummy content for Create Mod frame
        tk.Label(create_mod_frame, text="Mod will be prefixed with MOE.").grid(row=0, column=0, padx=5, pady=5)
        tk.Button(create_mod_frame, text="Create Mod", command=self.save_images).grid(row=3, column=0, padx=5, pady=5)

        # Preview Canvas
        self.canvas = tk.Canvas(root, width=(256 + 10) * 3, height=256)
        self.canvas.grid(row=3, column=0, padx=10, pady=10)

        # Automatically generate previews on startup
        self.generate_previews()

    def run_insignia_script(self):
        """Function to run the insignia.py script."""
        try:
            subprocess.run(['python', 'insignia.py'], check=True)
        except subprocess.CalledProcessError as e:
            print(f"Error running insignia.py: {e}")
            
    def choose_stripe_color(self):
        color_code = colorchooser.askcolor(title="Choose Stripe Color")
        self.stripe_color = color_code[1]
        self.generate_previews()

    def choose_stripe_border_color(self):
        color_code = colorchooser.askcolor(title="Choose Stripe Border Color")
        self.stripe_border_color = color_code[1]
        self.generate_previews()

    def choose_shape_color(self):
        color_code = colorchooser.askcolor(title="Choose Shape Color")
        self.shape_color = color_code[1]
        self.generate_previews()

    def choose_shape_border_color(self):
        color_code = colorchooser.askcolor(title="Choose Shape Border Color")
        self.shape_border_color = color_code[1]
        self.generate_previews()

    def choose_bg_color(self):
        color_code = colorchooser.askcolor(title="Choose Background Color")
        self.bg_color = color_code[1]
        self.generate_previews()

    def choose_texture_image(self):
        self.texture_image_path = filedialog.askopenfilename(
            title="Select Texture Image",
            filetypes=[("Image Files", "*.png")]
        )
        self.generate_previews()

    def choose_shape_image(self):
        """Open a file dialog to select an image for the shape."""
        self.shape_image_path = filedialog.askopenfilename(
            title="Select Shape Image",
            filetypes=[("Image Files", "*.png")]
        )
        self.generate_previews()  # Regenerate previews after choosing a new image
        
    def choose_bg_image(self):
        self.bg_image_path = filedialog.askopenfilename(
            title="Select Background Image",
            filetypes=[("Image Files", "*.png;*.jpg;*.jpeg")]
        )
        self.generate_previews()

    def generate_previews(self, *args):
        self.canvas.delete("all")
        img_1 = self.create_image(1)
        img_2 = self.create_image(2)
        img_3 = self.create_image(3)

        if self.apply_dirt_effect.get():
            img_1 = self.apply_dirted_effect(img_1)
            img_2 = self.apply_dirted_effect(img_2)
            img_3 = self.apply_dirted_effect(img_3)
            
        # Corrected indentation for wear effect
        if self.apply_wear_effect_var.get():
            img_1 = self.apply_wear_effect(img_1)
            img_2 = self.apply_wear_effect(img_2)
            img_3 = self.apply_wear_effect(img_3)

        # Convert images to PhotoImage
        img_1_tk = ImageTk.PhotoImage(img_1)
        img_2_tk = ImageTk.PhotoImage(img_2)
        img_3_tk = ImageTk.PhotoImage(img_3)
        
        self.canvas.create_image(0, 0, anchor=tk.NW, image=img_1_tk)
        self.canvas.create_image(256 + 10, 0, anchor=tk.NW, image=img_2_tk)
        self.canvas.create_image((256 + 10) * 2, 0, anchor=tk.NW, image=img_3_tk)

        # Keep references to avoid garbage collection
        self.canvas.image1 = img_1_tk
        self.canvas.image2 = img_2_tk
        self.canvas.image3 = img_3_tk

    def create_image(self, columns):
        if self.use_transparent_bg.get():
            img = Image.new("RGBA", (256, 256), (0, 0, 0, 0))
        else:
            img = Image.new("RGBA", (256, 256), self.bg_color)

        if self.use_bg_image.get() and self.bg_image_path and os.path.exists(self.bg_image_path):
            bg_image = Image.open(self.bg_image_path).resize(img.size, Image.LANCZOS)
            img.paste(bg_image, (0, 0))

        draw = ImageDraw.Draw(img)

        column_width = img.width // 3
        centers = [(column_width // 2), (img.width // 2), (img.width - (column_width // 2))]

        for i in range(columns):
            x_center = centers[i]
            self.draw_stripe(draw, x_center, img.height)

            # Draw image if selected, otherwise draw shape
            if self.enable_image.get() and self.shape_image_path and os.path.exists(self.shape_image_path):
                self.draw_shape_image(img, x_center, img.height // 2)
            elif self.shape_type.get() != "No Shape":
                self.draw_shape(draw, x_center, img.height // 2)

        return img

    def draw_stripe(self, draw, x_center, canvas_height):
        stripe_height = self.stripe_height.get()
        stripe_width = self.stripe_width.get()
        border_size = self.stripe_border_size.get()

        x1 = x_center - stripe_width // 2
        x2 = x_center + stripe_width // 2
        y1 = canvas_height // 2 - stripe_height // 2
        y2 = canvas_height // 2 + stripe_height // 2

        draw.rectangle([x1, y1, x2, y2], fill=self.stripe_color,
                       outline=self.stripe_border_color, width=border_size)


    def draw_shape(self, draw, x_center, y_center):
        size = self.shape_size.get()
        shape = self.shape_type.get()
        border_size = self.shape_border_size.get()

        if shape == "Circle":
            draw.ellipse([x_center - size, y_center - size, x_center + size, y_center + size],
                         fill=self.shape_color, outline=self.shape_border_color, width=border_size)
        elif shape == "Square":
            draw.rectangle([x_center - size, y_center - size, x_center + size, y_center + size],
                           fill=self.shape_color, outline=self.shape_border_color, width=border_size)
        elif shape == "Ring":
            outer_radius = size
            inner_radius = size // 2
            
            # Draw outer circle
            draw.ellipse([x_center - outer_radius, y_center - outer_radius, 
                           x_center + outer_radius, y_center + outer_radius],
                         fill=self.shape_color, outline=self.shape_border_color, width=border_size)

            # To create the effect of a ring, draw the inner circle with the background color
            # Ensure the background color is the same as the canvas to make it transparent
            draw.ellipse([x_center - inner_radius, y_center - inner_radius, 
                           x_center + inner_radius, y_center + inner_radius],
                         fill=self.bg_color)  # This will "cut out" the center

        elif shape == "Diamond":
            points = [(x_center, y_center - size), (x_center + size, y_center), 
                      (x_center, y_center + size), (x_center - size, y_center)]
            draw.polygon(points, fill=self.shape_color, outline=self.shape_border_color, width=border_size)
        elif "Point Star" in shape:
            points = int(shape.split("-Point Star")[0])
            self.draw_star(draw, x_center, y_center, size, points, self.shape_color, self.shape_border_color, border_size)
        else:
            print(f"Shape '{shape}' is not recognized or not implemented.")


    def draw_shape_image(self, img, x_center, y_center):
        """Draw the shape image at the center of the column."""
        if self.shape_image_path and os.path.exists(self.shape_image_path):
            shape_image = Image.open(self.shape_image_path).convert("RGBA")
            original_width, original_height = shape_image.size

            if self.retain_aspect_ratio.get():
                # Calculate the new size while retaining aspect ratio
                aspect_ratio = original_width / original_height
                new_size = (self.shape_size.get() * 2, int((self.shape_size.get() * 2) / aspect_ratio))
            else:
                new_size = (self.shape_size.get() * 2, self.shape_size.get() * 2)

            shape_image = shape_image.resize(new_size, Image.LANCZOS)
            x_pos = x_center - shape_image.width // 2
            y_pos = y_center - shape_image.height // 2
            img.paste(shape_image, (x_pos, y_pos), shape_image)  # Use paste with mask

    def draw_star(self, draw, cx, cy, radius, points, color, border_color, border_size):
        outer_radius = radius
        inner_radius = radius / 2
        angle_offset = -math.pi / 2
        angle = 2 * math.pi / (points * 2)
        star_points = []
        for i in range(points * 2):
            r = outer_radius if i % 2 == 0 else inner_radius
            x = cx + r * math.cos(i * angle + angle_offset)
            y = cy + r * math.sin(i * angle + angle_offset)
            star_points.append((x, y))
        draw.polygon(star_points, fill=color, outline=border_color, width=border_size)

    def apply_dirted_effect(self, img):
        intensity = self.dirt_intensity.get()
        
        if self.texture_image_path and os.path.exists(self.texture_image_path):
            texture = Image.open(self.texture_image_path).convert("RGBA")
            texture = texture.resize(img.size, Image.LANCZOS)
            texture = texture.point(lambda p: p * (intensity / 100))
            img = Image.alpha_composite(img.convert("RGBA"), texture)
        
        return img
        
    def choose_wear_texture(self):
        """Open a file dialog to select a wear texture image."""
        self.wear_texture_path = filedialog.askopenfilename(
            title="Select Wear Texture Image",
            filetypes=[("Image Files", "*.png")]
        )
        self.generate_previews()  # Regenerate previews after choosing a new image

    def apply_wear_effect(self, img):
        intensity = self.wear_intensity.get()

        # Load the wear texture if it exists
        if self.wear_texture_path and os.path.exists(self.wear_texture_path):
            wear_texture = Image.open(self.wear_texture_path).convert("RGBA")
            wear_texture = wear_texture.resize(img.size, Image.LANCZOS)

            # Ensure the original image is also in RGBA format
            img = img.convert("RGBA")

            # Create a new image for the final output
            output_img = Image.new("RGBA", img.size)

            for x in range(img.width):
                for y in range(img.height):
                    # Get pixel values from both images
                    img_pixel = img.getpixel((x, y))
                    wear_pixel = wear_texture.getpixel((x, y))

                    # Use the wear texture's alpha to determine transparency
                    wear_alpha = wear_pixel[3]  # Get alpha from wear texture
                    wear_intensity_adjusted = int(wear_alpha * (intensity / 100))  # Scale alpha based on intensity

                    # Calculate new alpha value for the blended pixel
                    new_alpha = max(0, img_pixel[3] - wear_intensity_adjusted)

                    # Create new pixel with the blended values
                    blended_pixel = (
                        img_pixel[0],  # Keep original color values
                        img_pixel[1],
                        img_pixel[2],
                        new_alpha  # Apply new alpha value
                    )

                    output_img.putpixel((x, y), blended_pixel)

            return output_img

        return img
        
    def save_images(self):
        nations = ["china", "czech", "france_uk", "germany", "italian", "japan", "poland", "sweden", "usa", "ussr"]

        base_file_name = filedialog.asksaveasfilename(defaultextension=".wotmod", 
                                                       title="Save as", 
                                                       filetypes=[("WOTMOD files", "*.wotmod")])
        if not base_file_name:
            return

        base_file_name = os.path.join(os.path.dirname(base_file_name), f"MOE.{os.path.basename(base_file_name)}")

        output_dir = os.path.join("res", "gui", "maps", "vehicles", "decals", "insignia")
        os.makedirs(output_dir, exist_ok=True)

        for nation in nations:
            for i in range(1, 4):
                img = self.create_image(i)  # Create the image
                
                # Apply distress effects before saving
                if self.apply_dirt_effect.get():
                    img = self.apply_dirted_effect(img)
                if self.apply_wear_effect_var.get():
                    img = self.apply_wear_effect(img)

                img = img.convert("RGBA")  # Ensure the image is in RGBA format

                dds_filename = f"gun_{nation}_{i}.dds"
                dds_path = os.path.join(output_dir, dds_filename)

                try:
                    img.save(dds_path, format='DDS', compression='DXT5', options={'DXGI_FORMAT': 'BC3_UNORM_SRGB'})

                    print(f"Saved: {dds_path}")
                except Exception as e:
                    print(f"Error saving {dds_filename}: {e}")

        self.compress_to_wotmod(output_dir, base_file_name)

    def compress_to_wotmod(self, output_dir, base_file_name):
        """Compress the saved images into a WOTMOD file, preserving folder structure."""
        # Create the full path for the folder structure
        base_folder = os.path.dirname(base_file_name)
        full_output_dir = os.path.join(base_folder, 'res', 'gui', 'maps', 'vehicles', 'decals', 'insignia')

        with zipfile.ZipFile(base_file_name, 'w', compression=zipfile.ZIP_STORED) as wotmod_file:
            # Walk through the output directory and add files to the zip
            for foldername, subfolders, filenames in os.walk(full_output_dir):
                for filename in filenames:
                    file_path = os.path.join(foldername, filename)
                    # Set arcname to maintain the full folder structure
                    arcname = os.path.relpath(file_path, start=base_folder)
                    wotmod_file.write(file_path, arcname)

        print(f"Compressed files into {base_file_name}")

    # Delete the 'res' folder after creating the mod
    if os.path.exists("res"):
        shutil.rmtree("res")
        print("Deleted the 'res' folder.")
        
if __name__ == "__main__":
    root = tk.Tk()
    app = ImagePreviewGenerator(root)
    root.mainloop()
