using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RimWorld;
using Verse;
using HarmonyLib;
using UnityEngine;
using rjw;
using Rimworld_Animations;
using System.Reflection;

//since I test system alot, source cord is very dirty and not optimized.


namespace SizedApparel
{
    public struct supportedIndex
    {

    }

    [StaticConstructorOnStartup]
    [HarmonyPatch(typeof(Pawn_HealthTracker), "Notify_HediffChanged")]
    public class PawnHealthTrackerPatch
    {
        public static void Postfix(Hediff hediff, Pawn_HealthTracker __instance, ref Pawn ___pawn)
        {
            if (___pawn == null)
                return;
            var comp = ___pawn.GetComp<ApparelRecorderComp>();
            if (comp == null)
                return;
            if (hediff == null)
                return;
            
            if (SizedApparelUtility.isRJWParts(hediff))///
            {
                //comp.ClearAll();
                //comp.Update();
                comp.SetDirty(false,true,false);
                //already doing set dirty in hediffchange method.
                //___pawn.Drawer.renderer.graphics.SetApparelGraphicsDirty();
                //PortraitsCache.SetDirty(___pawn);
                //GlobalTextureAtlasManager.TryMarkPawnFrameSetDirty(___pawn);
                return;
            }
            if (SizedApparelUtility.isBellyBulgeHediff(hediff))
            {
                comp.SetDirty(false, true, false);
                return;
            }

        }
    }


    [StaticConstructorOnStartup]
    [HarmonyPatch(typeof(Pawn_AgeTracker), "PostResolveLifeStageChange")]
    public class PawnAgeTrackerPatch
    {
        public static void Postfix(Pawn ___pawn)
        {
            var comp = ___pawn.GetComp<ApparelRecorderComp>();
            if (comp == null)
                return;
            comp.SetDirty(false,false,false,true,true);//Apparel and Hediff will be changed with other reason. just set skeleton dirty.
        }
    }



    [StaticConstructorOnStartup]
    public class HeddifPatchForRimNudeWorld
    {
        //hediff.get_Severity()
        public static void GetSeverityPostFix(Hediff __instance)
        {
            if (__instance.Part != null)
            {

                if (__instance.Part.def.defName.Equals(SizedApparelUtility.chestString))
                {
                    if (__instance.def.defName.EndsWith(SizedApparelUtility.breastsString))
                    {
                        //Log.Message("Found Breast Hediff");
                        //_breastSeverity = __instance.Severity;
                        //_breastHediff = __instance;
                        //result = true;
                        //Log.Message(_breastSeverity.ToString());
                    }
                }
            }
        }

        //...get_severity()
        /*
        public static void BodyAddonHediffSeverityGraphicPatch(AlienRace.AlienPartGenerator.BodyAddonHediffSeverityGraphic __instance, ref float __result)
        {
            if (!SizedApparelPatch.rimNudeWorldActive) { return; }
            if (!SizedApparelSettings.matchBreastToSupportedApparelSize) { return; }
            if (__instance.path.Contains(SizedApparelUtility.breastsString))
            {

            }
        }*/
    }




    [Obsolete]
    [StaticConstructorOnStartup]
    public class BodyPatch
    {
        public static void SetBodyGraphic(Pawn pawn, bool drawClothFlag = true, bool fromGraphicRecord = true, bool revert = false)
        {
            //Pawn_ApparelTracker __instance;


            //GetBreastSeverity(__instance.pawn, out breastSeverity, out breastHediff);
            //bool flag = hasUnSupportedApparel(__instance.pawn, breastSeverity, breastHediff);
            ApparelRecorderComp comp = pawn.GetComp<ApparelRecorderComp>();
            if (comp == null)
                return;

            if (revert)
            {
                var pawnRenderer = pawn.Drawer?.renderer?.graphics;
                //Log.Message("useBodyTexture");

                /*
                if (!pawnRenderer.AllResolved)
                {
                    pawnRenderer.ResolveAllGraphics();
                }*/

                if (pawnRenderer == null)
                    return;
                if (comp.graphicSourceNaked != null)
                    pawnRenderer.nakedGraphic = comp.graphicSourceNaked.GetColoredVersion(pawnRenderer.nakedGraphic.Shader, pawnRenderer.nakedGraphic.color, pawnRenderer.nakedGraphic.colorTwo);
                if (comp.graphicSourceRotten != null)
                    pawnRenderer.rottingGraphic = comp.graphicSourceRotten.GetColoredVersion(pawnRenderer.rottingGraphic.Shader, pawnRenderer.rottingGraphic.color, pawnRenderer.rottingGraphic.colorTwo);
                if (comp.graphicSourceFurCovered != null)
                    pawnRenderer.furCoveredGraphic = comp.graphicSourceRotten.GetColoredVersion(pawnRenderer.furCoveredGraphic.Shader, pawnRenderer.furCoveredGraphic.color, pawnRenderer.furCoveredGraphic.colorTwo);

            }


            if (!comp.hasUpdateBefore)
            {
                //comp.Update(true, fromGraphicRecord);
            }

            float breastSeverity = comp.breastSeverity;
            Hediff breastHediff = comp.breastHediff;


            if (SizedApparelSettings.drawBodyParts == true && SizedApparelSettings.useBodyTexture)//old:SizedApparelSettings.useBodyTexture
            {
                //if (SizedApparelSettings.Debug)
                //    Log.Message("[Sized Apparel] Trying to change" + pawn.Name + "'s body texture.");

                var pawnRenderer = pawn.Drawer?.renderer?.graphics;
                //Log.Message("useBodyTexture");

                /*
                if (!pawnRenderer.AllResolved)
                {
                    pawnRenderer.ResolveAllGraphics();
                }*/
                if (pawnRenderer == null)
                    return;

                if (!comp.hasUnsupportedApparel || SizedApparelUtility.isPawnNaked(pawn) || !drawClothFlag)
                {

                    if (comp.graphicbaseBodyNaked != null)
                    {
                        pawnRenderer.nakedGraphic = comp.graphicbaseBodyNaked.GetColoredVersion(pawnRenderer.nakedGraphic.Shader, pawnRenderer.nakedGraphic.color, pawnRenderer.nakedGraphic.colorTwo);

                    }
                    if (comp.graphicbaseBodyRotten != null)
                    {
                        pawnRenderer.rottingGraphic = comp.graphicbaseBodyRotten.GetColoredVersion(pawnRenderer.rottingGraphic.Shader, pawnRenderer.rottingGraphic.color, pawnRenderer.rottingGraphic.colorTwo);

                    }
                    if (comp.graphicbaseBodyFurCovered != null)
                    {
                        pawnRenderer.furCoveredGraphic = comp.graphicbaseBodyFurCovered.GetColoredVersion(pawnRenderer.furCoveredGraphic.Shader, pawnRenderer.furCoveredGraphic.color, pawnRenderer.furCoveredGraphic.colorTwo);

                    }
                }
                else
                {
                    if (comp.graphicSourceNaked != null)
                        pawnRenderer.nakedGraphic = comp.graphicSourceNaked.GetColoredVersion(pawnRenderer.nakedGraphic.Shader, pawnRenderer.nakedGraphic.color, pawnRenderer.nakedGraphic.colorTwo);
                    if (comp.graphicSourceRotten != null)
                        pawnRenderer.rottingGraphic = comp.graphicSourceRotten.GetColoredVersion(pawnRenderer.rottingGraphic.Shader, pawnRenderer.rottingGraphic.color, pawnRenderer.rottingGraphic.colorTwo);
                    if (comp.graphicSourceFurCovered != null)
                        pawnRenderer.furCoveredGraphic = comp.graphicSourceFurCovered.GetColoredVersion(pawnRenderer.furCoveredGraphic.Shader, pawnRenderer.furCoveredGraphic.color, pawnRenderer.furCoveredGraphic.colorTwo);

                }


                return;//no need to replace full body texture anymore.

                /*
                if (SizedApparelSettings.useBodyTexture == false)
                    return;

                //Log.Message("Hello");
                if (pawnGraphicSet == null)
                    return;

                if (pawnGraphicSet.pawn.RaceProps.Humanlike == false)
                    return;

                return;


                //Log.Message("SetBodyGraphic");
                //pawnGraphicSet.ClearCache();
                string path = pawnGraphicSet.pawn.story.bodyType.bodyNakedGraphicPath;
                string path_bodyDessicated = pawnGraphicSet.pawn.story.bodyType.bodyDessicatedGraphicPath;
                string filename;
                string pathname;
                string filename_bodyDessicated;
                string pathname_bodyDessicated;
                pathname = System.IO.Path.GetDirectoryName(path);
                filename = System.IO.Path.GetFileName(path);
                filename_bodyDessicated = System.IO.Path.GetFileName(path_bodyDessicated);
                pathname_bodyDessicated = System.IO.Path.GetDirectoryName(path_bodyDessicated);

                //Log.Message("SetPath");
                bool validBody = false;
                bool validDessicatedBody = false;

                //pawnGraphicSet.pawn.Drawer.renderer.graphics.nakedGraphic
                //pawnGraphicSet.pawn.Drawer.renderer.graphics.dessicatedGraphic

                if (hasUnsupportApparel)
                {
                    //Log.Message("IfhasUnsupportApparel");
                    //Graphic newBodyGraphic = null;
                    //Graphic newRottingGraphic = null;
                    //string path;

                    //Log.Message("tryUnsupportedApparelBodyTexture");
                    //OLD::pawnGraphicSet.pawn.Drawer.renderer.graphics
                    if (pawnGraphicSet.nakedGraphic != null)
                        if (ContentFinder<Texture2D>.Get((pawnGraphicSet.pawn.Drawer.renderer.graphics.nakedGraphic.path + "_UnsupportedApparel" + "_south"), false) != null)
                        {
                            pawnGraphicSet.pawn.Drawer.renderer.graphics.nakedGraphic = GraphicDatabase.Get<Graphic_Multi>(pawnGraphicSet.nakedGraphic.path + "_UnsupportedApparel", pawnGraphicSet.nakedGraphic.Shader, pawnGraphicSet.nakedGraphic.drawSize, pawnGraphicSet.nakedGraphic.color, pawnGraphicSet.nakedGraphic.colorTwo);
                            validBody = true;
                        }
                    if (pawnGraphicSet.dessicatedGraphic != null)
                        if (ContentFinder<Texture2D>.Get((pawnGraphicSet.dessicatedGraphic.path + "_UnsupportedApparel" + "_south"), false) != null)
                        {
                            pawnGraphicSet.dessicatedGraphic = GraphicDatabase.Get<Graphic_Multi>(pawnGraphicSet.dessicatedGraphic.path + "_UnsupportedApparel", pawnGraphicSet.rottingGraphic.Shader, pawnGraphicSet.rottingGraphic.drawSize, pawnGraphicSet.rottingGraphic.color, pawnGraphicSet.rottingGraphic.colorTwo);
                            validDessicatedBody = true;
                        }
                }
                else
                {
                    //Log.Message("undo");
                    string currentPath = pawnGraphicSet.nakedGraphic.path;
                    string currentDessicatedPath = pawnGraphicSet.dessicatedGraphic.path;
                    //Log.Message(currentPath.Substring(0,currentPath.Length - "_UnsupportedApparel".Length));
                    if (pawnGraphicSet.nakedGraphic != null)
                        if (ContentFinder<Texture2D>.Get(currentPath.Substring(0, currentPath.Length - "_UnsupportedApparel".Length) + "_south", false) != null)
                        {
                            pawnGraphicSet.nakedGraphic = GraphicDatabase.Get<Graphic_Multi>((currentPath.Substring(0, currentPath.Length - "_UnsupportedApparel".Length)), pawnGraphicSet.nakedGraphic.Shader, pawnGraphicSet.nakedGraphic.drawSize, pawnGraphicSet.nakedGraphic.color, pawnGraphicSet.nakedGraphic.colorTwo);

                        }
                    if (pawnGraphicSet.dessicatedGraphic != null)
                        if (ContentFinder<Texture2D>.Get(currentDessicatedPath.Substring(0, currentDessicatedPath.Length - "_UnsupportedApparel".Length) + "_south", false) != null)
                        {
                            pawnGraphicSet.dessicatedGraphic = GraphicDatabase.Get<Graphic_Multi>((currentDessicatedPath.Substring(0, currentDessicatedPath.Length - "_UnsupportedApparel".Length)), pawnGraphicSet.nakedGraphic.Shader, pawnGraphicSet.nakedGraphic.drawSize, pawnGraphicSet.nakedGraphic.color, pawnGraphicSet.nakedGraphic.colorTwo);

                        }

                }*/
                //pawnGraphicSet.ClearCache();
            }
        }






        private static void GetBreastSeverity(Pawn pawn, out float breastSeverity, out Hediff breastHediff)
        {
            throw new NotImplementedException();
        }
    }


    [Obsolete]  
    [StaticConstructorOnStartup]
    //[HarmonyPatch(typeof(Pawn_ApparelTracker), "ExposeData")]
    class ApparelTrackerExposePatch
    {
        static void Postfix(Pawn_ApparelTracker __instance)
        {
            if (!UnityData.IsInMainThread)
            {
                return;
            }
            if (__instance.pawn == null)
                return;
            if (Scribe.mode == LoadSaveMode.PostLoadInit)
            {
                ApparelRecorderComp comp = __instance.pawn.GetComp<ApparelRecorderComp>();
                if (comp == null)
                    return;
                if (!comp.hasUpdateBefore)
                    comp.Update(true, false);
            }

        }
    }


    //[StaticConstructorOnStartup]
    //[HarmonyPatch(typeof(Pawn_ApparelTracker), "Notify_ApparelChanged")]
    //rimworld 1.2 => apparelChanged, rimworld 1.3 => apparelAdded, apparelRemoved
    public class ApparelTrackerPatch
    {
        //Prefix
        public static void Changed(Pawn_ApparelTracker __instance)
        {
            /*
            if (Current.Game.World == null)
                return;
            */
            if (!UnityData.IsInMainThread)
            {
                return;
            }
            if (__instance.pawn == null)
                return;

            if (SizedApparelSettings.Debug)
                Log.Message("[Sized Apparel] " + __instance.pawn.Name + "'s apparels are changed. updating sizedApparels for it.");

            //GetBreastSeverity(__instance.pawn, out breastSeverity, out breastHediff);
            //bool flag = hasUnSupportedApparel(__instance.pawn, breastSeverity, breastHediff);
            ApparelRecorderComp comp = __instance.pawn.GetComp<ApparelRecorderComp>();
            if (comp != null)
            {
                //comp.ClearBreastCacheValue();
                //comp.ClearAll();
                //bool flag = false;
                //SizedApparelUtility.GetBreastSeverity(__instance.pawn, out breastSeverity, out breastHediff);
                //flag = SizedApparelUtility.hasUnSupportedApparel(__instance.pawn, breastSeverity, breastHediff);
                //flag = SizedApparelUtility.hasUnSupportedApparelFromWornData(__instance.pawn, breastSeverity, breastHediff);
                //comp.hasUpdateBefore = true;
                //comp.hasUnsupportedApparel = flag;
                //comp.breastHediff = breastHediff; ;
                //comp.breastSeverity = breastSeverity;
                //comp.Update(true, false);//TODO: Coverd But No Graphic may cause Big Issue!!!
                //comp.Update(true, true);

                comp.SetDirty(false,false,true);

                /*
                if (SizedApparelSettings.drawBodyParts)//old:SizedApparelSettings.useBodyTexture
                    BodyPatch.SetBodyGraphic(__instance.pawn);
                */
                /*
                //Log.Message(comp.testbool.ToString());
                //Log.Message("ApparelChanged");
                //comp.hasUnsupportedApparel = flag;
                //comp.hasUpdateBefore = true;
                //comp.SetHasUnsupportedApparel(flag);
                //comp.SetHasUpdateBefore(true);
                //if (__instance.pawn.Drawer.renderer.graphics != null)


                LongEventHandler.ExecuteWhenFinished(delegate
                {
                    SetBodyGraphic(__instance.pawn.Drawer.renderer.graphics, flag);
                }
                );
                */

            }

        }
    }
    [StaticConstructorOnStartup]
    //[HarmonyPatch(typeof(PawnGraphicSet), "SetApparelGraphicsDirty")]
    class SetApparelGraphicsDirtyPatch
    {
        public static bool Prefix(PawnGraphicSet __instance)
        {
            if (SizedApparelSettings.useBodyTexture)
            {
                //__instance.SetAllGraphicsDirty();
                //return false;
            }
            return true;
        }
    }

    //TODO
    [StaticConstructorOnStartup]
    //[HarmonyPatch(typeof(PawnGraphicSet), "get_apparelGraphics")]
    class PrivatePartsPatch
    {
        public static void Postfix(PawnGraphicSet __instance, ref List<ApparelGraphicRecord> __result)
        {
            var resualtCach = __result;

            if (SizedApparelSettings.drawBodyParts)
            {
                var privateParts = new List<ApparelGraphicRecord>();
                if (SizedApparelSettings.drawPenis)
                {

                }
                if (SizedApparelSettings.drawVagina)
                {

                }
                if (SizedApparelSettings.drawHips)
                {

                }
                if (SizedApparelSettings.drawHips)
                {

                }
                if (SizedApparelSettings.drawAnus)
                {

                }
            }
        }
    }



    //[HarmonyPatch(typeof(PawnGraphicSet), "MatsBodyBaseAt")]
    public class MatBodyBaseAtPatch
    {
        public static void Postfix(PawnGraphicSet __instance, Rot4 facing, RotDrawMode bodyCondition, bool drawClothes, List<Material> __result)
        {
            if (__result == null)
                return;
            int num = facing.AsInt + 1000 * (int)bodyCondition;
            List<Material> copy;
            copy = __result.ListFullCopy();
            for (int i = 0;  i<__result.Count; i++)
            {
                //SizedApparelsDatabase.GetSupportedApparelOriginalPath(__result[i].g)
            }
        }

    }


    //Apparel Graphic Texture injection
    [StaticConstructorOnStartup]
    [HarmonyPatch(typeof(ApparelGraphicRecordGetter), "TryGetGraphicApparel")]
    [HarmonyBefore(new string[]{"QualityOfBuilding"})]
    public class GetApparelGraphicFix
    {
        public static void Postfix(Apparel apparel, BodyTypeDef bodyType, ref ApparelGraphicRecord rec, ref bool __result)
        {
            if (__result == false)
                return;

            if (apparel == null)
                return;

            if (bodyType == null)
                return;

            if (apparel.Wearer != null)
            {
                //rec = new ApparelGraphicRecord(null, null);
                var comp = apparel.Wearer.GetComp<ApparelRecorderComp>();

                //if (SizedApparelSettings.matchBodyTextureToMinimumApparelSize)
                //    BreastSeverity = comp.BreastSeverityCache;
                //int currentBreastSizeIndex = 0;
                //float currentBreastSeverity = -1;
                //int minSupportedBreastSizeIndex = 1000;
                //float minSupportedBreastSeverity = 1000;

                //SizedApparelUtility.GetBreastSeverity(apparel.Wearer, out BreastSeverity, out breastHediff);
                if (comp != null)
                {

                    if (comp.hasUpdateBefore == false)
                    {
                        //SizedApparelUtility.GetBreastSeverity(apparel.Wearer, out BreastSeverity, out breastHediff);
                        //comp.hasUnsupportedApparel = SizedApparelUtility.hasUnSupportedApparelFromWornData(apparel.Wearer, BreastSeverity, breastHediff);
                        //comp.breastSeverity = BreastSeverity;
                        //comp.breastHediff = breastHediff;
                        //comp.hasUpdateBefore = true;
                        //comp.Update(true,false);
                    }
                    if (comp.isDirty == true)
                    {
                        //return;
                        //comp.ClearAll();
                        //comp.Update(true, false);
                    }
                    /*
                    if (comp.needToCheckApparelGraphicRecords)
                    {
                        TODO;
                        if (comp.isApparelGraphicRecordChanged())
                            comp.Update(true, false);
                           
                    }*/
                    if (comp.needToCheckApparelGraphicRecords)
                    {
                        /*
                        if (comp.isApparelGraphicRecordChanged())
                        {
                            //return;
                            //comp.Update(true, true); //1.3
                            //SizedApparelUtility.UpdateAllApparel(___pawn, true);
                        }*/
                    }

                    //Log.Message("1");
                    var breastHediff = comp.breastHediff;
                    float BreastSeverity = comp.breastSeverity;
                    //Log.Message("2");

                    if (SizedApparelSettings.useBreastSizeCapForApparels) //SizedApparelSettings.useBreastSizeCapForApparels //wip
                            BreastSeverity = Math.Min(comp.BreastSeverityCache, BreastSeverity);
                    //Log.Message("3");
                    if (comp.hasUnsupportedApparel == false)//&& (comp.bodyPartBreasts.bodyPartGraphic !=null || comp.bodyPartBreasts.bodyPartGraphicHorny != null)
                    {
                        Graphic sizedGraphic = null;
                        //Log.Message("4");
                        string resultPath = SizedApparelsDatabase.GetSupportedApparelSizedPath(new SizedApparelsDatabase.SizedApparelDatabaseKey(rec.graphic.path, apparel?.Wearer?.def.defName, breastHediff?.def?.defName, apparel.Wearer.gender, apparel?.Wearer?.story?.bodyType?.defName, SizedApparelUtility.BreastSeverityInt(BreastSeverity))).pathWithSizeIndex;
                        if(resultPath != null)
                        {
                            //sizedGraphic = SizedApparelUtility.GetSizedApparelGraphic(rec.graphic, BreastSeverity, apparel?.Wearer?.def.defName, breastHediff.def.defName);
                            sizedGraphic = GraphicDatabase.Get<Graphic_Multi>(resultPath, rec.graphic.Shader, rec.graphic.drawSize, rec.graphic.color, rec.graphic.colorTwo);
                        }



                        if(sizedGraphic != null)
                        rec = new ApparelGraphicRecord(sizedGraphic, rec.sourceApparel);

                        //minSupportedBreastSizeIndex = Math.Min(currentBreastSizeIndex, minSupportedBreastSizeIndex);
                        //comp.breastSeverityCapToDraw = Math.Min(comp.breastSeverityCapToDraw, minSupportedBreastSeverity);
                    }
                }

                else
                {
                    if (SizedApparelSettings.Debug)
                        Log.Warning("[Sized Apparel] " + apparel.Wearer.Name + " doesn't have SizedApparel Compoenet!!");

                }
            }
        }
    }
        
    [StaticConstructorOnStartup]
    [HarmonyPatch(typeof(PawnGraphicSet), "ClearCache")]
    class GraphicSetClearFix
    {
        public static void Postfix(PawnGraphicSet __instance)
        {
            if (__instance.pawn == null)
            {
                return;
            }
            var comp = __instance.pawn.GetComp<ApparelRecorderComp>();
            if (comp == null)
                return;
            //comp.ClearAll(false);
            //comp.needToCheckApparelGraphicRecords = true;
            comp.SetDirty(false,true,false,false,false); // Check Hediff. If you don't the crotch will not have graphic on first load
        }
    }



       
    

    //[HarmonyPatch(typeof(PawnGraphicSet), "MatsBodyBaseAt")]
    public class PawnGraphicSetPatch
    {
        public static void Postfix(ref List<Material> __result, ref PawnGraphicSet __instance, Rot4 facing, RotDrawMode bodyCondition)
        {

        }
    }

    //TODO: Patch After RJW (Sexualize_GenerateNewPawnInternal) or just postfix to Sexualize
    [HarmonyPatch(typeof(PawnGenerator), "GenerateNewPawnInternal")]
    public class PawnGeneratorPatch
    {

    }

    [HarmonyPatch(typeof(Corpse), "RotStageChanged")]
    public class RotStagePatch
    {
        public static void Prefix(CompRottable __instance)
        {
            var comp = __instance.parent.GetComp<ApparelRecorderComp>();
            if (comp == null)
                return;
            comp.SetDirty(false,false,false); // should clear graphicSet....?
        }
    }

    //Should I Patch this?
    //[HarmonyPatch(typeof(Pawn_AgeTracker), "RecalculateLifeStageIndex")]
    public class AgePatch
    {
        public static void Postfix(Pawn_AgeTracker __instance, Pawn ___pawn)
        {
            var comp = ___pawn.GetComp<ApparelRecorderComp>();
            if (comp == null)
                return;
            comp.CheckAgeChanged();
        }
    }



    //Styling Station patch..? wip
    //[HarmonyPatch(typeof(PawnGraphicSet), "MatsBodyBaseAt")]
    public class BodyMatPatch
    {
        public static void Postfix(PawnGraphicSet __instance, RotDrawMode bodyCondition, Rot4 facing, ref List<Material> __result, bool drawClothes)
        {
            List<Material> loc = new List<Material>();

            if (bodyCondition == RotDrawMode.Dessicated)
            {
                return;
            }

            for (int i = 0; i< __result.Count; i++)
            {
                if (bodyCondition == RotDrawMode.Fresh)
                {
                    if (__result[i] == __instance.nakedGraphic.MatAt(facing, null))
                    {

                        continue;
                    }

                }
                else if (bodyCondition == RotDrawMode.Rotting || __instance.dessicatedGraphic == null)
                {
                    if (__result[i] == __instance.rottingGraphic.MatAt(facing, null))
                    {


                        continue;
                    }
                }

                if (drawClothes)
                {


                }


                loc.Add(__result[i]);
            }

        }
    }
    //RimWorld 1.3
    //WIPWIPWIP TODO
    [HarmonyPatch(typeof(PawnRenderer), "BaseHeadOffsetAt")]
    public class BaseHeadOffsetAtPatch
    {
        public static void Postfix(ref PawnRenderer __instance, Pawn ___pawn, Rot4 rotation, ref Vector3 __result)
        {
            ApparelRecorderComp apparelRecorder = ___pawn.GetComp<ApparelRecorderComp>();
            if (apparelRecorder == null)
                return;
            if (apparelRecorder.customPose != null)
            {
                //var item = apparelRecorder.currentCustomPose.headOffset.FirstOrDefault(b => b.bodyType == ___pawn.story.bodyType.label);
                //__result += item.offsets.GetOffset(rotation);
            }
        }
    }

    [HarmonyPatch(typeof(Pawn_GeneTracker), "Notify_GenesChanged")]
    public class GeneChangePatch
    {
        public static void Postfix(Pawn_GeneTracker __instance, GeneDef addedOrRemovedGene)
        {
            ApparelRecorderComp apparelRecorder = __instance.pawn.GetComp<ApparelRecorderComp>();
            if (apparelRecorder == null)
                return;
            if (addedOrRemovedGene.bodyType!= null)
            {
                /*
                apparelRecorder.Update();
                if (SizedApparelSettings.drawBodyParts)
                    BodyPatch.SetBodyGraphic(__instance.pawn);
                    */
                apparelRecorder.SetDirty(false,false,false,true);
                return;
            }
            if(addedOrRemovedGene.graphicData != null)
            {
                if(addedOrRemovedGene.graphicData.fur != null)
                {
                    apparelRecorder.SetDirty(false,false,false,true);
                    return;
                }
            }
        }
    }

    //RimWorld 1.3 , 1.4
    [HarmonyPatch(typeof(PawnRenderer), "DrawPawnBody")]
    public class DrawPawnBodyPatch
    {
        static MethodInfo overrideMatMethod = AccessTools.Method(typeof(PawnRenderer), "OverrideMaterialIfNeeded");


        public static void Prefix(ref PawnRenderer __instance, Pawn ___pawn, PawnRenderFlags flags)
        {
            if (!SizedApparelSettings.drawBodyParts)
                return;
            if (___pawn == null)
                return;
            ApparelRecorderComp apparelRecorder = ___pawn.GetComp<ApparelRecorderComp>();
            if (apparelRecorder == null)
                return;
            //!flags.FlagSet(PawnRenderFlags.StylingStation)&&
            bool flag = false;
            //if (apparelRecorder.needToCheckApparelGraphicRecords && apparelRecorder.isApparelGraphicRecordChanged())
            /*
            if (apparelRecorder.isApparelGraphicRecordChanged())
            {
                flag = true;
            }
            */

            if (false && flags.FlagSet(PawnRenderFlags.StylingStation))//TODO...?
            {
                //apparelRecorder.isDirty is allways true

                if (true)//StylingStation Doesn't work with cache! patch in postfix
                {
                    if(apparelRecorder.recentClothFlag != flags.FlagSet(PawnRenderFlags.Clothes))
                        apparelRecorder.Update(true, true, true, flags.FlagSet(PawnRenderFlags.Clothes));
                    if(flags.FlagSet(PawnRenderFlags.Clothes))
                        SizedApparelUtility.UpdateAllApparel(___pawn, true);
                }


            }
            else
            {
                if ((!apparelRecorder.hasUpdateBefore || apparelRecorder.isDirty))
                {
                    if (SizedApparelSettings.Debug)
                        Log.Message("[SizedApparel] trying to draw " + ___pawn.Name + " with unupdated component or SetDirty! Updating it.");
                    //apparelRecorder.ClearAll();
                    //Todo. Async Update?
                    apparelRecorder.Update(true, true, true, flags.FlagSet(PawnRenderFlags.Clothes));
                    SizedApparelUtility.UpdateAllApparel(___pawn,true);
                }
                if (flag)
                {
                    //apparelRecorder.Update(true, true, true, flags.FlagSet(PawnRenderFlags.Clothes));
                    //apparelRecorder.Update(true, true); 1.3
                    //SizedApparelUtility.UpdateAllApparel(___pawn, true);
                }
            }
            return;
            //don't change body graphic file. it will inject material in PawnGraphicSet.MatsBodyBaseAt
            if (SizedApparelSettings.drawBodyParts)
                BodyPatch.SetBodyGraphic(___pawn, flags.FlagSet(PawnRenderFlags.Clothes), false);
            else
                BodyPatch.SetBodyGraphic(___pawn, flags.FlagSet(PawnRenderFlags.Clothes), false, false);
        }

        public static void Postfix(ref PawnRenderer __instance, Vector3 rootLoc, float angle, Rot4 facing, RotDrawMode bodyDrawType, PawnRenderFlags flags, Pawn ___pawn, Mesh bodyMesh)
        {
            if (___pawn == null)
                return;
            ApparelRecorderComp apparelRecorder = ___pawn.GetComp<ApparelRecorderComp>();
            if (apparelRecorder == null)
                return;
            Quaternion quaternion = Quaternion.AngleAxis(angle, Vector3.up);

            if (bodyMesh == null)
                return;

            Quaternion quat = Quaternion.AngleAxis(angle, Vector3.up);


            string defName = __instance.graphics.pawn.def.defName;

            /*
            Shader shader = ___pawn.Drawer.renderer.graphics.nakedGraphic.Shader;
            Color skinColor = Color.white;
            Color skinColor2 = Color.white;
            if (bodyDrawType == RotDrawMode.Fresh)
            {
                shader = ___pawn.Drawer.renderer.graphics.nakedGraphic.Shader;
                if (!ShaderUtility.SupportsMaskTex(shader))
                    shader = ShaderDatabase.CutoutSkinOverlay;
                skinColor = ___pawn.Drawer.renderer.graphics.nakedGraphic.Color;
                skinColor2 = ___pawn.Drawer.renderer.graphics.nakedGraphic.ColorTwo;
            }
            else if(bodyDrawType == RotDrawMode.Rotting)
            {
                shader = ___pawn.Drawer.renderer.graphics.rottingGraphic.Shader;
                if (!ShaderUtility.SupportsMaskTex(shader))
                    shader = ShaderDatabase.CutoutSkinOverlay;
                skinColor = ___pawn.Drawer.renderer.graphics.rottingGraphic.Color;
                skinColor2 = ___pawn.Drawer.renderer.graphics.rottingGraphic.ColorTwo;
            }*/

            /*
            //FurCoveredColor
            if(bodyDrawType == RotDrawMode.Fresh && __instance.graphics.furCoveredGraphic != null)
            {
                shader = ___pawn.Drawer.renderer.graphics.furCoveredGraphic.Shader;
                if (!ShaderUtility.SupportsMaskTex(shader))
                    shader = ShaderDatabase.CutoutSkinOverlay;
                skinColor = ___pawn.Drawer.renderer.graphics.furCoveredGraphic.Color;
                skinColor2 = ___pawn.Drawer.renderer.graphics.furCoveredGraphic.ColorTwo;
            }*/



            if (flags.FlagSet(PawnRenderFlags.StylingStation))//Styling station doesn't affect to real pawn data. so cannot cache to component.
            {


            }

            //breasts are only rendered when all of worn apparels are supported or nude.
            if (__instance.graphics.furCoveredGraphic == null)
            {
                apparelRecorder.UpdateTickAnim(rootLoc,angle);
                apparelRecorder.DrawAllBodyParts(rootLoc,angle,facing,bodyDrawType,flags,___pawn, bodyMesh);
            }

            //else. the DrawPawnFur will draw bodyparts becaust it should be upper

        }
    }


    //Base Body Graphic Injection
    [HarmonyPatch(typeof(PawnGraphicSet), "MatsBodyBaseAt")]
    public class MatsBodyBastAtPatch
    {
        public static void Postfix(ref List<Material> __result, PawnGraphicSet __instance, Rot4 facing, bool dead, RotDrawMode bodyCondition, bool drawClothes)
        {
            if (!SizedApparelSettings.useBodyTexture)
                return;

            if (__result.NullOrEmpty())
                return;

            if (!SizedApparelUtility.CanApplySizedApparel(__instance.pawn))
                return;

            var comp = __instance.pawn.GetComp<ApparelRecorderComp>();
            if (comp == null) // maybe it can be null? but why...? mechanoids?
                return;

            if (drawClothes)
            {
                if (comp.hasUnsupportedApparel)
                    return;
            }

            Material bodyMat = null;
            Material sizedApparelBaseBodyMat = null;
            switch (bodyCondition)
            {
                case RotDrawMode.Fresh:
                    if (dead && __instance.corpseGraphic != null)
                    {
                        return; //don't inject for corpse graphic since the mod doesn't support yet.
                        bodyMat = __instance.corpseGraphic.MatAt(facing, null);
                        if(comp.graphicbaseBodyCorpse != null)
                            sizedApparelBaseBodyMat = comp.graphicbaseBodyCorpse.MatAt(facing, null);
                    }
                    else if(__instance.nakedGraphic != null && comp.graphicbaseBodyNaked != null)
                    {
                        bodyMat = __instance.nakedGraphic.MatAt(facing, null);
                        sizedApparelBaseBodyMat = comp.graphicbaseBodyNaked.MatAt(facing, null);
                    }
                    break;
                case RotDrawMode.Rotting:
                    if (__instance.rottingGraphic != null && comp.graphicbaseBodyRotten != null)
                    {
                        bodyMat = __instance.rottingGraphic.MatAt(facing, null);
                        sizedApparelBaseBodyMat = comp.graphicbaseBodyRotten.MatAt(facing, null);
                    }
                    break;
                case RotDrawMode.Dessicated:
                    return;// //don't inject for Dessicated graphic. it doesn't need to patch for this
                    break;
                default:
                    bodyMat = null;
                    sizedApparelBaseBodyMat = null;
                    break;
            }

            if (sizedApparelBaseBodyMat == null)
                return;

            //the body mat would be in first index but not sure. so search from start
            for (int i = 0; i < __result.Count; i++)
            {
                if(__result[i] == bodyMat)
                {
                    __result[i] = sizedApparelBaseBodyMat;
                    //should inject body part graphics here?
                    break;
                }
            }
            //should do something more? such as add body parts or somthing?
        }
    }

    [HarmonyPatch(typeof(PawnRenderer), "DrawPawnFur")]
    public class DrawPawnFurPatch
    {
        public static void Prefix()
        {
            
        }
        //when this function called, the drawpawnbody did not draw sizedapparel body parts. draw pawn fur will draw those.
        //this is for sort graphics in some renderer case, some render are sorted by call order
        public static void Postfix(PawnRenderer __instance, Pawn ___pawn, Vector3 shellLoc, Rot4 facing, Quaternion quat, PawnRenderFlags flags)
        {
            var comp = ___pawn.GetComp<ApparelRecorderComp>();
            if (comp == null)
                return;
            float angle;
            Vector3 v;
            //angle = Quaternion.Angle(quat, Quaternion.AngleAxis(0, Vector3.up));
            quat.ToAngleAxis(out angle, out v);
            angle = angle * v.y; // Quaternion only gives 180 degree. but angle needs 360 degree

            /*
            //for more safe angle calculation, Not need till someone touched pawn's quaternion system. (for now, axis is fixed as Vector.up)
            angle = angle * new Vector3(0, v.y, 0).normalized.y;
            // 
             
            */

            //Quaternion.Inverse(quat).ToAngleAxis(out angle, out v);
            //angle = angle / 57.29578f;
            //angle = Quaternion.Angle(Quaternion.AngleAxis(0, Vector3.up), quat);
            Mesh mesh = HumanlikeMeshPoolUtility.GetHumanlikeBodySetForPawn(___pawn).MeshAt(facing);
            Vector3 loc = shellLoc;
            loc.y -= 0.009187258f; //rimworld 1.4 add 0.009187258f;



            comp.UpdateTickAnim(loc, angle);
            comp.DrawAllBodyParts(loc, angle, facing, RotDrawMode.Fresh, flags, ___pawn, mesh);
        }
    }
    //Base Body Graphic Injection for FurCovered
    [HarmonyPatch(typeof(PawnGraphicSet), "FurMatAt")]
    public class MatsFurMatAtPatch
    {
        //TODO should do in Postfix...? or Prefix?
        public static void Postfix(ref Material __result, PawnGraphicSet __instance, Rot4 facing, bool portrait = false, bool cached = false)
        {
            if (!SizedApparelSettings.useBodyTexture)
                return;

            if (__result == null)
                return;
            var comp = __instance.pawn.GetComp<ApparelRecorderComp>();
            if (comp == null)
                return;
            Material sizedApparelBaseBodyMat = null;

            if (comp.graphicbaseBodyFurCovered != null)
                sizedApparelBaseBodyMat = comp.graphicbaseBodyFurCovered.MatAt(facing, null);

            if (sizedApparelBaseBodyMat == null)
                return;

            if (!portrait && __instance.pawn.IsInvisible())
            {
                sizedApparelBaseBodyMat = InvisibilityMatPool.GetInvisibleMat(sizedApparelBaseBodyMat);
            }
            if (!cached)
            {
                sizedApparelBaseBodyMat = __instance.flasher.GetDamagedMat(sizedApparelBaseBodyMat);
            }

            __result = sizedApparelBaseBodyMat;
        }
    }

    //TODO
    [HarmonyPatch(typeof(PawnRenderer), "BaseHeadOffsetAt")]
    public class HeadOffsetPatch
    {

        public static void Postfix(PawnRenderer __instance, Pawn ___pawn, Rot4 rotation, ref Vector3 __result)
        {
            var comp = ___pawn.GetComp<ApparelRecorderComp>();
            if (comp == null)
                return;
        }

    }



}


