import gradio as gr
import requests
import time
import json
import base64
import os
from io import BytesIO
import html
import re
from deep_translator import GoogleTranslator
from langdetect import detect



class Prodia:
    def __init__(self, api_key, base=None):
        self.base = base or "https://api.prodia.com/v1"
        self.headers = {
            "X-Prodia-Key": api_key
        }
    
    def generate(self, params):
        response = self._post(f"{self.base}/sd/generate", params)
        return response.json()
    
    def transform(self, params):
        response = self._post(f"{self.base}/sd/transform", params)
        return response.json()
    
    def controlnet(self, params):
        response = self._post(f"{self.base}/sd/controlnet", params)
        return response.json()
    
    def get_job(self, job_id):
        response = self._get(f"{self.base}/job/{job_id}")
        return response.json()

    def wait(self, job):
        job_result = job

        while job_result['status'] not in ['succeeded', 'failed']:
            time.sleep(0.25)
            job_result = self.get_job(job['job'])

        return job_result

    def list_models(self):
        response = self._get(f"{self.base}/sd/models")
        return response.json()

    def list_samplers(self):
        response = self._get(f"{self.base}/sd/samplers")
        return response.json()

    def _post(self, url, params):
        headers = {
            **self.headers,
            "Content-Type": "application/json"
        }
        response = requests.post(url, headers=headers, data=json.dumps(params))

        if response.status_code != 200:
            raise Exception(f"Bad Prodia Response: {response.status_code}")

        return response

    def _get(self, url):
        response = requests.get(url, headers=self.headers)

        if response.status_code != 200:
            raise Exception(f"Bad Prodia Response: {response.status_code}")

        return response


def image_to_base64(image):
    # Convert the image to bytes
    buffered = BytesIO()
    image.save(buffered, format="PNG")  # You can change format to PNG if needed
    
    # Encode the bytes to base64
    img_str = base64.b64encode(buffered.getvalue())

    return img_str.decode('utf-8')  # Convert bytes to string


def remove_id_and_ext(text):
    text = re.sub(r'\[.*\]$', '', text)
    extension = text[-12:].strip()
    if extension == "safetensors":
        text = text[:-13]
    elif extension == "ckpt":
        text = text[:-4]
    return text


def get_data(text):
    results = {}
    patterns = {
        'prompt': r'(.*)',
        'negative_prompt': r'Negative prompt: (.*)',
        'steps': r'Steps: (\d+),',
        'seed': r'Seed: (\d+),',
        'sampler': r'Sampler:\s*([^\s,]+(?:\s+[^\s,]+)*)', 
        'model': r'Model:\s*([^\s,]+)',
        'cfg_scale': r'CFG scale:\s*([\d\.]+)',
        'size': r'Size:\s*([0-9]+x[0-9]+)'
        }
    for key in ['prompt', 'negative_prompt', 'steps', 'seed', 'sampler', 'model', 'cfg_scale', 'size']:
        match = re.search(patterns[key], text)
        if match:
            results[key] = match.group(1)
        else:
            results[key] = None
    if results['size'] is not None:
        w, h = results['size'].split("x")
        results['w'] = w
        results['h'] = h
    else:
        results['w'] = None
        results['h'] = None
    return results


def send_to_txt2img(image):

    result = {tabs: gr.update(selected="t2i")}

    try:
        text = image.info['parameters']
        data = get_data(text)
        result[prompt] = gr.update(value=data['prompt'])
        result[negative_prompt] = gr.update(value=data['negative_prompt']) if data['negative_prompt'] is not None else gr.update()
        result[steps] = gr.update(value=int(data['steps'])) if data['steps'] is not None else gr.update()
        result[seed] = gr.update(value=int(data['seed'])) if data['seed'] is not None else gr.update()
        result[cfg_scale] = gr.update(value=float(data['cfg_scale'])) if data['cfg_scale'] is not None else gr.update()
        result[width] = gr.update(value=int(data['w'])) if data['w'] is not None else gr.update()
        result[height] = gr.update(value=int(data['h'])) if data['h'] is not None else gr.update()
        result[sampler] = gr.update(value=data['sampler']) if data['sampler'] is not None else gr.update()
        if model in model_names:
            result[model] = gr.update(value=model_names[model])
        else:
            result[model] = gr.update()
        return result

    except Exception as e:
        print(e)

        return result


prodia_client = Prodia(api_key=os.getenv("PRODIA_API_KEY"))
model_list = prodia_client.list_models()
model_names = {}

for model_name in model_list:
    name_without_ext = remove_id_and_ext(model_name)
    model_names[name_without_ext] = model_name


def txt2img(prompt, negative_prompt, model, steps, sampler, cfg_scale, width, height, seed):
    language = detect(prompt)
    
    if language == 'ru':
        prompt = GoogleTranslator(source='ru', target='en').translate(prompt)
        print(prompt)
    
    result = prodia_client.generate({
        "prompt": prompt,
        "negative_prompt": negative_prompt,
        "model": model,
        "steps": steps,
        "sampler": sampler,
        "cfg_scale": cfg_scale,
        "width": width,
        "height": height,
        "seed": seed
    })

    job = prodia_client.wait(result)

    return job["imageUrl"]


def img2img(input_image, denoising, prompt, negative_prompt, model, steps, sampler, cfg_scale, width, height, seed):
    result = prodia_client.transform({
        "imageData": image_to_base64(input_image),
        "denoising_strength": denoising,
        "prompt": prompt,
        "negative_prompt": negative_prompt,
        "model": model,
        "steps": steps,
        "sampler": sampler,
        "cfg_scale": cfg_scale,
        "width": width,
        "height": height,
        "seed": seed
    })

    job = prodia_client.wait(result)

    return job["imageUrl"]


# Ссылка на файл CSS
css_url = "https://neurixyufi-aihub.static.hf.space/style.css"

# Получение CSS по ссылке
response = requests.get(css_url)
css = response.text + " .gradio-container{max-width: 700px !important} h1{text-align:center}"

 
with gr.Blocks(css=css) as demo:
    gr.Markdown("# Stable Diffusion")
    with gr.Row():
        with gr.Accordion(label="Модель", open=False):
            model = gr.Radio(interactive=True, value="absolutereality_v181.safetensors [3d9d4d2b]", show_label=False, choices=prodia_client.list_models())

    with gr.Tabs() as tabs:
        
        with gr.Tab("txt2img", id='t2i'):
                    
            with gr.Row():
                with gr.Column(scale=3):
                    with gr.Tab("Основные настройки"):
                        with gr.Column(scale=6, min_width=600):
                            prompt = gr.Textbox("", placeholder="Prompt", show_label=False, lines=3)
                            negative_prompt = gr.Textbox(placeholder="Negative Prompt", show_label=False, lines=3, value="[deformed | disfigured], poorly drawn, [bad : wrong] anatomy, [extra | missing | floating | disconnected] limb, (mutated hands and fingers), blurry")
                                        
                        with gr.Row():
                            with gr.Column(scale=1):
                                steps = gr.Slider(label="Sampling Steps", minimum=1, maximum=50, value=30, step=1)
    
                        with gr.Row():
                            with gr.Column(scale=1):
                                width = gr.Slider(label="Ширина", minimum=15, maximum=1024, value=512, step=8)
                                height = gr.Slider(label="Длина", minimum=15, maximum=1024, value=512, step=8)
                            
                    with gr.Tab("Расширенные настройки"):
                        with gr.Row():
                            with gr.Column(scale=1):
                                sampler = gr.Dropdown(value="DPM++ 2M Karras", show_label=True, label="Sampling Method", choices=prodia_client.list_samplers())
                            
                        cfg_scale = gr.Slider(label="CFG Scale", minimum=1, maximum=20, value=7, step=1)
                        seed = gr.Slider(label="Seed", minimum=-1, maximum=10000000, value=-1)
                    text_button = gr.Button("Генерация", variant='primary', elem_id="generate")
                    image_output = gr.Image()
    
            text_button.click(txt2img, inputs=[prompt, negative_prompt, model, steps, sampler, cfg_scale, width, height, seed], outputs=image_output)
        
        with gr.Tab("img2img", id='i2i'):
            with gr.Row():
                with gr.Column(scale=3):
                    with gr.Tab("Основные настройки"):
                        i2i_image_input = gr.Image(type="pil")
                        with gr.Column(scale=6, min_width=600):
                            i2i_prompt = gr.Textbox("", placeholder="Prompt", show_label=False, lines=3)
                            i2i_negative_prompt = gr.Textbox(placeholder="Negative Prompt", show_label=False, lines=3, value="[deformed | disfigured], poorly drawn, [bad : wrong] anatomy, [extra | missing | floating | disconnected] limb, (mutated hands and fingers), blurry")
                            
                        with gr.Row():
                                
                            with gr.Column(scale=1):
                                i2i_steps = gr.Slider(label="Sampling Steps", minimum=1, maximum=50, value=30, step=1)
    
                        with gr.Row():
                            with gr.Column(scale=1):
                                i2i_width = gr.Slider(label="Ширина", minimum=15, maximum=1024, value=512, step=8)
                                i2i_height = gr.Slider(label="Высота", minimum=15, maximum=1024, value=512, step=8)
                            
    
                    with gr.Tab("Расширенные настройки"):

                        with gr.Row():
                            with gr.Column(scale=1):
                                i2i_sampler = gr.Dropdown(value="DPM++ 2M Karras", show_label=True, label="Sampling Method", choices=prodia_client.list_samplers())
                                
                           
                        i2i_cfg_scale = gr.Slider(label="CFG Scale", minimum=1, maximum=20, value=7, step=1)
                        i2i_denoising = gr.Slider(label="Схожесть с оригиналом", minimum=0, maximum=1, value=0.7, step=0.1)
                        i2i_seed = gr.Slider(label="Seed", minimum=-1, maximum=10000000, value=-1)

                
                    i2i_text_button = gr.Button("Генерация", variant='primary', elem_id="generate")
                    i2i_image_output = gr.Image()

            i2i_text_button.click(img2img, inputs=[i2i_image_input, i2i_denoising, i2i_prompt, i2i_negative_prompt, model, i2i_steps, i2i_sampler, i2i_cfg_scale, i2i_width, i2i_height, i2i_seed], outputs=i2i_image_output)
demo.queue(concurrency_count=512, max_size=512, api_open=False).launch(max_threads=256)