import zipfile
import hashlib
import re
import os


def get_file_hash_in_zip(z, file_path, chunk_size=1024) ->str:
    # Calculate the SHA-256 hash of a file
    hasher = hashlib.sha256()
    with z.open(file_path) as f:
        while True:
            data = f.read(chunk_size)
            if not data:
                break
            hasher.update(data)
    return hasher.hexdigest()


def extract_morph_data(zip_path, morph_path) -> list:
    morphs_data = []

    with zipfile.ZipFile(zip_path, 'r') as zipf:
        # Get list of files inside ZIP
        file_list = zipf.namelist()

        # Fing .vmi files in directory custom/atom/person/morphs/female
        for file_name in file_list:
            if file_name.startswith(morph_path) and file_name.endswith('.vmi'):
                # Read contents of .vmi file
                with zipf.open(file_name) as vmi_file:
                    content = vmi_file.read().decode('utf-8')

                    # Extract morph name without extension
                    morph_name = os.path.splitext(os.path.basename(file_name))[0]

                    # Define regex for data extract
                    patterns = {
                        'id': r'"id" : "([^"]+)"',
                        'displayName': r'"displayName" : "([^"]+)"',
                        'group': r'"group" : "([^"]+)"',
                        'region': r'"region" : "([^"]+)"',
                        'min': r'"min" : (-?\d+)',
                        'max': r'"max" : (-?\d+)',
                        'numDeltas': r'"numDeltas" : (\d+)',
                        'isPoseControl': r'"isPoseControl" : (true|false)',
                        'formulas': r'"formulas" : \[([^\]]+)\]'
                    }

                    # Create a dictionary for storing data
                    morph_dict = {'name': morph_name}

                    # Extract each item using regex
                    vmb_hash=True
                    for key, pattern in patterns.items():
                        match = re.search(pattern, content)
                        if match:
                            value = match.group(1)
                            if key in ['min', 'max', 'numDeltas']:
                                morph_dict[key] = int(value)
                                if morph_dict['numDeltas'] == '0': vmb_hash=False
                            elif key == 'isPoseControl':
                                morph_dict[key] = value.lower() == 'true'
                            elif key == 'formulas':
                                # Formulas separated by comma
                                # morph_dict[key] = [item.strip().strip('"') for item in value.split(',')]

                                # Get start index after "formulas"
                                start_index = key.end()
                                # Extract substring desde ese punto hasta el final
                                sub_content = content[start_index]
                                # Count '{'
                                morph_dict[key] = len(re.findall(r'\{', sub_content))
                            else:
                                morph_dict[key] = value

                    if vmb_hash:
                        # Get hash from related .vmb file
                        vmb_file_name = file_name.replace('.vmi', '.vmb')
                    else: # no deltas case
                        # Pass vmi as vmb (all vmb with no deltas are the same)
                        vmb_file_name = file_name                        

                    if vmb_file_name in file_list:
                        # with zipf.open(vmb_file_name) as vmb_file:
                        # vmb_content = vmb_file.read().decode('utf-8')
                        hash = get_file_hash_in_zip(zipf, vmb_file_name)
                        morph_dict['hash'] = hash
                    else:
                        morph_dict['hash'] = None
                    
                    # Add dictionary to final result
                    morphs_data.append(morph_dict)
    
    return morphs_data

# Example
# if __name__ == "__main__":
    # zip_path = 'path/to/your/file.zip'
    # morphs = extract_morph_data(zip_path)
    # for morph in morphs:
        # print(morph)


def calc_hash(file_obj, hash_func='md5') -> str:
    # Calculates hash from file using specified hashing function.
    h = hashlib.new(hash_func)
    for chunk in iter(lambda: file_obj.read(4096), b""):
        h.update(chunk)
    return h.hexdigest()



def get_hash_of_file_in_zip(zip_ref, file_path) -> str:
    # Read the content of the file in the zip archive
    with zip_ref.open(file_path) as f:
        content = f.read()
    # Create hash object and update it with file content
    md5_hash = hashlib.md5(content).hexdigest()
    return md5_hash

def get_morphs_list_from_zip(zip_ref: zipfile, prefix: str, internal_path) -> list:
    vmi_dict = {}
    
    # Open the zip file and iterate over its contents
    # with zipfile.ZipFile(zip_file, 'r') as zip_ref:
    for file_info in zip_ref.infolist():
        file_name = file_info.filename
        
        # Check if file is inside the specified path
        if not file_name.startswith(internal_path):
            continue
        
        # Handle .vmi files first to check "numDeltas" : "0"
        if file_name.endswith('.vmi'):
            base_name = os.path.basename(file_name[:-4])  # Remove '.vmi' extension and full path
            with zip_ref.open(file_name) as f:
                content = f.read().decode('utf-8')
            
            if '"numDeltas" : "0"' in content:
                vmi_dict[base_name] = get_hash_of_file_in_zip(zip_ref, file_name)
        
        # Handle .vmb files
        elif file_name.endswith('.vmb'):
            base_name = os.path.basename(file_name[:-4])  # Remove '.vmb' extension and full path
            
            if base_name not in vmi_dict:
                # If corresponding .vmi was not found or did not have "numDeltas" : "0"
                hash_value = get_hash_of_file_in_zip(zip_ref, file_name)
                vmi_dict[base_name] = hash_value

    # Format the output string as required
    return list(f'{prefix}.{name}.{hash_val}' for name, hash_val in vmi_dict.items())


# Example usage:
#zip_path = 'a:\AddonPackages\morphs\WeebU.My_morphs.4.var'
#internal_dir = 'Custom/Atom/Person/Morphs'
#with zipfile.ZipFile(zip_path, 'r') as zip_file:
#    print(', '.join(get_morphs_list_from_zip(zip_file, internal_dir)))
#    print(len(get_morphs_list_from_zip(zip_file, internal_dir)))