import os,platform
import re,random,json
from PIL import Image
import numpy as np
# FONT_PATH= os.path.abspath(os.path.join(os.path.dirname(__file__),'../assets/王汉宗颜楷体繁.ttf'))
import folder_paths
import matplotlib.font_manager as fm
import torch
import importlib.util

def create_incrementing_list(min_value, max_value, step, count):
    l1 = [int(min_value + i * step) for i in range(count) if min_value + i * step <= max_value]
    l2 = [float(min_value + i * step) for i in range(count) if min_value + i * step <= max_value]
    return (l1,l2)

def split_list(lst, chunk_size, transition_size):
    result = []
    for i in range(0, len(lst), chunk_size):
        start = i - transition_size
        end = i + chunk_size + transition_size
        result.append(lst[max(start, 0):end])
    return result

def recursive_search(directory, excluded_dir_names=None):
    if not os.path.isdir(directory):
        return [], {}

    if excluded_dir_names is None:
        excluded_dir_names = []

    result = []
    dirs = {directory: os.path.getmtime(directory)}
    for dirpath, subdirs, filenames in os.walk(directory, followlinks=True, topdown=True):
        subdirs[:] = [d for d in subdirs if d not in excluded_dir_names]
        for file_name in filenames:
            relative_path = os.path.relpath(os.path.join(dirpath, file_name), directory)
            result.append(relative_path)
        for d in subdirs:
            path = os.path.join(dirpath, d)
            dirs[path] = os.path.getmtime(path)
    return result, dirs

def filter_files_extensions(files, extensions):
    return sorted(list(filter(lambda a: os.path.splitext(a)[-1].lower() in extensions or len(extensions) == 0, files)))


def get_system_font_path():
    ps=[]
    system = platform.system()
    if system == "Windows":
        ps.append(os.path.join(os.environ["WINDIR"], "Fonts"))
    elif system == "Darwin":
        ps.append(os.path.join("/Library", "Fonts"))
    elif system == "Linux":
        ps.append(os.path.join("/usr", "share", "fonts"))
        ps.append(os.path.join("/usr", "local", "share", "fonts"))
    ps=[p for p in ps if os.path.exists(p)]
    file_paths=[]
    for f in ps:
        result, dirs=recursive_search(f)
        for r in result:
            file_paths.append(r)
    file_paths=filter_files_extensions(file_paths,[".otf", ".ttf"])

    return file_paths



# import json
# import hashlib


# def get_json_hash(json_content):
#     json_string = json.dumps(json_content, sort_keys=True)
#     hash_object = hashlib.sha256(json_string.encode())
#     hash_value = hash_object.hexdigest()
#     return hash_value
    


def tensor2pil(image):
    return Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8))


def create_temp_file(image,counter=1):
    output_dir = folder_paths.get_temp_directory()

    (
            full_output_folder,
            filename,
            _,
            subfolder,
            _,
        ) = folder_paths.get_save_image_path('tmp', output_dir)

    
    im=tensor2pil(image)
 
    image_file = f"{filename}_{counter:05}.png"
     
    image_path=os.path.join(full_output_folder, image_file)

    im.save(image_path,compress_level=4)

    return [{
                "filename": image_file,
                "subfolder": subfolder,
                "type": "temp"
                }]

def get_font_files(directory):
    font_files = {}

    # 从指定目录加载字体
    for file in os.listdir(directory):
        if file.endswith('.ttf') or file.endswith('.otf'):
            font_name = os.path.splitext(file)[0]
            font_path = os.path.join(directory, file)
            font_files[font_name] = os.path.abspath(font_path)

    # 尝试获取系统字体
    try:
        font_paths = get_system_font_path()
        for file in font_paths:
            try:
                font_name = os.path.splitext(file)[0]
                font_path = file
                font_files[font_name] = os.path.abspath(font_path)
            except Exception as e:
                print(f"Error processing font {file}: {e}")
    except Exception as e:
        print(f"Error finding system fonts: {e}")

    return font_files

r_directory = os.path.join(os.path.dirname(__file__), '..','assets','/')

font_files = get_font_files(r_directory)
# print(font_files)


def flatten_list(nested_list):
    flat_list = []
    for item in nested_list:
        if isinstance(item, list):
            flat_list.extend(flatten_list(item))
        else:
            if torch.is_tensor(item):
                print('item.shape',item.shape)
                for i in range(item.shape[0]):
                    flat_list.append(item[i:i + 1, ...])
            else:
                flat_list.append(item)
    return flat_list


class ColorInput:
    @classmethod
    def INPUT_TYPES(s):
        return {"required": { 
                    "color":("TCOLOR",), 
                             },
                }
    
    RETURN_TYPES = ("STRING","INT","INT","INT","FLOAT",)
    RETURN_NAMES = ("hex","r","g","b","a",)

    FUNCTION = "run"

    CATEGORY = "♾️Mixlab/Color"

    INPUT_IS_LIST = False
    OUTPUT_IS_LIST = (False,False,False,False,False,)

    def run(self,color):
        h=color['hex']
        r=color['r']
        g=color['g']
        b=color['b']
        a=color['a']
        return (h,r,g,b,a,)


class KeyInput:
    @classmethod
    def INPUT_TYPES(s):
        return {"required": { 
                    "key":("KEY",), 
                             },
                }
    
    RETURN_TYPES = ("STRING",)
    RETURN_NAMES = ("key",)

    FUNCTION = "run"

    CATEGORY = "♾️Mixlab/Input"

    INPUT_IS_LIST = False
    OUTPUT_IS_LIST = (False,)

    def run(self,key):
        return (key,)



class FontInput:
    @classmethod
    def INPUT_TYPES(s):
        return {"required": { 
             
                    "font": (list(font_files.keys()),),
                             },
                }
    
    RETURN_TYPES = ("STRING",)
    # RETURN_NAMES = ("WIDTH","HEIGHT","X","Y",)

    FUNCTION = "run"

    CATEGORY = "♾️Mixlab/Input"

    INPUT_IS_LIST = False
    OUTPUT_IS_LIST = (False,)

    def run(self,font):
        
        return (font_files[font],)
    
class TextToNumber:
    @classmethod
    def INPUT_TYPES(s):
        return {"required": { 
                    "text": ("STRING",{"multiline": False,"default": "1"}),
                    "random_number": (["enable", "disable"],),
                    "max_num":("INT", {
                        "default": 10, 
                        "min":2, #Minimum value
                        "max": 10000000000, #Maximum value
                        "step": 1, #Slider's step
                        "display": "number" # Cosmetic only: display as "number" or "slider"
                    }),
                             },
                 "optional":{
                    "seed": (any_type,  {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
                }
                }
    
    RETURN_TYPES = ("INT",)
    # RETURN_NAMES = ("WIDTH","HEIGHT","X","Y",)

    FUNCTION = "run"

    CATEGORY = "♾️Mixlab/Text"

    INPUT_IS_LIST = False
    OUTPUT_IS_LIST = (False,)

    def run(self,text,random_number,max_num,seed=0):
        
        numbers = re.findall(r'\d+', text)
        result=0
        for n in numbers:
            result = int(n)
            # print(result)
        
        if random_number=='enable' and result>0:
            result= random.randint(1, max_num)
        return {"ui": {"text": [text],"num":[result]}, "result": (result,)}
    

   
class FloatSlider:
    @classmethod
    def INPUT_TYPES(s):
        return {"required": {
                    "number":("FLOAT", {
                        "default": 0, 
                        "min": 0, #Minimum value
                        "max": 0xffffffffffffffff, #Maximum value
                        "step": 0.001, #Slider's step
                        "display": "slider" # Cosmetic only: display as "number" or "slider"
                    }),
                    "min_value":("FLOAT", {
                        "default": 0, 
                        "min": -0xffffffffffffffff, 
                        "max": 0xffffffffffffffff,
                        "step": 0.001, 
                        "display": "number"  
                    }),
                    "max_value":("FLOAT", {
                            "default": 1, 
                            "min": -0xffffffffffffffff,
                            "max": 0xffffffffffffffff,
                            "step": 0.001, 
                            "display": "number"  
                        }),
                    "step":("FLOAT", {
                            "default": 0.001, 
                            "min": -0xffffffffffffffff,
                            "max": 0xffffffffffffffff,
                            "step": 0.001, 
                            "display": "number"  
                        }),
                             },
                }
    
    RETURN_TYPES = ("FLOAT",) 
    RETURN_NAMES = ('FLOAT',)
    FUNCTION = "run"

    CATEGORY = "♾️Mixlab/Input"

    INPUT_IS_LIST = False
    OUTPUT_IS_LIST = (False,)

    def run(self, number, min_value, max_value, step):
        if number < min_value:
            number = min_value
        elif number > max_value:
            number = max_value
        return (number,)    
  
class IntNumber:
    @classmethod
    def INPUT_TYPES(s):
        return {"required": {
                    "number":("INT", {
                        "default": 0, 
                        "min": -1, #Minimum value
                        "max": 0xffffffffffffffff,
                        "step": 1, 
                        "display": "number"
                    }),
                    "min_value":("INT", {
                        "default": 0, 
                        "min": -0xffffffffffffffff, 
                        "max": 0xffffffffffffffff,
                        "step": 1, 
                        "display": "number"  
                    }),
                    "max_value":("INT", {
                            "default": 1, 
                            "min": -0xffffffffffffffff,
                            "max": 0xffffffffffffffff,
                            "step": 1, 
                            "display": "number"  
                        }),
                    "step":("INT", {
                            "default": 1, 
                            "min": -0xffffffffffffffff,
                            "max": 0xffffffffffffffff,
                            "step":1,
                            "display": "number"  
                        }),
                             },
                }
    
    RETURN_TYPES = ("INT",) 

    FUNCTION = "run"

    CATEGORY = "♾️Mixlab/Input"

    INPUT_IS_LIST = False
    OUTPUT_IS_LIST = (False,)

    def run(self,number,min_value,max_value,step):
        if number < min_value:
            number= min_value
        elif number > max_value:
            number= max_value
        return (number,)

class MultiplicationNode:
    @classmethod
    def INPUT_TYPES(s):
        return {"required": {
                    "numberA":(any_type,),
                    "multiply_by":("FLOAT", {
                        "default": 1, 
                        "min": -2, #Minimum value
                        "max": 0xffffffffffffffff,
                        "step": 0.01, #Slider's step
                        "display": "number" # Cosmetic only: display as "number" or "slider"
                    }),
                     "add_by":("FLOAT", {
                        "default": 0, 
                        "min": -2000, #Minimum value
                        "max": 0xffffffffffffffff,
                        "step": 0.01, #Slider's step
                        "display": "number" # Cosmetic only: display as "number" or "slider"
                    })
                             },
                }
    
    RETURN_TYPES = ("FLOAT","INT",) 

    FUNCTION = "run"

    CATEGORY = "♾️Mixlab/Utils"

    INPUT_IS_LIST = False
    OUTPUT_IS_LIST = (False,False,)

    def run(self,numberA,multiply_by,add_by):
        b=int(numberA*multiply_by+add_by)
        a=float(numberA*multiply_by+add_by)
        return (a,b,) 

class TextInput:
    @classmethod
    def INPUT_TYPES(s):
        return {"required": {
                    "text": ("STRING",{"multiline": True,"default": ""})
                             },
                }
    
    RETURN_TYPES = ("STRING",) 

    FUNCTION = "run"

    CATEGORY = "♾️Mixlab/Input"

    INPUT_IS_LIST = False
    OUTPUT_IS_LIST = (False,)

    def run(self,text):
       
        return (text,)
    

class IncrementingListNode:
    @classmethod
    def INPUT_TYPES(s):
        return {"required": {
                    "min_value": ("FLOAT", {
                        "default": 0, 
                        "min": -2000, #Minimum value
                        "max": 0xffffffffffffffff,
                        "step": 0.01, #Slider's step
                        "display": "number" # Cosmetic only: display as "number" or "slider"
                    }),
                    "max_value": ("FLOAT", {
                        "default": 10, 
                        "min": -2000, #Minimum value
                        "max": 0xffffffffffffffff,
                        "step": 0.01, #Slider's step
                        "display": "number" # Cosmetic only: display as "number" or "slider"
                    }),
                    "step": ("FLOAT", {
                        "default": 0, 
                        "min": -2000, #Minimum value
                        "max": 0xffffffffffffffff,
                        "step": 0.01, #Slider's step
                        "display": "number" # Cosmetic only: display as "number" or "slider"
                    }),
                    "count": ("INT", {
                        "default": 1, 
                        "min": 1, #Minimum value
                        "max": 0xffffffffffffffff,
                        "step":1, #Slider's step
                        "display": "number" # Cosmetic only: display as "number" or "slider"
                    })
                             },
                             "optional":{
                    "seed":("INT", {"default": -1, "min": -1, "max": 1000000}), 
                    
                },
                }
    
    RETURN_TYPES = ("INT","FLOAT",) 
    RETURN_NAMES = ('int_list','float_list',)

    FUNCTION = "run"

    CATEGORY = "♾️Mixlab/Video"

    INPUT_IS_LIST = False
    OUTPUT_IS_LIST = (True,True,)

    def run(self,min_value,max_value,step,count,seed):
        print('create_incrementing_list',seed)
        l1,l2=create_incrementing_list(min_value,max_value,step,count)
        return (l1,l2,)
    
# 接收一个值，然后根据字符串或数值长度计算延迟时间，用户可以自定义延迟"字/s"，延迟之后将转化

import comfy.samplers
import folder_paths
 
# import time


class AnyType(str):
  """A special class that is always equal in not equal comparisons. Credit to pythongosssss"""

  def __ne__(self, __value: object) -> bool:
    return False

any_type = AnyType("*")
import time

class DynamicDelayProcessor:

    @classmethod
    def INPUT_TYPES(cls):
        # print("print INPUT_TYPES",cls)
        return {
            "required":{
                    "delay_seconds":("INT",{
                    "default":1,
                    "min": 0,
                    "max": 1000000,
                    }),
            },
            "optional":{
                "any_input":(any_type,),
                "delay_by_text":("STRING",{"multiline":True,"dynamicPrompts": False,}),
                "words_per_seconds":("FLOAT",{ "default":1.50,"min": 0.0,"max": 1000.00,"display":"Chars per second?"}),
                "replace_output": (["disable","enable"],),
                "replace_value":("INT",{ "default":-1,"min": 0,"max": 1000000,"display":"Replacement value"})
                }
            }
  
    @classmethod
    def calculate_words_length(cls,text):
        chinese_char_pattern = re.compile(r'[\u4e00-\u9fff]')
        english_word_pattern = re.compile(r'\b[a-zA-Z]+\b')
        number_pattern = re.compile(r'\b[0-9]+\b')

        words_length = 0
        for segment in text.split():
            if chinese_char_pattern.search(segment):
                # 中文字符，每个字符计为 1
                words_length += len(segment)
            elif number_pattern.match(segment):
                # 数字，每个字符计为 1
                words_length += len(segment)
            elif english_word_pattern.match(segment):
                # 英文单词，整个单词计为 1
                words_length += 1

        return words_length



    FUNCTION = "run"
    RETURN_TYPES = (any_type,)
    RETURN_NAMES = ('output',)

    CATEGORY = "♾️Mixlab/Utils"
    def run(self,any_input,delay_seconds,delay_by_text,words_per_seconds,replace_output,replace_value):
        # print(f"Delay text:",delay_by_text )
        # 获取开始时间戳
        start_time = time.time()

        # 计算延迟时间
        delay_time = delay_seconds
        if delay_by_text and isinstance(delay_by_text, str) and words_per_seconds > 0:
            words_length = self.calculate_words_length(delay_by_text)
            print(f"Delay text: {delay_by_text}, Length: {words_length}")
            delay_time += words_length / words_per_seconds
            
        # 延迟执行
        print(f"延迟执行: {delay_time}")
        time.sleep(delay_time)

        # 获取结束时间戳并计算间隔
        end_time = time.time()
        elapsed_time = end_time - start_time
        print(f"实际延迟时间: {elapsed_time} 秒")        

        # 根据 replace_output 决定输出值
        return (max(0, replace_value),) if replace_output == "enable" else (any_input,)
        



# app 配置节点
class AppInfo:
    @classmethod
    def INPUT_TYPES(s):
        return {"required": { 
                    "name": ("STRING",{"multiline": False,"default": "Mixlab-App","dynamicPrompts": False}),
                    "input_ids":("STRING",{"multiline": True,"default": "\n".join(["1","2","3"]),"dynamicPrompts": False}),
                    "output_ids":("STRING",{"multiline": True,"default": "\n".join(["5","9"]),"dynamicPrompts": False}),
                             },

                "optional":{
                    "image": ("IMAGE",),
                    "description":("STRING",{"multiline": True,"default": "","dynamicPrompts": False}),
                    "version":("INT", {
                        "default": 1, 
                        "min": 1, 
                        "max": 10000, 
                        "step": 1, 
                        "display": "number"  
                    }),
                    "share_prefix":("STRING",{"multiline": False,"default": "","dynamicPrompts": False}),
                    "link":("STRING",{"multiline": False,"default": "https://","dynamicPrompts": False}),
                    "category":("STRING",{"multiline": False,"default": "","dynamicPrompts": False}),
                    "auto_save": (["enable","disable"],),
                    "idle_animation": ("BOOLEAN", {"default": False},),
                }

                }
    
    RETURN_TYPES = ()
    # RETURN_NAMES = ("IMAGE",)

    FUNCTION = "run"

    CATEGORY = "♾️Mixlab"

    OUTPUT_NODE = True
    INPUT_IS_LIST = True
    # OUTPUT_IS_LIST = (True,)

    def run(self,name,input_ids,output_ids,image,description,version,share_prefix,link,category,auto_save,idle_animation):
        name=name[0]

        idle_animation=idle_animation[0]
        
        im=[]
        if image:
            images=[image] 
            # batch 的方式需要处理
            images=flatten_list(images)
            # img=image[0][0] 
            print('AppInfo_image',len(images))
            for i in range(len(images)):
                img=images[i]
                im.append(create_temp_file(img,i+1)[0])
        # image [img,] img[batch,w,h,a] 列表里面是batch，

        input_ids=input_ids[0]
        output_ids=output_ids[0]
        description=description[0]
        version=version[0]
        share_prefix=share_prefix[0]
        link=link[0]
        category=category[0]
        
        # id=get_json_hash([name,im,input_ids,output_ids,description,version])

        return {"ui": {"json": [name,im,input_ids,output_ids,description,version,share_prefix,link,category,idle_animation]}, "result": ()}
    

    
class CreateJsonNode:
    @classmethod
    def INPUT_TYPES(s):
        return {"required": { 
                    "key": ("STRING",{"multiline": False,"default": "data","dynamicPrompts": False}),
                    "value":(any_type,),
                    "save":("BOOLEAN", {"default": True},),
                             }, 
                "optional":{
                    "json_str":("STRING", {"forceInput": True,}),
                }

                }
    
    RETURN_TYPES = ("STRING",)
    RETURN_NAMES = ("json_str",)

    FUNCTION = "run"

    CATEGORY = "♾️Mixlab/Output"

    OUTPUT_NODE = True
    INPUT_IS_LIST = False
    # OUTPUT_IS_LIST = (True,)

    def run(self,key,value,save,json_str=None):
        data={}

        data[key]=value

        if json_str:
            json_obj = json.loads(json_str)
            data.update(json_obj)

        if save:
            # 保存为本地文件
            with open(os.path.join(folder_paths.get_output_directory(),'data.json'), 'w') as file:
                json.dump(data, file, ensure_ascii=False, indent=4)

        return (json.dumps(data),)
    

    


class SwitchByIndex:
    @classmethod
    def INPUT_TYPES(cls):
        return {
             "optional":{
                  "A":(any_type,),
                  "B":(any_type,),
            },
            "required": { 
                "index":("INT", {
                        "default": -1, 
                        "min": -1, 
                        "max": 1000, 
                        "step": 1, 
                        "display": "number"  
                    }),
                 "flat": (['off',"on"],),
            }
        }

    RETURN_TYPES = (any_type,"INT",)
    RETURN_NAMES = ("list", "count",)

    FUNCTION = "run"

    CATEGORY = "♾️Mixlab/Utils"

    INPUT_IS_LIST = True
    OUTPUT_IS_LIST = (True, False,)

    def run(self, A=[],B=[],index=-1,flat='on'):

        flat=flat[0]

        C=[]
        index=index[0]

        for a in A:    
            C.append(a)
        for b in B:
            C.append(b)

        if flat=='on':
            C=flatten_list(C)

        if index>-1:
            try:
                C=[C[index]]
            except Exception as e:
                C=[C[-1]] #最后一个

        return (C, len(C),)

class ListSplit:
    @classmethod
    def INPUT_TYPES(cls):
        return {
             "optional":{
                  "A":(any_type,), 
            },
            "required": { 
                "chunk_size": ("INT", {"default": 10, "min": 1, "step": 1}),
                "transition_size": ("INT", {"default": 0, "min": 0, "step": 1}),
                "index": ("INT", {"default": -1, "min": -1, "step": 1}),
            }
        }

    RETURN_TYPES = (any_type,)
    RETURN_NAMES = ("B",)

    FUNCTION = "run"

    CATEGORY = "♾️Mixlab/Utils"

    INPUT_IS_LIST = True
    OUTPUT_IS_LIST = (True,)

    def run(self, A=[],chunk_size=[10],transition_size=[0],index=[-1]):
        # print(len(A))
        B=split_list(A,chunk_size[0],transition_size[0])

        if index[0]>-1:
            B=B[index[0]]

        return (B,)
    


class LimitNumber:
    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                "number":(any_type,),
                "min_value":("INT", {
                        "default": 0, 
                        "min": 0, 
                        "max": 0xffffffffffffffff,
                        "step": 1, 
                        "display": "number"  
                    }),
                "max_value":("INT", {
                        "default": 1, 
                        "min": 1, 
                        "max": 0xffffffffffffffff,
                        "step": 1, 
                        "display": "number"  
                    }),
            }
        }

    RETURN_TYPES = (any_type,)
    RETURN_NAMES = ("number",)

    FUNCTION = "run"

    CATEGORY = "♾️Mixlab/Input"

    INPUT_IS_LIST = False
    OUTPUT_IS_LIST = (False,)

    def run(self, number, min_value, max_value):
        nn=number

        if isinstance(number, int):
            min_value=int(min_value)
            max_value=int(max_value)
        if isinstance(number, float):
            min_value=float(min_value)
            max_value=float(max_value)

        if number < min_value:
            nn= min_value
        elif number > max_value:
            nn= max_value
        
        return (nn,)



class ListStatistics:
    @staticmethod
    def count_types(lst):
        type_count = {}

        for item in lst:
            item_type = type(item).__name__
            if item_type not in type_count:
                type_count[item_type] = []

            if item_type in ['dict', 'str', 'int', 'float']:
                type_count[item_type].append(item)

        return type_count

# # 示例列表
# my_list = [1, 'hello', {'name': 'John'}, 3.14, {'age': 25}, 'world', 10]

# # 创建ListStatistics对象
# list_stats = ListStatistics()

# # 调用count_types方法进行统计
# result = list_stats.count_types(my_list)

# # 输出结果
# for item_type, values in result.items():
#     print(item_type + ':')
#     for value in values:
#         print(value)
#     print('---')

class TESTNODE_:
    @classmethod
    def INPUT_TYPES(s):
        return {"required": { 
                                "ANY":(any_type,),                
                              },
                }
    
    RETURN_TYPES = (any_type,)

    FUNCTION = "run"

    CATEGORY = "♾️Mixlab/Test"

    OUTPUT_NODE = True
    INPUT_IS_LIST = True
    OUTPUT_IS_LIST = (True,)

    def run(self,ANY):

        print('#TESTNODE_',len(ANY))

        print(type(ANY))
        try:
            print(ANY[0].shape)
            img= tensor2pil(ANY[0])
            print(img.size)
        except:
            print('')
        
        # data=ANY
        list_stats = ListStatistics()

        # 调用count_types方法进行统计
        result = list_stats.count_types(ANY)
 

        # 假设我们有一个模块文件名为 my_module.py，它位于 'importables' 目录下
        module_path = os.path.join(os.path.dirname(__file__),'test.py')

        # 使用 spec_from_file_location 获取模块的元数据（名称、定义等）
        spec = importlib.util.spec_from_file_location('test', module_path)

        module = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(module)

        functions = getattr(module, 'run')  # 获取函数
        
        functions(ANY)


        return {"ui": {"data": result,"type":[str(type(ANY[0]))]}, "result": (ANY,)}


class TESTNODE_TOKEN:
    @classmethod
    def INPUT_TYPES(s):
        return {"required": { 
            "text":("STRING", {"forceInput": True,}),
            "clip": ("CLIP", )                 
                              },
                }
    
    RETURN_TYPES = ("STRING",)

    FUNCTION = "run"

    CATEGORY = "♾️Mixlab/Test"

    OUTPUT_NODE = True
    INPUT_IS_LIST = False
    OUTPUT_IS_LIST = (False,)

    def run(self,text,clip=None):
        # print(text)
        
        tokens = clip.tokenize(text)
        
        tokens=[v for v in tokens.values()][0][0]

        tokens=json.dumps(tokens)
        
        return (tokens,)



class CreateSeedNode:
    def __init__(self):
        pass

    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                "seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
            }
        }

    RETURN_TYPES = ("INT",)
    RETURN_NAMES = ("seed",)
  
    OUTPUT_NODE = True
    FUNCTION = "run"

    CATEGORY = "♾️Mixlab/Experiment"

    def run(self, seed):
        return (seed,)
    

class CreateCkptNames:
    def __init__(self):
        pass

    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                "ckpt_names": ("STRING",{"multiline": True,"default": "\n".join(folder_paths.get_filename_list("checkpoints")),"dynamicPrompts": False}),
            }
        }

    RETURN_TYPES = (any_type,)
    RETURN_NAMES = ("ckpt_names",)
    
    INPUT_IS_LIST = False
    OUTPUT_IS_LIST = (True,)

    # OUTPUT_NODE = True
    FUNCTION = "run"

    CATEGORY = "♾️Mixlab/Experiment"

    def run(self, ckpt_names):
        ckpt_names=ckpt_names.split('\n')
        ckpt_names = [name for name in ckpt_names if name.strip()]
        return (ckpt_names,)


class CreateLoraNames:
    def __init__(self):
        pass

    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                "lora_names": ("STRING",{"multiline": True,"default": "\n".join(folder_paths.get_filename_list("loras")),"dynamicPrompts": False}),
            }
        }

    RETURN_TYPES = (any_type,"STRING",)
    RETURN_NAMES = ("lora_names","prompt",)
    
    INPUT_IS_LIST = False
    OUTPUT_IS_LIST = (True,True,)

    # OUTPUT_NODE = True
    FUNCTION = "run"

    CATEGORY = "♾️Mixlab/Experiment"

    def run(self, lora_names):
        lora_names=lora_names.split('\n')
        lora_names = [name for name in lora_names if name.strip()]
        prompts=[os.path.splitext(n)[0] for n in lora_names]
        return (lora_names,prompts,)
   


class CreateSampler_names:
    def __init__(self):
        pass

    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                "sampler_names": ("STRING",{"multiline": True,"default": "\n".join(comfy.samplers.KSampler.SAMPLERS),"dynamicPrompts": False}),
            }
        }

    RETURN_TYPES = (any_type,)
    RETURN_NAMES = ("sampler_names",)
    
    INPUT_IS_LIST = False
    OUTPUT_IS_LIST = (True,)

    # OUTPUT_NODE = True
    FUNCTION = "run"

    CATEGORY = "♾️Mixlab/Experiment"

    def run(self, sampler_names):
        sampler_names=sampler_names.split('\n')
        sampler_names = [name for name in sampler_names if name.strip()]
        return (sampler_names,)