import json
import re
from copy import deepcopy
import random

def clean_string(input_string):
    # remove leading and trailing spaces
    input_string = input_string.strip()
    # remove trailing ";" and "."
    input_string = re.sub(r";$", "", input_string)
    input_string = re.sub(r"\.$", "", input_string)
    return input_string

class GPTOutputParser():
    def __init__(self, version):
        self.version = version

    def __call__(self, description):
        if self.version == "v1":
            try:
                description = json.loads(description.strip("\n"))
                description['description'] = description['description'].split("; ")
                description['type'] = "a kind of {}".format(description['type'])
            except:
                description = {
                    "type": "object",
                    "description": [],
                    "similar objects": []
                }
            return description
        if self.version == "v5":
            info = description
            # check the format of type of thing
            if "has a tangible appearance and is" in info[0]:
                type_of_thing = info[0].split(" has a tangible appearance and is ")[-1].split(".")[0]
            elif "has a tangible appearance" in info[0]:
                type_of_thing = info[0].split(" has a tangible appearance ")[-1].split(".")[0]
                if "and refers to" in type_of_thing:
                    type_of_thing = type_of_thing.split("and refers to ")[-1]
            else:
                #print(info[0], "type of thing not found")
                type_of_thing = ""
            
            if " are:\t" in info[0]:
                similar_things = info[0].split(" are:\t")[-1].split("\nThere are several useful visual features to tell")[0].split("\t")
                similar_things = [i for i in similar_things if i.strip() != ""] # remove empty strings
            else:
                #print(info[0], "similar things not found")
                similar_things = []
            
            if " and not similar things in a photo:\t" in info[0]:
                visual_feature_descriptions = info[0].split(" and not similar things in a photo:\t")[-1].split("\t")
                visual_feature_descriptions = [i for i in visual_feature_descriptions if i.strip() != ""] # remove empty strings
            else:
                #print(info[0], "visual feature descriptions not found")
                visual_feature_descriptions = []
            return {
                "type": type_of_thing,
                "description": "; ".join(visual_feature_descriptions),
                "similar_things": similar_things
            }
        if self.version == "v6":
            description = description.lower()
            type_ = re.findall(r"type: (.*)", description)[0]
            # description
            
            visual_description = re.findall(r"visual description.*:(.*)similar objects", description, re.DOTALL)[0]
        
            # fine substrings with leading 1. 2. 
            visual_description = re.findall(r"[(\d\.)(-\.)]\ (.*)", visual_description)

            # similar objects
            similar_objects = re.findall(r"similar objects:(.*)", description, re.DOTALL)[0]
            similar_objects = re.findall(r"[(\d\.)(-\.)]\ (.*)", similar_objects)

            visual_description = [clean_string(i) for i in visual_description]
            visual_description = [i for i in visual_description if i != ""]

            similar_objects = [clean_string(i) for i in similar_objects]
            similar_objects = [i for i in similar_objects if i != ""]

            final_description = {
                "type": type_,
                "description": "; ".join(visual_description),
                "similar objects": similar_objects,
            }
            return final_description
            # except:
            #     print(description_dict)
            #     pdb.set_trace()
        if self.version == "v7":
            '''
            "- plumbing fixture\n- white or off-white\n- a bowl-shaped basin\n- a drain at the bottom\n- a water supply line\n- a flush handle or button\n- a splash guard\n- a wall-mounted or floor-mounted design"
            '''
            description = description.lower()
            description = [des.replace("- ", "") for des in description.split("\n")]
            final_description = {
                "type": description[0],
                "description": description[1:],
                "similar objects": [],
            }
            return final_description

        assert False, "version not supported"
    
    def form_span(self, 
                  noun, 
                  description, 
                  type = "vanilla", 
                  positive_range = "partial",
                  start_index = 0,
                  od_to_grounding_version = ''):
        '''
        Given the parsed description, form the span
        '''

        if "random_origin" in od_to_grounding_version and random.random() < 0.1:
            # directly use the noun it self
            return noun, len(noun), None, None # forget about span

        description = self(description)
        type_of_thing = description['type']
        
        pheso_caption = ""
        spans = []

        start_index_rolling = start_index
        # the first substring is the name
        if "remove_noun" in type:
            pass
        else:
            sub_descripion = "{}, ".format(noun)
            pheso_caption += sub_descripion
            spans.append([start_index_rolling, len(pheso_caption) + start_index])
        
        start_index_rolling = len(pheso_caption) + start_index
        if "skip_des" in type:
            descriptions = [type_of_thing] 
        else:
            descriptions = [type_of_thing] + description['description']

        for index, description_i in enumerate(descriptions):
            if index != len(descriptions) - 1:
                _suffix = ", "
            else:
                _suffix = ". "

            sub_descripion = "{}{}".format(description_i, _suffix)
            pheso_caption += sub_descripion
            spans.append([start_index_rolling, len(pheso_caption) + start_index])
            start_index_rolling = len(pheso_caption) + start_index

            if index == 0: # pheso_caption: cat, a kind of animal
                if positive_range == "partial": # when 
                    end_index = len(pheso_caption)
                    positive_spans = deepcopy(spans)

        if positive_range == "all":
            end_index = len(pheso_caption)
            positive_spans = deepcopy(spans)
        return pheso_caption, end_index, spans, positive_spans

    def form_span_independent(self, 
                  noun, 
                  description, 
                  type = "vanilla", 
                  positive_range = "partial",
                  start_index = 0,
                  od_to_grounding_version = None):
        '''
        Given the parsed description, form the span
        '''
        if "infer" in od_to_grounding_version:
            use_random = False
        else:
            use_random = True
        
        description = self(description)
        type_of_thing = description['type']
        
        pheso_caption = ""
        spans = []

        start_index_rolling = start_index
        # the first substring is the name
        if use_random and random.random() < 0.5:
                drop_noun = True
        else:
            drop_noun = False
        
        if not drop_noun:
            sub_descripion = "{}, {}. ".format(noun, type_of_thing)
            pheso_caption += sub_descripion
            spans.append([start_index_rolling, len(pheso_caption) + start_index])
        
        start_index_rolling = len(pheso_caption) + start_index
        descriptions = description['description']

        for index, description_i in enumerate(descriptions):
            _suffix = ". "
            if use_random and random.random() < 0.5:
                leading_word = type_of_thing
            else:
                leading_word = noun
            
            sub_descripion = "{}, {}{}".format(leading_word, description_i, _suffix)
            pheso_caption += sub_descripion
            spans.append([start_index_rolling, len(pheso_caption) + start_index])
            start_index_rolling = len(pheso_caption) + start_index

            if index == 0: # pheso_caption: cat, a kind of animal
                if positive_range == "partial": # when 
                    end_index = len(pheso_caption)
                    positive_spans = deepcopy(spans)

        if positive_range == "all":
            end_index = len(pheso_caption)
            positive_spans = deepcopy(spans)
        return pheso_caption, end_index, spans, positive_spans