# Copyright 2024 DEViantUa <t.me/deviant_ua>
# All rights reserved.

import asyncio
import io
from PIL import ImageDraw,Image,ImageFilter,ImageChops, ImageSequence

from ..tools import git, options
from ..tools.calculator import stats
from ..model.style import RelictScore

from ..tools import pill

_of = git.ImageCache()

_DEFAULT_SCORE = {'count': 0, 
                  'rolls': {}, 
                  'rank': {'name': 'N/A', 
                           'color': (255, 255, 255, 255)
                    }
}

async def get_cone_frame(x):
    if x == 5:
        return await _of.light_cone_frame_five
    elif x == 4:
        return await _of.light_cone_frame_four
    else:
        return await _of.light_cone_frame_three

class Creat:
    def __init__(self, data, lang, art, hide_uid, uid, seeleland,remove_logo) -> None:
        self.remove_logo = remove_logo
        self.data = data
        self.lang = lang
        self.art = art #Image.open("modified_exampl.gif")#art
        self.hide_uid = hide_uid
        self.uid = uid
        
        self.gif = False
        self.GIFT_BG = []
        
        self.seeleland = seeleland
        self.totall_eff = 0
        self.element_color = self.data.element.color.rgba
    
    async def creat_bacground(self):
        self.background = Image.new(RelictScore.RGBA, RelictScore.bacground_size, (0,0,0,0))
        background_dark = Image.new(RelictScore.RGBA, RelictScore.bacground_size, (0,0,0,100))
        background_art = Image.new(RelictScore.RGBA, RelictScore.bacground_size, (0,0,0,0))
        background_blur = Image.new(RelictScore.RGBA, RelictScore.bacground_size, (0,0,0,0))
        lines = await _of.background_line
        logo = await _of.LOGO_GIT
        shadow = await _of.background_shadow
        maska_art = await _of.background_maska_art
        background_overlay = await _of.background_overlay
        background_maska_blur = await _of.background_maska_blur
        background_default = await _of.background_default
                
        if self.art:
            user_image = await pill.get_centr_size(RelictScore.art_size, self.art)
            bg = await pill.GradientGenerator(user_image).generate(1,RelictScore.bacground_size[1])       
            bg = bg.resize(RelictScore.bacground_size)
            user_image_op = await pill.apply_opacity(user_image, opacity = RelictScore.opacity_art)

            background_blur.alpha_composite(bg.convert("RGBA"))
            background_blur.alpha_composite(user_image_op)
            
            background_blur = background_blur.filter(ImageFilter.GaussianBlur(RelictScore.blur_art))
            line,self.element_color = await pill.recolor_image(lines, self.element_color[:3], light = True)            
        else:
            bg = background_default.copy() 
            user_image = await pill.get_centr_scale(RelictScore.splash_size, await pill.get_dowload_img(self.data.portrait))
            user_image_op = await pill.apply_opacity(user_image, opacity = RelictScore.opacity_splash)
            background_blur.alpha_composite(bg.convert("RGBA"))
            background_blur.alpha_composite(user_image_op)
            background_blur = background_blur.filter(ImageFilter.GaussianBlur(RelictScore.blur_splashart))
            line = await pill.recolor_image(lines, self.element_color[:3])
            
        background_blur.alpha_composite(background_dark)
        self.background.paste(background_blur,(0,0), background_maska_blur.convert("L"))
        if self.art:
            self.background = ImageChops.soft_light(self.background, background_overlay)
                
        background_art.alpha_composite(bg.convert("RGBA"))
        background_art.alpha_composite(user_image)
        self.background.paste(background_art,(0,0),maska_art.convert("L"))
        self.background.alpha_composite(shadow)
        self.background.alpha_composite(line, (RelictScore.position_line))
        if not self.remove_logo:
            self.background.alpha_composite(logo)
        
        if not self.hide_uid:
            uid = Image.new(RelictScore.RGBA, RelictScore.bacground_size, (0,0,0,0))
            d = ImageDraw.Draw(uid)
            
            font_10 = await pill.get_font(RelictScore.font_uid)
            d.text((0,0), f"UID: {self.uid}", font=font_10, fill=(255, 255, 255, 75))
            self.background.alpha_composite(uid,RelictScore.position_uid)
    
    async def creat_light_cone(self):
        self.background_light_cones = Image.new(RelictScore.RGBA, (330, 190), (0, 0, 0, 0))
        if self.data.light_cone is None:
            return
        
        background = Image.new(RelictScore.RGBA, (139, 190), (0, 0, 0, 0))
        image = await pill.get_dowload_img(self.data.light_cone.portrait, size=(118, 169))
        background.alpha_composite(image,(9,9))
        light_cone_frame_stars = await get_cone_frame(self.data.light_cone.rarity)
        background.alpha_composite(light_cone_frame_stars)
        light_cone_frame = await _of.light_cone_frame
        background.alpha_composite(light_cone_frame)
        
        self.background_light_cones.alpha_composite(background)
        
        background = await _of.light_cone_stats
        background = background.copy()
        
        d = ImageDraw.Draw(background)
        
        font_18 = await pill.get_font(18)
        d.text((28, 4), self.data.light_cone.attributes[0].display, font=font_18, fill=(255, 255, 255, 255))
        d.text((28, 30), self.data.light_cone.attributes[1].display, font=font_18, fill=(255, 255, 255, 255))
        d.text((28, 56), self.data.light_cone.attributes[2].display, font=font_18, fill=(255, 255, 255, 255))
        
        self.background_light_cones.alpha_composite(background,(140,100))
        
        background = await _of.light_cone_ups
        background = background.copy()
        
        d = ImageDraw.Draw(background)
        font_12 = await pill.get_font(12)
        up = options.ups(self.data.light_cone.rank)
        x = int(font_12.getlength(str(up))/2)
        d.text((10-x, 4), up, font= font_12, fill=(255, 217, 144, 255))
        
        self.background_light_cones.alpha_composite(background,(140,68))
        d = ImageDraw.Draw(self.background_light_cones)
        d.text((165, 69), f"{self.lang.lvl}: {self.data.light_cone.level}/{options.max_lvl(self.data.light_cone.promotion)}", font=font_18, fill=(255, 255, 255, 255))
        
        names = await pill.create_image_with_text(self.data.light_cone.name, 18, max_width=180, color=(255, 255, 255, 255))
        line = Image.new("RGBA", (1,48), options.color_lc_line.get(str(self.data.light_cone.rarity), (150, 202, 255, 255)))
        
        
        self.background_light_cones.alpha_composite(line,(140,9))
        self.background_light_cones.alpha_composite(names,(146,int(34-names.size[1]/2)))   
    
    async def creat_stats(self):
        self.background_stats = Image.new(RelictScore.RGBA, (563, 290), (0, 0, 0, 0))

        combined_attributes = {}
        dop = {}

        for attribute in self.data.attributes + self.data.additions:
            field = attribute.field
            if field in combined_attributes:
                combined_attributes[field].value += attribute.value
                dop[field]["dop"] = f"+{attribute.display}"
            else:
                dop[field] = {"main": attribute.display, "dop": 0}
                combined_attributes[field] = attribute

        dop = {key: value for key, value in dop.items() if value['dop'] != 0}
        
        xp,y = 0,0

        async for i, background in pill.creat_stats(combined_attributes, dop, 
                      RelictScore.stat_font_dop,RelictScore.stat_font,
                      RelictScore.stat_line_size, RelictScore.stat_name_size, RelictScore.stat_max_width, RelictScore.stat_icon_size,
                      RelictScore.stat_icon_position, RelictScore.stat_name_position, RelictScore.stat_x_position,
                      self.element_color, RelictScore.stat_value_font):
            self.background_stats.alpha_composite(background,(xp,y))
            y += 45
            if i == 5:
                xp = 290
                y = 0
 
    async def creat_name(self):
        self.background_name = Image.new(RelictScore.RGBA, RelictScore.name_size, (0, 0, 0, 0))
        d = ImageDraw.Draw(self.background_name)
        names = await pill.create_image_with_text(self.data.name, RelictScore.font_name_size , max_width=RelictScore.name_with, color= RelictScore.name_color)
        if names.size[1] <= RelictScore.name_h_max:
            self.background_name.alpha_composite(names,(RelictScore.name_position,int(RelictScore.name_h_max-names.size[1]/2)))
        else:
            self.background_name.alpha_composite(names,(RelictScore.name_position,int(RelictScore.name_h_min-names.size[1]/2)))
        
        font_17 = await pill.get_font(RelictScore.font_name_level)
        max_level = options.max_lvl(self.data.promotion)
        level = f"{self.lang.lvl}: {self.data.level}/{max_level}"
        d.text(RelictScore.position_name_level, level, font=font_17, fill= RelictScore.color_name_level)
        starts = await options.get_stars(self.data.rarity)
        self.background_name.alpha_composite(starts,RelictScore.position_name_star)
       
    async def creat_constant(self):
        self.background_skills = Image.new(RelictScore.RGBA, RelictScore.constant_size_background, (0, 0, 0, 0))
        x = 0
        rank = self.data.rank
        for key in self.data.rank_icons[:rank]:
            bg = Image.new(RelictScore.RGBA, RelictScore.constant_size, (0, 0, 0, 0))
            icon = await pill.get_dowload_img(key, size=RelictScore.constant_size_icon)
            icon_blur = icon.filter(ImageFilter.GaussianBlur(RelictScore.constant_blur))
            icon_blur = await pill.recolor_image(icon_blur, self.element_color[:3])
            bg.alpha_composite(icon_blur,RelictScore.constant_icon_position)
            bg.alpha_composite(icon,RelictScore.constant_icon_position)
            self.background_skills.alpha_composite(bg,(x,0))
            x += 68

        for key in self.data.rank_icons[rank:]:
            bg = Image.new(RelictScore.RGBA, RelictScore.constant_size, (0, 0, 0, 0))
            icon = await pill.get_dowload_img(key, size= RelictScore.constant_size_icon)
            icon = await pill.apply_opacity(icon, opacity=RelictScore.constant_size_icon_opacity)
            bg.alpha_composite(icon,RelictScore.constant_icon_position)
            self.background_skills.alpha_composite(bg,(x,0))
            x += 68
     
    async def creat_relict_sets(self):
        rel_set = options.calculator_relict_sets(self.data.relic_sets)

        self.background_sets = Image.new(RelictScore.RGBA, RelictScore.sets_background, (0,0,0,0))
        
        font = await pill.get_font(RelictScore.setst_font)
        
        line_items = []
        i = 0
        for key in rel_set:
            sets = rel_set[key]
            holst_line = Image.new(RelictScore.RGBA, RelictScore.setst_line_size, (0,0,0,0))
            background_count = await _of.relict_count_sets
            background_count = background_count.copy()
            d = ImageDraw.Draw(background_count)
            d.text(RelictScore.setst_count_position, str(sets["num"]), font=font, fill= RelictScore.setst_name_color)
            d = ImageDraw.Draw(holst_line)
            sets_name_font,size = await pill.get_text_size_frame(sets["name"],15,492)
            if key[:1] == "1":
                holst_line.alpha_composite(background_count)
                d.text((36, 4), sets["name"], font=sets_name_font, fill=RelictScore.setst_name_color)
                line_items.append({"setap": i, "line": holst_line})
                i += 1
            else:
                holst_line.alpha_composite(background_count,(532,0))
                d.text((int(521-size), 4), sets["name"], font=sets_name_font, fill=RelictScore.setst_name_color)
                line_items.append({"setap": 1, "line": holst_line})
                
        for key in line_items:
            if key["setap"] == 1:
                self.background_sets.alpha_composite(key["line"],(0,29))
            elif key["setap"] == 2:
                self.background_sets.alpha_composite(key["line"],(0,29))
            else:
                self.background_sets.alpha_composite(key["line"],(0,0))
    
    async def creat_relict(self,relict):
        background_main = Image.new(RelictScore.RGBA,RelictScore.relict_size, (0,0,0,0))
        background = Image.new(RelictScore.RGBA,RelictScore.relict_size, (0,0,0,0))
        background_image = Image.new("RGBA",RelictScore.relict_size, (0,0,0,0))
        relict_frame = await _of.relict_frame
        relict_maska = await _of.relict_maska
        relict_score_frame = await _of.relict_score_frame
        relict_line = await _of.relict_line
        
        background_main.alpha_composite(relict_frame)
        
        image = await pill.get_dowload_img(relict.icon, size= RelictScore.relict_icon_size)
        background_image.alpha_composite(image,RelictScore.relict_icon_position)
        background.paste(background_image,(0,0),relict_maska.convert("L"))
        
        background_main.alpha_composite(relict_score_frame,(8,0))
        background_main.alpha_composite(background)
        background_main.alpha_composite(relict_line)
        score = self.score_info["score"].get(str(relict.id),_DEFAULT_SCORE)
        
        main_stat_icon = await pill.get_dowload_img(relict.main_affix.icon, size= RelictScore.relict_main_stat_icon_size)
        color = options.color_element.get(relict.main_affix.type, None)
        if not color is None:
            main_stat_icon = await pill.recolor_image(main_stat_icon, color[:3])
        stars = await options.get_stars(relict.rarity, type = 1)
        
        background_main.alpha_composite(stars,RelictScore.relict_position_star)
        background_main.alpha_composite(main_stat_icon,RelictScore.relict_main_stat_icon_position)
        
        font_22 = await pill.get_font(22)
        font_14 = await pill.get_font(14)
        font_18 = await pill.get_font(18)
        
        d = ImageDraw.Draw(background_main)
        x = 135 - int(font_22.getlength(relict.main_affix.display))
        d.text((x, 86), relict.main_affix.display, font= font_22, fill= (255,255,255,255))
        
        x = 121 - int(font_22.getlength(f"+{relict.level}")/2)
        d.text((x, 110), f"+{relict.level}", font= font_14, fill= self.element_color)
        
        y_icon = 21
        y_roll = 30
        y_text = 27
                
        eff = 0
        
        for k in relict.sub_affix:
            icon = await pill.get_dowload_img(k.icon, size=RelictScore.relict_sub_icon_size)
            background_main.alpha_composite(icon,(142,y_icon))
            scoreR = score["rolls"].get(k.type,0)
            if k.type in self.score_info["bad"]:
                d.text((173, y_text), str(k.display), font=font_18, fill= (255,255,255,255))
            else:
                eff += 1
                d.text((173, y_text), str(k.display), font=font_18, fill= options.color_scoreR.get(scoreR,(255,255,255,255)))
            d.text((232, y_roll), f"+{scoreR}", font=font_18, fill= options.color_scoreR.get(scoreR,(255,255,255,255)))
            y_icon += 27
            y_text += 28
            y_roll += 27
            
        d.text((22, 4), "Score:", font=font_14, fill = (255,255,255,255))
        d.text((70, 4), str(score["count"]), font=font_14, fill = score["rank"]["color"])
        
        d.text((105, 4), "Rank:", font=font_14, fill = (255,255,255,255))
        d.text((149, 4), str(score["rank"]["name"]), font=font_14, fill = score["rank"]["color"])
        
        d.text((187, 4), "Eff Stat:", font=font_14, fill = (255,255,255,255))
        d.text((247, 4), str(eff), font=font_14, fill = options.color_scoreR.get(eff,(255,255,255,255)))
        
        self.totall_eff += eff
        
        line_f = await _of.relict_frame_line
        line_f = line_f.copy()
        line_f = await pill.recolor_image(line_f, self.element_color[:3])
        background_main.alpha_composite(line_f,(0,21))
        
        return {"position": str(relict.id)[-1:] , "img": background_main}
    
    async def get_path(self):
        self.background_path = await pill.creat_path(self.data)
    
    async def get_score(self):
        self.score_info = await stats.Calculator(self.data).start()
        
    async def build_relict(self):
        self.background_relict = Image.new(RelictScore.RGBA, (1131,297), (0,0,0,0))
        none_relict = await _of.none_relict
        position = {
            "1": (0,163),
            "2": (286,163),
            "3": (572,163),
            "4": (858,163),
            "5": (572,0),
            "6": (858,0),
        }
        
        map = {"1": None, "2": None, "3": None, "4": None, "5": None, "6": None}
        
        for key in self.relict:
            self.background_relict.alpha_composite(key["img"], position.get(key["position"]))
            map[key["position"]] = 0
        
        for key in map:
            if map[key] is None:
                self.background_relict.alpha_composite(none_relict, position.get(key))

        self.background_relict.alpha_composite(self.background_score,(0,112))
        self.background_relict.alpha_composite(self.background_sets,(0,45))
    
    async def creat_seeleland(self):
        self.seelelen = Image.new("RGBA",(1,1), (0,0,0,0))
        
        if self.seeleland:
            self.seelelen = await _of.seeleland_v2
            self.seelelen = self.seelelen.copy()
            font = await pill.get_font(15)
            data = await options.get_seeleland(self.uid, self.data.id)
            if data is None:
                self.seelelen = Image.new("RGBA",(1,1), (0,0,0,0))
                return None
            if not "rank" in data:
                self.seelelen = Image.new("RGBA",(1,1), (0,0,0,0))
                return None
            draw = ImageDraw.Draw(self.seelelen)
            draw.text((143, 7), str(data["sc"]), font=font, fill=(255, 255, 255, 255))
            draw.text((136, 24), f'{data["rank"][:-2]}..', font=font, fill=(255, 255, 255, 255))
            draw.text((127, 42), data["percrank"].replace("top ", ""), font=font, fill=(255, 255, 255, 255))
    
    async def creat_score_total(self):
        self.background_score = Image.new("RGBA",(559,43), (0,0,0,0))
        
        sclor_bg = await _of.relict_backgroundl_score_line
        sclor_bg = sclor_bg.copy()
        sclor_bg_color = await _of.relict_full_score_line
        sclor_bg_color = sclor_bg_color.copy()
        sclor_bg_color = await pill.recolor_image(sclor_bg_color, self.element_color[:3])
        
        result_percentage = (self.score_info["total_score"]['count'] / 260.53) * 100
        pixels_to_fill = int((result_percentage / 100)*599)
        if pixels_to_fill <= 0:
            pixels_to_fill = 1
        sclor_bg.alpha_composite(sclor_bg_color.resize((pixels_to_fill,16)))
        
        font_18 = await pill.get_font(18)
        
        d = ImageDraw.Draw(sclor_bg)
        x = 280 - int(font_18.getlength(f"{round(result_percentage,1)}%")/2)
        frame = await pill.get_colors(await pill.crop_image(sclor_bg), 15, common=True, radius=5, quality=800) 
        ll = await pill.light_level(frame)
        if ll > 0.6 or frame == (51, 51, 51):
            d.text((x, -1), f"{round(result_percentage,1)}%", font=font_18, fill = (0,0,0,166))
        else:
            d.text((x, -1), f"{round(result_percentage,1)}%", font=font_18, fill = (255,255,255,255))
        
        self.background_score.alpha_composite(sclor_bg,(0,27))
        
        font_21 = await pill.get_font(21)
        d = ImageDraw.Draw(self.background_score)
        
        d.text((249, -2), "Score:", font=font_21, fill = (255,255,255,255))
        d.text((320, -2), str(self.score_info["total_score"]['count']), font=font_21, fill = self.score_info["total_score"]["rank"]["color"])
        
        d.text((17, -2), "Summary Rank:", font=font_21, fill = (255,255,255,255))
        d.text((189, -2), str(self.score_info["total_score"]["rank"]["name"]), font=font_21, fill = self.score_info["total_score"]["rank"]["color"])
        
        d.text((393, -2), "Eff Stat:", font=font_21, fill = (255,255,255,255))
        d.text((497, -2), str(self.totall_eff), font=font_21, fill = options.color_scoreR.get(self.totall_eff,(255,255,255,255)))   
    
    async def build(self):
        bg = Image.new(RelictScore.RGBA, RelictScore.bacground_size, (0,0,0,0))
        bg.alpha_composite(self.background_light_cones,(782,16))
        bg.alpha_composite(self.background_stats,(781,220))
        bg.alpha_composite(self.background_name,(6,654))
        bg.alpha_composite(self.background_skills.resize((320,47)),(1033,134))
        bg.alpha_composite(self.background_path.resize((488,428)),(1409,16))
        bg.alpha_composite(self.background_relict,(781,473))
        bg.alpha_composite(self.seelelen,(1120,35))
        
        if self.gif:
            self.background = []
            for key in self.GIFT_BG:
                key.alpha_composite(bg)
                self.background.append(key.convert("RGB"))
        else:
            self.background.alpha_composite(bg)
                    
    async def start(self):   
        _of.set_mapping(1)
        
        if self.art:
            if "gif" in self.art:
                self.gif = True
            self.art = await pill.get_user_image(self.art)
            
        if self.gif:    
            n = 2           
            frame_count = 0
            color = True
            with io.BytesIO(self.art) as f:
                self.art = Image.open(f)
                for frame in ImageSequence.Iterator(self.art):
                    if color:
                        self.element_color = await pill.get_colors(frame.convert("RGBA"), 15, common=True, radius=5, quality=800)
                        color = False
                    if frame_count >= 50:
                        break
                    if frame_count % n != 0:
                        self.art = frame.convert("RGBA")
                        await self.creat_bacground()
                        self.GIFT_BG.append(self.background.copy())
                    frame_count += 1            
        else:
            if self.art:
                self.element_color = await pill.get_colors(self.art, 15, common=True, radius=5, quality=800)
            
            await self.creat_bacground()
            
        await asyncio.gather(
            self.creat_light_cone(),
            self.creat_stats(),
            self.creat_name(),
            self.creat_constant(),
            self.creat_relict_sets(),
            self.get_score(),
            self.get_path(),
            self.creat_seeleland()
        )
        relic_tasks = [self.creat_relict(key) for key in self.data.relics]
        self.relict = await asyncio.gather(*relic_tasks)
        
        await self.creat_score_total()
        
        await self.build_relict()
        await self.build()
        
        data = {
            "id": self.data.id,
            "animation":  self.gif,
            "name": self.data.name,
            "rarity": self.data.rarity,
            "card": self.background,
            "size": RelictScore.bacground_size
        }
        
        return data