﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FreeDF.Game.Creatures
{
    class BodyPart
    // This is not an abstract body part, but unique for each creature
    // For abstract body parts BodyPartTemplate is used
    {
        float volume; // all volumes are in cubic centimeters
        Tissue tissue;
        List<BodyPart> innerParts = new List<BodyPart>();
        string name;

        public BodyPart(Tissue tissue, float volume, List<BodyPart> inner, string name)
        {
            this.volume = volume;
            this.tissue = tissue;
            this.innerParts = inner;
            this.name = name;
        }

        public float TotalVolume
        {
            get
            {
                float ans = volume;
                foreach (BodyPart p in innerParts) ans += p.TotalVolume;
                return ans;
            }
        }

        public float TotalMass
        {
            get
            {
                float ans = volume * tissue.Density;
                foreach (BodyPart p in innerParts) ans += p.TotalMass;
                return ans;
            }
        }
    }

    class BodyPartTemplate
    {
        FloatInterval possible_volumes;
        List<Tissue> possible_tissues = new List<Tissue>();
        string name;
        static Dictionary<string, BodyPartTemplate> register = new Dictionary<string, BodyPartTemplate>();
        Dictionary<string, BodyPartTemplate> innerParts = new Dictionary<string, BodyPartTemplate>();

        public void SetName(string name)
        {
            this.name = name;
        }

        public string GetName()
        {
            return name;
        }

        public BodyPartTemplate Rename(string name)
        {
            this.name = name;
            return this;
        }

        public BodyPartTemplate(string name, FloatInterval volumes, List<Tissue> tissues, bool save = true)
        {
            this.possible_volumes = volumes;
            this.possible_tissues = tissues;
            this.name = name;
            if (save) register[name] = this;
        }

        public void Save()
        {
            register[name] = this;
        }

        public void allowTissue(Tissue t)
        {
            if (!possible_tissues.Contains(t)) possible_tissues.Add(t);
        }

        public void SetVolumes(FloatInterval volumes)
        {
            this.possible_volumes = volumes;
        }

        public void forbidTissue(Tissue t)
        {
            if (possible_tissues.Contains(t)) possible_tissues.RemoveAll((T) => t == T);
        }

        public void InsertOrgan(BodyPartTemplate organ, string name=null)
        {
            if (name == null) innerParts[organ.name] = organ;
            else innerParts[name] = organ;
        }

        public void RemoveOrgans(string name)
        {
            innerParts.Remove(name);
        }

        public BodyPartTemplate GetOrganByName(string name)
        {
            return innerParts[name];
        }

        public BodyPartTemplate Clone()
        {
            List<BodyPartTemplate> innerParts = new List<BodyPartTemplate>();
            BodyPartTemplate res = new BodyPartTemplate(name, possible_volumes, possible_tissues, save: false);

            foreach (string key in this.innerParts.Keys) res.InsertOrgan(this.innerParts[key].Clone());
            return res;

        }

        public static BodyPartTemplate byName(string name)
        {
            return register[name];
        }

        public BodyPart GetInstance()
        {
            float volume = possible_volumes.Random();
            //if ((!possible_tissues.Contains(tissue)) && (possible_tissues.Count != 0)) throw new Exception("Cannot create part with that tissue");
            Tissue tissue = Misc.GetRandomElement(possible_tissues);
            List<BodyPart> inner = new List<BodyPart>();

            foreach (BodyPartTemplate tem in innerParts.Values) inner.Add(tem.GetInstance());

            return new BodyPart(tissue, volume, inner, name);
        }

    }
}
