"""
Description: Create a new Track Starter or Blank Rip
Category: Create
Shortcut:
Level: Intermediate
Version: 1.04
Copyright: (c) Hit'n'Mix Ltd 2025
Author:    Hit'n'Mix
"""

import random
from tkinter import StringVar
from PIL import Image, ImageTk


def ripscript():
	# Define UI Lists (Display Name, ID)
	# Full mapping of enums provided
	styles = [
		("Pop", STYLE_POP),
		("R&B", STYLE_RNB),
		("Indie", STYLE_INDIE),
		("Rock", STYLE_ROCK),
		("Hip-Hop", STYLE_HIPHOP),
		("Trap", STYLE_TRAP),
		("Reggaeton", STYLE_REGGAETON),
		("House", STYLE_HOUSE),
		("Techno", STYLE_TECHNO),
		("Drum & Bass", STYLE_DRUMANDBASS),
		("Electro", STYLE_ELECTRO),
		("Breakbeat", STYLE_BREAKBEAT),
		("Synthwave", STYLE_SYNTHWAVE),
		("Afrobeats", STYLE_AFROBEATS),
		("Downtempo", STYLE_DOWNTEMPO),
		("Ambient", STYLE_AMBIENT),
		("Lo-Fi", STYLE_LOFI),
		("Cinematic", STYLE_CINEMATIC),
		("Experimental", STYLE_EXPERIMENTAL),
		("Classical", STYLE_CLASSICAL),
		("Funk", STYLE_FUNK),
		("Soul", STYLE_SOUL),
		("Jazz", STYLE_JAZZ),
		("Disco", STYLE_DISCO),
		("Reggae / Dub", STYLE_REGGAEDUB),
		("World Music", STYLE_WORLDMUSIC),
		("Metal", STYLE_METAL),
		("Punk", STYLE_PUNK)
	]
	
	moods = [
		("Happy", MOOD_HAPPY),
		("Funky", MOOD_FUNKY),
		("Soulful", MOOD_SOULFUL),
		("Sad", MOOD_SAD),
		("Moody", MOOD_MOODY),
		("Epic", MOOD_EPIC),
		("Dreamy", MOOD_DREAMY),
		("Jazzy", MOOD_JAZZY),
		("Chill", MOOD_CHILL),
		("Intense", MOOD_INTENSE)
	]
		
	types = [
		("Chords", TRACKSTARTERTYPE_CHORDS),
		("Rhythm", TRACKSTARTERTYPE_RHYTHM),
		("Full", TRACKSTARTERTYPE_BOTH)
	]

	# Helpers to find list index from Enum ID
	def get_style_index(style_id):
		for i, s in enumerate(styles):
			if s[1] == style_id: return i
		return 0

	def get_mood_index(mood_id):
		for i, m in enumerate(moods):
			if m[1] == mood_id: return i
		return 0

	def get_type_index(type_id):
		for i, t in enumerate(types):
			if t[1] == type_id: return i
		return 0

	# History Management
	# Stores tuples: (style_id, mood_id, seed)
	history = [] 
	history_index = -1 

	# Create Window
	window = ripx.add_tk_window(sticky='NSWE')
	pad_x = 8 * pixel_scale()
	pad_y = 6 * pixel_scale()

	# Settings & Defaults
	settings = Settings()
	# Default to Pop / Happy if not set
	settings.add("style_name", styles[0][0]) # Pop
	settings.add("mood_name", moods[1][0]) # Funky
	settings.add("type_name", types[2][0]) # Full

	# --- UI Layout ---

	# Pickers Frame
	pickers_frame = window.add_frame()
	pickers_frame.grid(column=0, row=1, padx=pad_x, pady=pad_y, sticky="ew")

	# Style Picker
	pickers_frame.add_label(text="Style").grid(column=0, row=0, padx=pad_x, pady=pad_y, sticky="e")
	style_names = [s[0] for s in styles]
	style_combo = pickers_frame.add_combo_box(values=style_names, textvariable=style_name, state="readonly")
	style_combo.grid(column=1, row=0, padx=pad_x, pady=pad_y, sticky="ew")
	def style_selected(event: Event):
		style_combo.selection_clear() # To prevent selected text that shouldn't apply to read-only combo
		#on_create_starter() # Trigger creation when style changes
	style_combo.bind("<<ComboboxSelected>>", style_selected)
	
	# Mood Picker
	pickers_frame.add_label(text="Mood").grid(column=0, row=1, padx=pad_x, pady=pad_y, sticky="e")
	mood_names = [m[0] for m in moods]
	mood_combo = pickers_frame.add_combo_box(values=mood_names, textvariable=mood_name, state="readonly")
	mood_combo.grid(column=1, row=1, padx=pad_x, pady=pad_y, sticky="ew")
	def mood_selected(event: Event):
		mood_combo.selection_clear() # To prevent selected text that shouldn't apply to read-only combo
		#on_create_starter() # Trigger creation when style changes
	mood_combo.bind("<<ComboboxSelected>>", mood_selected)
	
	# Type Picker
	pickers_frame.add_label(text="Type").grid(column=0, row=2, padx=pad_x, pady=pad_y, sticky="e")
	type_names = [t[0] for t in types]
	type_combo = pickers_frame.add_combo_box(values=type_names, textvariable=type_name, state="readonly")
	type_combo.grid(column=1, row=2, padx=pad_x, pady=pad_y, sticky="ew")
	def type_selected(event: Event):
		type_combo.selection_clear() # To prevent selected text that shouldn't apply to read-only combo
		#on_create_starter() # Trigger creation when style changes
	type_combo.bind("<<ComboboxSelected>>", type_selected)

	pickers_frame.columnconfigure(1, weight=1)

	# 3. Navigation & Randomise Frame
	
	# Load button images into global variables to ensure not garbage collected and removed
	global photos
	photos = []
	def add_image(filename):
		global photos
		im = Image.open(ripscript_path(filename))
		scale = pixel_scale() * 0.25 # We are provided x2 sized bitmaps
		im = im.resize((int(im.width * scale), int(im.height * scale)), Image.ANTIALIAS)
		photos.append(ImageTk.PhotoImage(im))
	add_image("prev_x2.png")
	add_image("rand_x2.png")
	add_image("next_x2.png")
	
	nav_frame = window.add_frame()
	nav_frame.grid(column=0, row=2, padx=pad_x, pady=pad_y)

	btn_prev = nav_frame.add_button(image=photos[0])
	btn_prev.grid(column=0, row=0, padx=pad_x, pady=0)
	
	btn_random = nav_frame.add_button(image=photos[1])
	btn_random.grid(column=1, row=0, padx=pad_x, pady=0)
	
	btn_next = nav_frame.add_button(image=photos[2])
	btn_next.grid(column=2, row=0, padx=pad_x, pady=0)

	# 4. Action Buttons Frame
	action_frame = window.add_frame()
	action_frame.grid(column=0, row=3, padx=pad_x, pady=pad_y, sticky="ew")

	btn_blank = action_frame.add_button(text=" Create Blank ")
	btn_blank.grid(column=0, row=0, padx=pad_x, pady=pad_y, sticky="w")
	
	btn_create = action_frame.add_button(text=" Create ")
	btn_create.grid(column=1, row=0, padx=pad_x, pady=pad_y, sticky="e")
	
	# Make buttons expand evenly
	action_frame.columnconfigure(0, weight=1)
	action_frame.columnconfigure(1, weight=1)

	# --- Logic Functions ---

	def update_nav_state():
		# Enable/Disable Prev button
		if history_index <= 0:
			btn_prev.config(state='disabled')
		else:
			btn_prev.config(state='normal')
			
		# Enable/Disable Next button
		if history_index >= 0:
			btn_next.config(state='normal')
		else:
			btn_next.config(state='disabled')

	def generate_song(style_id, mood_id, type_id, seed, is_history_recall=False):
		nonlocal history_index
		
		# Call C++ function exposed via Python
		rip = ripx.riplist.new_track_starter(style=style_id, mood=mood_id, type=type_id, seed=seed)
		
		if rip:
			rip.play()
			
			if not is_history_recall:
				# Logic: If we were back in history and generated something NEW,
				# we remove the 'future' history and append the new one.
				if history_index < len(history) - 1:
					del history[history_index+1:]
				
				history.append((style_id, mood_id, type_id, seed))
				history_index = len(history) - 1

		update_nav_state()

	def on_create_starter(event=None):
		# Get current selection from Combo
		s_index = style_combo.current()
		m_index = mood_combo.current()
		t_index = type_combo.current()

		# Safety check
		if s_index < 0: s_index = 0
		if m_index < 0: m_index = 0
		if t_index < 0: t_index = 0

		chosen_style = styles[s_index][1]
		chosen_mood = moods[m_index][1]
		chosen_type = types[t_index][1]

		# Generate new 64-bit seed
		new_seed = random.getrandbits(64)
		
		# Execute
		generate_song(chosen_style, chosen_mood, chosen_type, new_seed)
		
		# Persist settings
		settings.save()
		
	def on_create_blank(event=None):
		# Create blank uses NONE/NONE/CHORDS/0
		rip = ripx.riplist.new_track_starter(style=STYLE_NONE, mood=MOOD_NONE, type=TRACKSTARTERTYPE_CHORDS, seed=0)

		# Close window
		window.winfo_toplevel().destroy()

	def on_randomise(event=None):
		# Pick random indices from our lists
		r_style_idx = random.randint(0, len(styles) - 1)
		r_mood_idx = random.randint(0, len(moods) - 1)
		
		# Update UI selection
		style_combo.current(r_style_idx)
		mood_combo.current(r_mood_idx)
		
		# Trigger creation immediately
		on_create_starter()

	def on_prev(event=None):
		nonlocal history_index
		if history_index > 0:
			history_index -= 1
			item = history[history_index]
			style_id, mood_id, type_id, seed = item
			
			# Sync UI
			s_idx = get_style_index(style_id)
			m_idx = get_mood_index(mood_id)
			t_idx = get_type_index(type_id)
			style_combo.current(s_idx)
			mood_combo.current(m_idx)
			type_combo.current(t_idx)
		
			generate_song(style_id, mood_id, type_id, seed, is_history_recall=True)

	def on_next(event=None):
		nonlocal history_index
		
		# Case 1: Move forward in existing history
		if history_index < len(history) - 1:
			history_index += 1
			item = history[history_index]
			style_id, mood_id, type_id, seed = item
			
			# Sync UI
			s_idx = get_style_index(style_id)
			m_idx = get_mood_index(mood_id)
			t_idx = get_type_index(type_id)
			style_combo.current(s_idx)
			mood_combo.current(m_idx)
			type_combo.current(t_idx)
		
			generate_song(style_id, mood_id, type_id, seed, is_history_recall=True)
		
		# Case 2: We are at the latest history item, so "Next" acts as "Generate New"
		elif history_index >= 0:
			on_create_starter()

	def on_close(event=None):
		window.winfo_toplevel().destroy()

	# --- Bindings ---
	
	btn_create.bind("<Button-1>", on_create_starter)
	btn_blank.bind("<Button-1>", on_create_blank)
	btn_random.bind("<Button-1>", on_randomise)
	btn_prev.bind("<Button-1>", on_prev)
	btn_next.bind("<Button-1>", on_next)
	
	# Initialize nav state
	update_nav_state()
	
	# Also press Enter to Create
	window.winfo_toplevel().bind("<Key-Return>", on_create_starter)
	#window.winfo_toplevel().bind("<Key-Left>", on_prev)
	#window.winfo_toplevel().bind("<Key-Right>", on_next)
	#window.winfo_toplevel().bind("<space>", on_randomise)	# Too easily confused with play start/stop
	# Allow ESC to close
	window.winfo_toplevel().bind("<Key-Escape>", on_close)
	