#!/usr/bin/env python3

import tkinter as tk
from tkinter import filedialog, messagebox
import subprocess
import os

class MediaEditor:
    def __init__(self, root):
        self.root = root
        self.caminho_midia = tk.StringVar()
        self.setup_ui()

    def setup_ui(self):
        self.root.title("CGS Editor - Vídeo com Margens")
        self.root.geometry("500x500")

        main_frame = tk.Frame(self.root, padx=5, pady=5)
        main_frame.pack(fill=tk.BOTH, expand=True)

        # Seleção de arquivo
        file_frame = tk.Frame(main_frame)
        file_frame.pack(fill=tk.X, pady=2)
        tk.Label(file_frame, text="Arquivo:").pack(side=tk.LEFT)
        tk.Entry(file_frame, textvariable=self.caminho_midia, width=40).pack(side=tk.LEFT, padx=2)
        tk.Button(file_frame, text="Abrir", command=self.escolher_arquivo, width=4).pack(side=tk.LEFT)

        # Duração
        self.label_duracao = tk.Label(main_frame, text="Duração: --:--:--.--", font=('Arial', 10))
        self.label_duracao.pack(pady=2)

        # Frames de operações
        ops_main_frame = tk.Frame(main_frame)
        ops_main_frame.pack(fill=tk.BOTH, expand=True)

        # Lado esquerdo: corte, rotação e flip
        left_frame = tk.Frame(ops_main_frame)
        left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=2)

        # Corte
        cut_frame = tk.LabelFrame(left_frame, text="Corte de tempo", font=('Arial', 10, 'bold'), padx=5, pady=5)
        cut_frame.pack(fill=tk.BOTH, expand=True, pady=2)
        linha_tempos = tk.Frame(cut_frame)
        linha_tempos.pack(fill=tk.X, pady=2)

        tk.Label(linha_tempos, text="Início:", width=5, anchor='e', font=('Arial', 10)).pack(side=tk.LEFT)
        self.entrada_inicio = tk.Entry(linha_tempos, width=10, font=('Arial', 10))
        self.entrada_inicio.insert(0, "00:00:00.00")
        self.entrada_inicio.pack(side=tk.LEFT, padx=2)

        tk.Label(linha_tempos, text="Duração:", width=8, anchor='e', font=('Arial', 10)).pack(side=tk.LEFT)
        self.entrada_duracao = tk.Entry(linha_tempos, width=10, font=('Arial', 10))
        self.entrada_duracao.insert(0, "00:00:10.00")
        self.entrada_duracao.pack(side=tk.LEFT, padx=2)

        tk.Button(cut_frame, text="Cortar", command=self.cortar_midia, height=1).pack(pady=2, fill=tk.X)

        # Transformações: rotação e flip
        transform_frame = tk.LabelFrame(left_frame, text="Transformações", font=('Arial', 10, 'bold'), padx=5, pady=5)
        transform_frame.pack(fill=tk.BOTH, expand=True, pady=2)

        rotate_frame = tk.Frame(transform_frame)
        rotate_frame.pack(fill=tk.X, pady=1)
        tk.Label(rotate_frame, text="Rotação:", width=8, anchor='e').pack(side=tk.LEFT)
        self.rotacao_var = tk.StringVar(value="0")
        for val in ["0","90","180","-90"]:
            tk.Radiobutton(rotate_frame, text=val+"°", variable=self.rotacao_var, value=val, font=('Arial',10)).pack(side=tk.LEFT)

        flip_frame = tk.Frame(transform_frame)
        flip_frame.pack(fill=tk.X, pady=1)
        tk.Label(flip_frame, text="Espelhar:", width=8, anchor='e').pack(side=tk.LEFT)
        self.flip_var = tk.StringVar(value="none")
        for val, txt in [("none","Nenhum"),("horizontal","H"),("vertical","V")]:
            tk.Radiobutton(flip_frame, text=txt, variable=self.flip_var, value=val, font=('Arial',10)).pack(side=tk.LEFT)

        # Lado direito: crop e margens
        right_frame = tk.LabelFrame(ops_main_frame, text="Corte e Margens", font=('Arial',10,'bold'), padx=5, pady=5)
        right_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=2)

        # Campos de crop/margens
        self.entrada_left = self.criar_campo(right_frame, "Esquerda:", "0")
        self.entrada_right = self.criar_campo(right_frame, "Direita:", "0")
        self.entrada_top = self.criar_campo(right_frame, "Superior:", "0")
        self.entrada_bottom = self.criar_campo(right_frame, "Inferior:", "0")

        # Campos para adicionar margem/padding
        self.pad_left = self.criar_campo(right_frame, "Margem Esq:", "0")
        self.pad_right = self.criar_campo(right_frame, "Margem Dir:", "0")
        self.pad_top = self.criar_campo(right_frame, "Margem Sup:", "0")
        self.pad_bottom = self.criar_campo(right_frame, "Margem Inf:", "0")
        tk.Label(right_frame, text="Cor da Margem:").pack()
        self.pad_cor = tk.Entry(right_frame, width=10)
        self.pad_cor.insert(0,"white")
        self.pad_cor.pack()
        tk.Label(right_frame, text="Extensão saída:").pack()
        self.pad_ext = tk.Entry(right_frame, width=5)
        self.pad_ext.insert(0,"mp4")
        self.pad_ext.pack()

        tk.Button(right_frame, text="Aplicar Crop/Margem", command=self.aplicar_margens, height=1).pack(pady=2, fill=tk.X)

        # Botões inferiores
        btn_frame = tk.Frame(main_frame)
        btn_frame.pack(fill=tk.X, pady=3)
        tk.Button(btn_frame, text="Visualizar", command=self.visualizar_corte, height=1).pack(side=tk.LEFT, padx=2, fill=tk.X, expand=True)
        tk.Button(btn_frame, text="Aplicar Tudo", command=self.aplicar_tudo, height=1).pack(side=tk.LEFT, padx=2, fill=tk.X, expand=True)
        tk.Label(btn_frame, text="by Clayton GS").pack(side=tk.LEFT, padx=2, fill=tk.X, expand=True)

    def criar_campo(self, parent, texto, valor_padrao):
        frame = tk.Frame(parent)
        frame.pack(fill=tk.X, pady=1)
        tk.Label(frame, text=texto, width=10, anchor='e', font=('Arial',10)).pack(side=tk.LEFT)
        entry = tk.Entry(frame, width=10, font=('Arial',10))
        entry.insert(0, valor_padrao)
        entry.pack(side=tk.LEFT)
        return entry

    def escolher_arquivo(self):
        arquivo = filedialog.askopenfilename(
            filetypes=[("Mídia", "*.mp4 *.mov *.avi *.mkv *.jpg *.jpeg *.png *.bmp")]
        )
        if arquivo:
            self.caminho_midia.set(arquivo)
            if self.is_video_file(arquivo):
                self.mostrar_duracao_total(arquivo)
            else:
                self.label_duracao.config(text="Imagem: " + os.path.basename(arquivo))

    def is_video_file(self, filename):
        return os.path.splitext(filename)[1].lower() in ['.mp4','.mov','.avi','.mkv']

    def is_image_file(self, filename):
        return os.path.splitext(filename)[1].lower() in ['.jpg','.jpeg','.png','.bmp']

    def mostrar_duracao_total(self, arquivo):
        try:
            cmd = ["ffprobe","-v","error","-show_entries","format=duration","-of","default=nokey=1:noprint_wrappers=1", arquivo]
            result = subprocess.run(cmd, capture_output=True, text=True)
            segundos = float(result.stdout.strip())
            horas = int(segundos // 3600)
            minutos = int((segundos % 3600) // 60)
            segundos = int(segundos % 60)
            self.label_duracao.config(text=f"Duração: {horas:02}:{minutos:02}:{segundos:02}")
        except:
            self.label_duracao.config(text="Erro na duração")

    def executar_comando(self, comando, mensagem_sucesso, mensagem_erro):
        try:
            subprocess.run(comando, check=True)
            messagebox.showinfo("Sucesso", mensagem_sucesso)
            return True
        except subprocess.CalledProcessError as e:
            messagebox.showerror("Erro", f"{mensagem_erro}\nErro: {e}")
            return False

    # Função para adicionar margens (pad)
    def aplicar_margens(self):
        if not self.validar_entrada(): return
        entrada = self.caminho_midia.get()
        nome_base, _ = os.path.splitext(entrada)
        try:
            l = int(self.pad_left.get())
            r = int(self.pad_right.get())
            t = int(self.pad_top.get())
            b = int(self.pad_bottom.get())
            cor = self.pad_cor.get()
            ext = self.pad_ext.get().strip().lower() or "mp4"
            saida = f"{nome_base}_COM_MARGEM.{ext}"
            comando = ["ffmpeg","-i",entrada,"-vf",f"pad=iw+{l+r}:ih+{t+b}:{l}:{t}:{cor}","-c:a","copy",saida]
            subprocess.run(comando, check=True)
            messagebox.showinfo("Sucesso", f"Vídeo salvo: {saida}")
        except Exception as e:
            messagebox.showerror("Erro", f"Falha ao aplicar margem:\n{e}")

    def cortar_midia(self):    # Função para cortar vídeo ou imagem
        if not self.validar_entrada(): return  # Verifica se o arquivo foi selecionado
        
        entrada = self.caminho_midia.get()
        nome_base, extensao = os.path.splitext(entrada)  # Separa nome e extensão
        
        if self.is_image_file(entrada):
            # Para imagens, faz crop total da imagem (sem modificação real)
            resolucao = self.obter_resolucao(entrada)
            if not resolucao: return
            w, h = resolucao
            saida = f"{nome_base}_CUT{extensao}"  # Nome do arquivo de saída
            comando = [
                "ffmpeg", "-i", entrada,
                "-vf", f"crop={w}:{h}:0:0",  # Crop com mesmo tamanho da imagem
                saida
            ]
        else:
            # Para vídeos, corta trecho entre início e duração
            saida = f"{nome_base}_CUT{extensao}"
            comando = [
                "ffmpeg", "-ss", self.entrada_inicio.get(),  # Início
                "-i", entrada,
                "-t", self.entrada_duracao.get(),  # Duração
                "-c", "copy",  # Sem re-encodificação
                saida
            ]
        
        self.executar_comando(comando, f"Salvo como:\n{saida}", "Erro ao cortar")  # Executa e exibe resultado

    def cortar_margens(self):    # Aplica crop (recorte de margens)
        if not self.validar_entrada(): return
        
        entrada = self.caminho_midia.get()
        nome_base, extensao = os.path.splitext(entrada)
        resolucao = self.obter_resolucao(entrada)
        if not resolucao: return
        
        w, h = resolucao  # Largura e altura originais
        l = self.interpretar_valor(self.entrada_left.get(), w)  # Margem esquerda
        r = self.interpretar_valor(self.entrada_right.get(), w)  # Margem direita
        t = self.interpretar_valor(self.entrada_top.get(), h)  # Margem superior
        b = self.interpretar_valor(self.entrada_bottom.get(), h)  # Margem inferior
        
        nw, nh = w - l - r, h - t - b  # Novas dimensões
        if nw <= 0 or nh <= 0:
            messagebox.showerror("Erro", "Margens inválidas")
            return
        
        saida = f"{nome_base}_CROP{extensao}"  # Nome do novo arquivo
        comando = [
            "ffmpeg", "-i", entrada,
            "-filter:v", f"crop={nw}:{nh}:{l}:{t}",  # Comando de crop
            "-c:a", "copy", saida
        ]
        
        self.executar_comando(comando, f"Salvo como:\n{saida}", "Erro no crop")


    def aplicar_rotacao(self, entrada, saida):    # Aplica rotação no vídeo ou imagem
        graus = self.rotacao_var.get()  # Obtém o valor selecionado
        if graus == "0": return entrada  # Se for 0°, não faz nada
        
        if graus in ("90", "-90"):
            # Rotação 90° ou 270° (transpose)
            transpose = "1" if graus == "90" else "2"
            comando = [
                "ffmpeg", "-i", entrada,
                "-vf", f"transpose={transpose}",
                "-c:a", "copy", saida
            ]
        else:  # 180°
            comando = [
                "ffmpeg", "-i", entrada,
                "-vf", "hflip,vflip",  # Inverte horizontal e vertical
                "-c:a", "copy", saida
            ]
        
        if self.executar_comando(comando, "Rotação aplicada", "Erro na rotação"):
            return saida
        return entrada

    def aplicar_espelhamento(self, entrada, saida):    # Aplica espelhamento horizontal ou vertical
        tipo = self.flip_var.get()
        if tipo == "none": return entrada  # Sem espelhamento
        
        filtro = "hflip" if tipo == "horizontal" else "vflip"
        comando = [
            "ffmpeg", "-i", entrada,
            "-vf", filtro,
            "-c:a", "copy", saida
        ]
        
        if self.executar_comando(comando, "Espelhamento aplicado", "Erro no flip"):
            return saida
        return entrada

    def aplicar_tudo(self):    # Aplica crop, rotação e espelhamento em sequência
        if not self.validar_entrada(): return
        
        entrada = self.caminho_midia.get()
        nome_base, extensao = os.path.splitext(entrada)
        temp1 = f"{nome_base}_TEMP1{extensao}"  # Arquivo temporário 1
        temp2 = f"{nome_base}_TEMP2{extensao}"  # Arquivo temporário 2
        saida = f"{nome_base}_FINAL{extensao}"  # Arquivo final
        
        try: 
            resolucao = self.obter_resolucao(entrada)    # Aplica crop
            if resolucao:
                w, h = resolucao
                l = self.interpretar_valor(self.entrada_left.get(), w)
                r = self.interpretar_valor(self.entrada_right.get(), w)
                t = self.interpretar_valor(self.entrada_top.get(), h)
                b = self.interpretar_valor(self.entrada_bottom.get(), h)
                nw, nh = w - l - r, h - t - b
                
                if nw > 0 and nh > 0:
                    comando = [
                        "ffmpeg", "-i", entrada,
                        "-filter:v", f"crop={nw}:{nh}:{l}:{t}",
                        "-c:a", "copy", temp1
                    ]
                    subprocess.run(comando, check=True)
                    entrada = temp1
# Aplica rotação
            entrada = self.aplicar_rotacao(entrada, temp2)
            if entrada == temp2:
                temp1, temp2 = temp2, temp1  # Troca temporários
# Aplica espelhamento
            entrada = self.aplicar_espelhamento(entrada, saida)
# Remove arquivos temporários
            if os.path.exists(temp1): os.remove(temp1)
            if os.path.exists(temp2): os.remove(temp2)
            
            messagebox.showinfo("Sucesso", f"Processamento completo!\nSalvo como: {saida}")
        except Exception as e:
            messagebox.showerror("Erro", f"Falha no processamento:\n{e}")
            for temp in [temp1, temp2]:
                if os.path.exists(temp): os.remove(temp)
# Visualiza um trecho do vídeo com ffplay e o crop aplicado
    def visualizar_corte(self):
        if not self.validar_entrada(): return
        
        entrada = self.caminho_midia.get()
        if self.is_image_file(entrada):
            comando = ["ffplay", entrada]
        else:
            resolucao = self.obter_resolucao(entrada)
            if not resolucao: return
            
            w, h = resolucao
            l = self.interpretar_valor(self.entrada_left.get(), w)
            r = self.interpretar_valor(self.entrada_right.get(), w)
            t = self.interpretar_valor(self.entrada_top.get(), h)
            b = self.interpretar_valor(self.entrada_bottom.get(), h)
            nw, nh = w - l - r, h - t - b
            
            if nw <= 0 or nh <= 0:
                messagebox.showerror("Erro", "Margens inválidas")
                return
            
            comando = [
                "ffplay", "-ss", self.entrada_inicio.get(),
                "-t", self.entrada_duracao.get(), "-autoexit",
                "-vf", f"crop={nw}:{nh}:{l}:{t}", entrada
            ]
        
        try:
            subprocess.Popen(comando)  # Executa player em processo separado
        except Exception as e:
            messagebox.showerror("Erro", f"Erro na visualização:\n{e}")
# Usa ffprobe para obter resolução do vídeo/imagem
    def obter_resolucao(self, arquivo):
        try:
            cmd = [
                "ffprobe", "-v", "error",
                "-select_streams", "v:0" if self.is_video_file(arquivo) else "v",
                "-show_entries", "stream=width,height",
                "-of", "csv=s=x:p=0", arquivo
            ]
            result = subprocess.run(cmd, stdout=subprocess.PIPE, text=True)
            return tuple(map(int, result.stdout.strip().split('x')))  # Retorna (largura, altura)
        except:
            messagebox.showerror("Erro", "Falha ao obter resolução")
            return None
# Converte valor percentual ou absoluto para inteiro
    def interpretar_valor(self, valor, total):
        valor = valor.strip()
        if valor.endswith('%'):
            return int((float(valor[:-1]) / 100) * total)
        return int(valor) if valor else 0
# Verifica se o arquivo foi selecionado e existe
    def validar_entrada(self):
        entrada = self.caminho_midia.get()
        if not entrada:
            messagebox.showerror("Erro", "Nenhum arquivo selecionado")
            return False
        if not os.path.isfile(entrada):
            messagebox.showerror("Erro", "Arquivo não encontrado")
            return False
        return True

if __name__ == "__main__":	# Executa o programa se for o arquivo principal
    root = tk.Tk()  # Cria a janela principal
    app = MediaEditor(root)  # Instancia a aplicação
    root.mainloop()  # Inicia o loop da interface gráfica
